catalyst-core 0.2.0-beta.3 → 0.3.0-beta.1

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 (160) hide show
  1. package/babel.native.config.cjs +8 -0
  2. package/bin/catalyst.js +49 -105
  3. package/changelog.md +8 -0
  4. package/dist/caching.js +1 -1
  5. package/dist/index.jsx +8 -0
  6. package/dist/logger.js +74 -3
  7. package/dist/native/androidSetup.js +300 -8
  8. package/dist/native/bridge/WebBridge.js +481 -59
  9. package/dist/native/bridge/constants/NativeInterfaces.js +290 -45
  10. package/dist/native/bridge/errors.js +250 -26
  11. package/dist/native/bridge/hooks/index.js +125 -1
  12. package/dist/native/bridge/hooks/useCamera.js +347 -9
  13. package/dist/native/bridge/hooks/useDataProtection.js +200 -2
  14. package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
  15. package/dist/native/bridge/hooks/useFilePicker.js +429 -2
  16. package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
  17. package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
  18. package/dist/native/bridge/hooks/useIntent.js +95 -1
  19. package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
  20. package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
  21. package/dist/native/bridge/hooks/useNotification.js +318 -2
  22. package/dist/native/bridge/hooks/useSafeArea.js +84 -2
  23. package/dist/native/bridge/hooks/useVideoStream.js +434 -6
  24. package/dist/native/bridge/hooks/utils.js +27 -1
  25. package/dist/native/bridge/hooks.js +16 -1
  26. package/dist/native/bridge/perf/collectors/ApiCollector.js +66 -3
  27. package/dist/native/bridge/perf/collectors/CacheCollector.js +186 -11
  28. package/dist/native/bridge/perf/collectors/HookCollector.js +141 -19
  29. package/dist/native/bridge/perf/collectors/InsightsCollector.js +481 -23
  30. package/dist/native/bridge/perf/collectors/InteractionCollector.js +245 -10
  31. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +107 -8
  32. package/dist/native/bridge/perf/collectors/NavigationCollector.js +273 -32
  33. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +178 -2
  34. package/dist/native/bridge/perf/collectors/RenderCollector.js +258 -14
  35. package/dist/native/bridge/perf/collectors/ScrollCollector.js +95 -9
  36. package/dist/native/bridge/perf/collectors/ThreadCollector.js +90 -6
  37. package/dist/native/bridge/perf/core/constants.js +103 -20
  38. package/dist/native/bridge/perf/core/measure.js +179 -30
  39. package/dist/native/bridge/perf/core/session.js +89 -23
  40. package/dist/native/bridge/perf/core/store.js +554 -1
  41. package/dist/native/bridge/perf/core/summary.js +174 -1
  42. package/dist/native/bridge/perf/core/traceExport.js +240 -1
  43. package/dist/native/bridge/perf/core/waterfalls.js +146 -1
  44. package/dist/native/bridge/perf/index.js +359 -38
  45. package/dist/native/bridge/safeArea.js +53 -5
  46. package/dist/native/bridge/standardizedHooks.js +595 -82
  47. package/dist/native/bridge/useBaseHook.js +274 -38
  48. package/dist/native/bridge/utils/CameraUtils.js +201 -56
  49. package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
  50. package/dist/native/bridge/utils/NativeBridge.js +425 -141
  51. package/dist/native/buildAndroid/assets.js +510 -13
  52. package/dist/native/buildAndroid/build.js +296 -19
  53. package/dist/native/buildAndroid/config.js +87 -1
  54. package/dist/native/buildAndroid/index.js +374 -1
  55. package/dist/native/buildAppAndroid.js +34 -2
  56. package/dist/native/buildAppIos.js +62 -2
  57. package/dist/native/buildIos/assets.js +609 -8
  58. package/dist/native/buildIos/build.js +639 -20
  59. package/dist/native/buildIos/config.js +294 -12
  60. package/dist/native/buildIos/index.js +343 -6
  61. package/dist/native/buildIos/plugins.js +343 -16
  62. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  63. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  64. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  65. package/dist/native/internalPluginUtils.js +232 -7
  66. package/dist/native/package.json +3 -0
  67. package/dist/native/plugin-bridge/PluginBridge.js +227 -1
  68. package/dist/native/pluginComposerAndroid.js +407 -23
  69. package/dist/native/pluginComposerIos.js +435 -11
  70. package/dist/native/renameAndroidProject.js +1094 -88
  71. package/dist/native/setupEmulatorIos.js +289 -22
  72. package/dist/native/setupServer.js +160 -19
  73. package/dist/native/terminalProgress.js +199 -11
  74. package/dist/native/utils.js +193 -15
  75. package/dist/offline/catalyst-sw.js +145 -1
  76. package/dist/offline/registerServiceWorker.js +33 -1
  77. package/dist/otel.js +375 -46
  78. package/dist/router/ClientRouter.js +14 -2
  79. package/dist/router/ServerRouter.js +9 -1
  80. package/dist/router.js +1 -1
  81. package/dist/scripts/build.js +112 -3
  82. package/dist/scripts/checkVersion.js +3 -1
  83. package/dist/scripts/cleanCache.js +30 -1
  84. package/dist/scripts/generateOfflineManifest.js +116 -1
  85. package/dist/scripts/loadEnvironmentVariables.js +32 -6
  86. package/dist/scripts/plugins.js +309 -1
  87. package/dist/scripts/preServerInit.js +13 -0
  88. package/dist/scripts/scriptUtils.js +54 -3
  89. package/dist/scripts/serve.js +51 -4
  90. package/dist/scripts/start.js +47 -10
  91. package/dist/scripts/validator.js +149 -1
  92. package/dist/sentry.cjs +127 -0
  93. package/dist/sentry.js +125 -5
  94. package/dist/server/expressServer.js +250 -5
  95. package/dist/server/manifestCache.js +36 -0
  96. package/dist/server/renderer/ChunkExtractor.js +144 -0
  97. package/dist/server/renderer/document/Body.jsx +59 -0
  98. package/dist/server/renderer/document/FastRefresh.jsx +20 -0
  99. package/dist/server/renderer/document/Head.jsx +60 -0
  100. package/dist/server/renderer/document/index.jsx +2 -0
  101. package/dist/server/renderer/extract.js +149 -31
  102. package/dist/server/renderer/handler.jsx +433 -0
  103. package/dist/server/renderer/index.js +8 -1
  104. package/dist/server/renderer/render.js +47 -0
  105. package/dist/server/utils/botDetectionMiddleware.js +29 -0
  106. package/dist/server/utils/cjsRequire.js +10 -0
  107. package/dist/server/utils/userAgentUtil.js +87 -9
  108. package/dist/server/utils/validator.js +164 -1
  109. package/dist/vite/FastRefresh.jsx +20 -0
  110. package/dist/vite/inject-cache-key-plugin.js +276 -0
  111. package/dist/vite/loadCustomViteConfig.js +22 -0
  112. package/dist/vite/manifest-categorization-plugin.js +455 -0
  113. package/dist/vite/node-loader.mjs +458 -0
  114. package/dist/vite/scssParams.js +21 -0
  115. package/dist/vite/vite.config.client.js +130 -0
  116. package/dist/vite/vite.config.js +422 -0
  117. package/dist/vite/vite.config.server.js +84 -0
  118. package/dist/web-router/components/MetaTag.jsx +34 -0
  119. package/dist/web-router/components/RouterDataProvider.jsx +332 -0
  120. package/dist/web-router/components/Split.jsx +200 -0
  121. package/dist/web-router/components/SplitInview.jsx +106 -0
  122. package/dist/web-router/components/SsrRequestContext.jsx +14 -0
  123. package/dist/web-router/context.jsx +12 -0
  124. package/dist/web-router/hooks.jsx +72 -0
  125. package/dist/web-router/types/components/MetaTag.d.ts +2 -0
  126. package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
  127. package/dist/web-router/types/context.d.ts +14 -0
  128. package/dist/web-router/types/hooks.d.ts +1 -0
  129. package/dist/web-router/types/index.d.ts +3 -0
  130. package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
  131. package/dist/web-router/utils/metaDataUtils.jsx +119 -0
  132. package/mcp_v2/conversion-tasks.json +35 -17
  133. package/mcp_v2/knowledge-base.json +124 -13
  134. package/mcp_v2/lib/helpers.js +10 -0
  135. package/mcp_v2/mcp.js +4 -4
  136. package/mcp_v2/package.json +1 -0
  137. package/mcp_v2/tools/build.js +116 -20
  138. package/mcp_v2/tools/conversion.js +106 -5
  139. package/mcp_v2/tools/tasks.js +18 -14
  140. package/package.json +35 -68
  141. package/babel.config.js +0 -32
  142. package/dist/index.js +0 -1
  143. package/dist/scripts/devBuild.js +0 -3
  144. package/dist/scripts/devServe.js +0 -3
  145. package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
  146. package/dist/scripts/registerAliases.js +0 -6
  147. package/dist/server/renderer/document/Body.js +0 -11
  148. package/dist/server/renderer/document/Head.js +0 -9
  149. package/dist/server/renderer/document/index.js +0 -1
  150. package/dist/server/renderer/handler.js +0 -47
  151. package/dist/server/startServer.js +0 -57
  152. package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
  153. package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
  154. package/dist/webpack/babel.config.client.js +0 -1
  155. package/dist/webpack/babel.config.ssr.js +0 -1
  156. package/dist/webpack/base.babel.js +0 -7
  157. package/dist/webpack/development.client.babel.js +0 -10
  158. package/dist/webpack/production.client.babel.js +0 -4
  159. package/dist/webpack/production.ssr.babel.js +0 -8
  160. package/dist/webpack/scssParams.js +0 -9
@@ -1,13 +1,236 @@
1
- "use strict";const fs=require("fs");const path=require("path");// ─── Pure helpers (no ctx dependency) ────────────────────────────────────────
2
- function isPlainObject(value){return!!value&&typeof value==="object"&&!Array.isArray(value);}function deepEqual(left,right){return JSON.stringify(left)===JSON.stringify(right);}function mergeStructuredValues(existing,incoming,fieldName){if(existing===undefined)return JSON.parse(JSON.stringify(incoming));if(Array.isArray(existing)&&Array.isArray(incoming)){const merged=[];const seen=new Set();for(const value of[...existing,...incoming]){const key=JSON.stringify(value);if(seen.has(key))continue;seen.add(key);merged.push(value);}return merged;}if(isPlainObject(existing)&&isPlainObject(incoming)){const merged={...existing};for(const[key,value]of Object.entries(incoming)){merged[key]=mergeStructuredValues(merged[key],value,`${fieldName}.${key}`);}return merged;}if(deepEqual(existing,incoming))return existing;throw new Error(`Conflicting values for '${fieldName}' while composing iOS build metadata`);}function generateSwiftProperty(key,value,indent=" "){if(value===null||value===undefined)return`${indent}public static let ${key}: String? = nil`;if(key==="cachePattern"){if(typeof value==="string")return`${indent}public static let ${key}: [String] = ["${value}"]`;if(Array.isArray(value)){const arrayValues=value.map(v=>`"${v}"`).join(", ");return`${indent}public static let ${key}: [String] = [${arrayValues}]`;}}if(typeof value==="string")return`${indent}public static let ${key} = "${value}"`;if(typeof value==="number")return`${indent}public static let ${key} = ${value}`;if(typeof value==="boolean")return`${indent}public static let ${key} = ${value}`;if(Array.isArray(value)){if(value.length===0)return`${indent}public static let ${key}: [String] = []`;const firstElement=value[0];if(typeof firstElement==="string"){const arrayValues=value.map(v=>`"${v}"`).join(", ");return`${indent}public static let ${key}: [String] = [${arrayValues}]`;}else if(typeof firstElement==="number"){const arrayType=Number.isInteger(firstElement)?"Int":"Double";return`${indent}public static let ${key}: [${arrayType}] = [${value.join(", ")}]`;}else if(typeof firstElement==="boolean"){return`${indent}public static let ${key}: [Bool] = [${value.join(", ")}]`;}else{const arrayValues=value.map(v=>`"${v}"`).join(", ");return`${indent}public static let ${key}: [String] = [${arrayValues}]`;}}if(typeof value==="object"&&value!==null){let nestedContent=`${indent}public enum ${key.charAt(0).toUpperCase()+key.slice(1)} {\n`;for(const[nestedKey,nestedValue]of Object.entries(value)){nestedContent+=generateSwiftProperty(nestedKey,nestedValue,indent+" ")+"\n";}nestedContent+=`${indent}}`;return nestedContent;}return`${indent}public static let ${key} = "${value}"`;}// ─── Module factory ───────────────────────────────────────────────────────────
3
- module.exports=function createConfigPhase(ctx){const{WEBVIEW_CONFIG,iosConfig,PROJECT_DIR,url,progress,restoreManagedFileFromBaseline,readPlistObject,writePlistObject}=ctx;function mergeIntoTopLevelObject(target,source,fieldName){for(const[key,value]of Object.entries(source||{})){target[key]=mergeStructuredValues(target[key],value,`${fieldName}.${key}`);}}function mergeUrlSchemes(plistObject,entries){const existing=Array.isArray(plistObject.CFBundleURLTypes)?plistObject.CFBundleURLTypes:[];const merged=existing.map(entry=>({...entry,CFBundleURLSchemes:Array.isArray(entry?.CFBundleURLSchemes)?[...new Set(entry.CFBundleURLSchemes)]:[]}));for(const entry of entries){const targetName=entry.name||null;let match=merged.find(item=>item.CFBundleURLName===targetName);if(!match){match={CFBundleURLName:targetName,CFBundleURLSchemes:[]};merged.push(match);}match.CFBundleURLSchemes=[...new Set([...match.CFBundleURLSchemes,...entry.schemes])];}if(merged.length>0)plistObject.CFBundleURLTypes=merged;}function mergeQuerySchemes(plistObject,schemes){const existing=Array.isArray(plistObject.LSApplicationQueriesSchemes)?plistObject.LSApplicationQueriesSchemes:[];const merged=[...new Set([...existing,...schemes.filter(Boolean)])];if(merged.length>0)plistObject.LSApplicationQueriesSchemes=merged;}async function generateConfigConstants(){progress.start("config");try{const appConfigPath=path.join(PROJECT_DIR,// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal - PROJECT_DIR is the fixed generated iOS project directory.
4
- "Sources","Core","Constants","ConfigConstants.swift");const appConfigDir=path.dirname(appConfigPath);if(!fs.existsSync(appConfigDir))fs.mkdirSync(appConfigDir,{recursive:true});let configContent=`// This file is auto-generated. Do not edit.
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ // ─── Pure helpers (no ctx dependency) ────────────────────────────────────────
7
+
8
+ function isPlainObject(value) {
9
+ return !!value && typeof value === "object" && !Array.isArray(value);
10
+ }
11
+ function deepEqual(left, right) {
12
+ return JSON.stringify(left) === JSON.stringify(right);
13
+ }
14
+ function mergeStructuredValues(existing, incoming, fieldName) {
15
+ if (existing === undefined) return JSON.parse(JSON.stringify(incoming));
16
+ if (Array.isArray(existing) && Array.isArray(incoming)) {
17
+ const merged = [];
18
+ const seen = new Set();
19
+ for (const value of [...existing, ...incoming]) {
20
+ const key = JSON.stringify(value);
21
+ if (seen.has(key)) continue;
22
+ seen.add(key);
23
+ merged.push(value);
24
+ }
25
+ return merged;
26
+ }
27
+ if (isPlainObject(existing) && isPlainObject(incoming)) {
28
+ const merged = {
29
+ ...existing
30
+ };
31
+ for (const [key, value] of Object.entries(incoming)) {
32
+ merged[key] = mergeStructuredValues(merged[key], value, `${fieldName}.${key}`);
33
+ }
34
+ return merged;
35
+ }
36
+ if (deepEqual(existing, incoming)) return existing;
37
+ throw new Error(`Conflicting values for '${fieldName}' while composing iOS build metadata`);
38
+ }
39
+ function generateSwiftProperty(key, value, indent = " ") {
40
+ if (value === null || value === undefined) return `${indent}public static let ${key}: String? = nil`;
41
+ if (key === "cachePattern") {
42
+ if (typeof value === "string") return `${indent}public static let ${key}: [String] = ["${value}"]`;
43
+ if (Array.isArray(value)) {
44
+ const arrayValues = value.map(v => `"${v}"`).join(", ");
45
+ return `${indent}public static let ${key}: [String] = [${arrayValues}]`;
46
+ }
47
+ }
48
+ if (typeof value === "string") return `${indent}public static let ${key} = "${value}"`;
49
+ if (typeof value === "number") return `${indent}public static let ${key} = ${value}`;
50
+ if (typeof value === "boolean") return `${indent}public static let ${key} = ${value}`;
51
+ if (Array.isArray(value)) {
52
+ if (value.length === 0) return `${indent}public static let ${key}: [String] = []`;
53
+ const firstElement = value[0];
54
+ if (typeof firstElement === "string") {
55
+ const arrayValues = value.map(v => `"${v}"`).join(", ");
56
+ return `${indent}public static let ${key}: [String] = [${arrayValues}]`;
57
+ } else if (typeof firstElement === "number") {
58
+ const arrayType = Number.isInteger(firstElement) ? "Int" : "Double";
59
+ return `${indent}public static let ${key}: [${arrayType}] = [${value.join(", ")}]`;
60
+ } else if (typeof firstElement === "boolean") {
61
+ return `${indent}public static let ${key}: [Bool] = [${value.join(", ")}]`;
62
+ } else {
63
+ const arrayValues = value.map(v => `"${v}"`).join(", ");
64
+ return `${indent}public static let ${key}: [String] = [${arrayValues}]`;
65
+ }
66
+ }
67
+ if (typeof value === "object" && value !== null) {
68
+ let nestedContent = `${indent}public enum ${key.charAt(0).toUpperCase() + key.slice(1)} {\n`;
69
+ for (const [nestedKey, nestedValue] of Object.entries(value)) {
70
+ nestedContent += generateSwiftProperty(nestedKey, nestedValue, indent + " ") + "\n";
71
+ }
72
+ nestedContent += `${indent}}`;
73
+ return nestedContent;
74
+ }
75
+ return `${indent}public static let ${key} = "${value}"`;
76
+ }
77
+
78
+ // ─── Module factory ───────────────────────────────────────────────────────────
79
+
80
+ module.exports = function createConfigPhase(ctx) {
81
+ const {
82
+ WEBVIEW_CONFIG,
83
+ iosConfig,
84
+ PROJECT_DIR,
85
+ url,
86
+ progress,
87
+ restoreManagedFileFromBaseline,
88
+ readPlistObject,
89
+ writePlistObject
90
+ } = ctx;
91
+ function mergeIntoTopLevelObject(target, source, fieldName) {
92
+ for (const [key, value] of Object.entries(source || {})) {
93
+ target[key] = mergeStructuredValues(target[key], value, `${fieldName}.${key}`);
94
+ }
95
+ }
96
+ function mergeUrlSchemes(plistObject, entries) {
97
+ const existing = Array.isArray(plistObject.CFBundleURLTypes) ? plistObject.CFBundleURLTypes : [];
98
+ const merged = existing.map(entry => ({
99
+ ...entry,
100
+ CFBundleURLSchemes: Array.isArray(entry?.CFBundleURLSchemes) ? [...new Set(entry.CFBundleURLSchemes)] : []
101
+ }));
102
+ for (const entry of entries) {
103
+ const targetName = entry.name || null;
104
+ let match = merged.find(item => item.CFBundleURLName === targetName);
105
+ if (!match) {
106
+ match = {
107
+ CFBundleURLName: targetName,
108
+ CFBundleURLSchemes: []
109
+ };
110
+ merged.push(match);
111
+ }
112
+ match.CFBundleURLSchemes = [...new Set([...match.CFBundleURLSchemes, ...entry.schemes])];
113
+ }
114
+ if (merged.length > 0) plistObject.CFBundleURLTypes = merged;
115
+ }
116
+ function mergeQuerySchemes(plistObject, schemes) {
117
+ const existing = Array.isArray(plistObject.LSApplicationQueriesSchemes) ? plistObject.LSApplicationQueriesSchemes : [];
118
+ const merged = [...new Set([...existing, ...schemes.filter(Boolean)])];
119
+ if (merged.length > 0) plistObject.LSApplicationQueriesSchemes = merged;
120
+ }
121
+ async function generateConfigConstants() {
122
+ progress.start("config");
123
+ try {
124
+ const appConfigPath = path.join(PROJECT_DIR,
125
+ // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal - PROJECT_DIR is the fixed generated iOS project directory.
126
+ "Sources", "Core", "Constants", "ConfigConstants.swift");
127
+ const appConfigDir = path.dirname(appConfigPath);
128
+ if (!fs.existsSync(appConfigDir)) fs.mkdirSync(appConfigDir, {
129
+ recursive: true
130
+ });
131
+ let configContent = `// This file is auto-generated. Do not edit.
5
132
  import Foundation
6
133
 
7
134
  public enum ConfigConstants {
8
- public static let url = "${url}"`;const addedKeys=new Set();if(WEBVIEW_CONFIG&&typeof WEBVIEW_CONFIG==="object"){for(const[key,value]of Object.entries(WEBVIEW_CONFIG)){if(key==="ios"||key==="android")continue;if(key==="notifications")progress.log(`Processing notifications config: ${JSON.stringify(value)}`,"info");configContent+="\n"+generateSwiftProperty(key,value);addedKeys.add(key);}}if(iosConfig&&typeof iosConfig==="object"){configContent+="\n \n // iOS-specific configuration";for(const[key,value]of Object.entries(iosConfig)){if(addedKeys.has(key))continue;configContent+="\n"+generateSwiftProperty(key,value);}}if(WEBVIEW_CONFIG.accessControl){const accessControl=WEBVIEW_CONFIG.accessControl;configContent+=`\n public static let accessControlEnabled = ${accessControl.enabled||false}`;if(accessControl.allowedUrls&&Array.isArray(accessControl.allowedUrls)){const allowedUrls=accessControl.allowedUrls.map(u=>`"${u}"`).join(", ");configContent+=`\n public static let allowedUrls: [String] = [${allowedUrls}]`;}else if(accessControl.allowedUrls&&typeof accessControl.allowedUrls==="string"){const allowedUrls=accessControl.allowedUrls.split(",").map(u=>u.trim()).filter(u=>u.length>0).map(u=>`"${u}"`).join(", ");configContent+=`\n public static let allowedUrls: [String] = [${allowedUrls}]`;}}else{configContent+=`\n public static let accessControlEnabled = false\n public static let allowedUrls: [String] = []`;}const splashConfig=WEBVIEW_CONFIG.splashScreen;if(splashConfig){configContent+=`\n\n // Splash Screen Configuration\n public static let splashScreenEnabled = true`;configContent+=splashConfig.duration?`\n public static let splashScreenDuration: TimeInterval? = ${splashConfig.duration/1000.0}`:`\n public static let splashScreenDuration: TimeInterval? = nil`;configContent+=splashConfig.backgroundColor?`\n public static let splashScreenBackgroundColor = "${splashConfig.backgroundColor}"`:`\n public static let splashScreenBackgroundColor = "#ffffff"`;configContent+=`\n public static let splashScreenImageWidth: CGFloat = ${splashConfig.imageWidth||120}`;configContent+=`\n public static let splashScreenImageHeight: CGFloat = ${splashConfig.imageHeight||120}`;configContent+=`\n public static let splashScreenCornerRadius: CGFloat = ${splashConfig.cornerRadius||20}`;}else{configContent+=`\n\n // Splash Screen Configuration\n public static let splashScreenEnabled = false\n public static let splashScreenDuration: TimeInterval? = nil\n public static let splashScreenBackgroundColor = "#ffffff"\n public static let splashScreenImageWidth: CGFloat = 120\n public static let splashScreenImageHeight: CGFloat = 120\n public static let splashScreenCornerRadius: CGFloat = 20`;}if(!addedKeys.has("notifications")){progress.log("Notifications not found in config, adding default (false)","info");configContent+="\n public enum Notifications {\n public static let enabled = false\n }";addedKeys.add("notifications");}else{progress.log("Notifications config was processed from WEBVIEW_CONFIG","info");}if(!addedKeys.has("googleSignIn")){progress.log("Google Sign-In not found in config, adding defaults (disabled)","info");configContent+='\n public enum GoogleSignIn {\n public static let enabled = false\n public static let clientId = ""\n public static let iosClientId = ""\n }';addedKeys.add("googleSignIn");}if(!addedKeys.has("profiler")){configContent+="\n public enum Profiler {\n public static let enabled = false\n }";addedKeys.add("profiler");}if(!addedKeys.has("edgeToEdge")){progress.log("EdgeToEdge not found in config, adding default (false)","info");configContent+="\n public enum EdgeToEdge {\n public static let enabled = false\n }";addedKeys.add("edgeToEdge");}else{progress.log("EdgeToEdge config was processed from WEBVIEW_CONFIG","info");}if(!addedKeys.has("initial_url")){configContent+='\n public static let initial_url = ""';addedKeys.add("initial_url");}// Ensure appInfo always exists (default to empty string if not configured)
9
- if(!addedKeys.has("appInfo")){configContent+='\n public static let appInfo = ""';addedKeys.add("appInfo");}configContent+=`\n}`;fs.writeFileSync(appConfigPath,configContent,"utf8");progress.log("Configuration constants generated successfully (SPM Package)","success");progress.complete("config");}catch(error){progress.fail("config",error.message);throw error;}}async function generateXCConfig(){try{const xconfigPath=path.join(PROJECT_DIR,"Configurations","Shared.xcconfig");// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
10
- const configDir=path.dirname(xconfigPath);if(!fs.existsSync(configDir))fs.mkdirSync(configDir,{recursive:true});const bundleId=iosConfig.bundleIdentifier||iosConfig.appBundleId||"com.catalyst.framework.app";const appName=iosConfig.appName||"Catalyst App";const teamId=iosConfig.developmentTeam||"";const swiftVersion=iosConfig.deployment?.swiftVersion||"5.9";const deploymentTarget=iosConfig.deployment?.target||"17.0";const marketingVersion=iosConfig.version?`${iosConfig.version.major||1}.${iosConfig.version.minor||0}.${iosConfig.version.patch||0}`:"1.0.0";const buildNumber=iosConfig.version?.buildNumber||1;const provisioningProfile=iosConfig.provisioningProfile||"";const xconfigContent=`// This file is auto-generated by buildAppIos.js. Do not edit.
135
+ public static let url = "${url}"`;
136
+ const addedKeys = new Set();
137
+ if (WEBVIEW_CONFIG && typeof WEBVIEW_CONFIG === "object") {
138
+ for (const [key, value] of Object.entries(WEBVIEW_CONFIG)) {
139
+ if (key === "ios" || key === "android") continue;
140
+ if (key === "notifications") progress.log(`Processing notifications config: ${JSON.stringify(value)}`, "info");
141
+ configContent += "\n" + generateSwiftProperty(key, value);
142
+ addedKeys.add(key);
143
+ }
144
+ }
145
+ if (iosConfig && typeof iosConfig === "object") {
146
+ configContent += "\n \n // iOS-specific configuration";
147
+ for (const [key, value] of Object.entries(iosConfig)) {
148
+ if (addedKeys.has(key)) continue;
149
+ configContent += "\n" + generateSwiftProperty(key, value);
150
+ }
151
+ }
152
+ if (WEBVIEW_CONFIG.accessControl) {
153
+ const accessControl = WEBVIEW_CONFIG.accessControl;
154
+ configContent += `\n public static let accessControlEnabled = ${accessControl.enabled || false}`;
155
+ if (accessControl.allowedUrls && Array.isArray(accessControl.allowedUrls)) {
156
+ const allowedUrls = accessControl.allowedUrls.map(u => `"${u}"`).join(", ");
157
+ configContent += `\n public static let allowedUrls: [String] = [${allowedUrls}]`;
158
+ } else if (accessControl.allowedUrls && typeof accessControl.allowedUrls === "string") {
159
+ const allowedUrls = accessControl.allowedUrls.split(",").map(u => u.trim()).filter(u => u.length > 0).map(u => `"${u}"`).join(", ");
160
+ configContent += `\n public static let allowedUrls: [String] = [${allowedUrls}]`;
161
+ }
162
+ } else {
163
+ configContent += `\n public static let accessControlEnabled = false\n public static let allowedUrls: [String] = []`;
164
+ }
165
+ const splashConfig = WEBVIEW_CONFIG.splashScreen;
166
+ if (splashConfig) {
167
+ configContent += `\n\n // Splash Screen Configuration\n public static let splashScreenEnabled = true`;
168
+ configContent += splashConfig.duration ? `\n public static let splashScreenDuration: TimeInterval? = ${splashConfig.duration / 1000.0}` : `\n public static let splashScreenDuration: TimeInterval? = nil`;
169
+ configContent += splashConfig.backgroundColor ? `\n public static let splashScreenBackgroundColor = "${splashConfig.backgroundColor}"` : `\n public static let splashScreenBackgroundColor = "#ffffff"`;
170
+ configContent += `\n public static let splashScreenImageWidth: CGFloat = ${splashConfig.imageWidth || 120}`;
171
+ configContent += `\n public static let splashScreenImageHeight: CGFloat = ${splashConfig.imageHeight || 120}`;
172
+ configContent += `\n public static let splashScreenCornerRadius: CGFloat = ${splashConfig.cornerRadius || 20}`;
173
+ } else {
174
+ configContent += `\n\n // Splash Screen Configuration\n public static let splashScreenEnabled = false\n public static let splashScreenDuration: TimeInterval? = nil\n public static let splashScreenBackgroundColor = "#ffffff"\n public static let splashScreenImageWidth: CGFloat = 120\n public static let splashScreenImageHeight: CGFloat = 120\n public static let splashScreenCornerRadius: CGFloat = 20`;
175
+ }
176
+ if (!addedKeys.has("notifications")) {
177
+ progress.log("Notifications not found in config, adding default (false)", "info");
178
+ configContent += "\n public enum Notifications {\n public static let enabled = false\n }";
179
+ addedKeys.add("notifications");
180
+ } else {
181
+ progress.log("Notifications config was processed from WEBVIEW_CONFIG", "info");
182
+ }
183
+ if (!addedKeys.has("googleSignIn")) {
184
+ progress.log("Google Sign-In not found in config, adding defaults (disabled)", "info");
185
+ configContent += '\n public enum GoogleSignIn {\n public static let enabled = false\n public static let clientId = ""\n public static let iosClientId = ""\n }';
186
+ addedKeys.add("googleSignIn");
187
+ }
188
+ if (!addedKeys.has("profiler")) {
189
+ configContent += "\n public enum Profiler {\n public static let enabled = false\n }";
190
+ addedKeys.add("profiler");
191
+ }
192
+ if (!addedKeys.has("edgeToEdge")) {
193
+ progress.log("EdgeToEdge not found in config, adding default (false)", "info");
194
+ configContent += "\n public enum EdgeToEdge {\n public static let enabled = false\n }";
195
+ addedKeys.add("edgeToEdge");
196
+ } else {
197
+ progress.log("EdgeToEdge config was processed from WEBVIEW_CONFIG", "info");
198
+ }
199
+ if (!addedKeys.has("initial_url")) {
200
+ configContent += '\n public static let initial_url = ""';
201
+ addedKeys.add("initial_url");
202
+ }
203
+
204
+ // Ensure appInfo always exists (default to empty string if not configured)
205
+ if (!addedKeys.has("appInfo")) {
206
+ configContent += '\n public static let appInfo = ""';
207
+ addedKeys.add("appInfo");
208
+ }
209
+ configContent += `\n}`;
210
+ fs.writeFileSync(appConfigPath, configContent, "utf8");
211
+ progress.log("Configuration constants generated successfully (SPM Package)", "success");
212
+ progress.complete("config");
213
+ } catch (error) {
214
+ progress.fail("config", error.message);
215
+ throw error;
216
+ }
217
+ }
218
+ async function generateXCConfig() {
219
+ try {
220
+ const xconfigPath = path.join(PROJECT_DIR, "Configurations", "Shared.xcconfig"); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
221
+ const configDir = path.dirname(xconfigPath);
222
+ if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, {
223
+ recursive: true
224
+ });
225
+ const bundleId = iosConfig.bundleIdentifier || iosConfig.appBundleId || "com.catalyst.framework.app";
226
+ const appName = iosConfig.appName || "Catalyst App";
227
+ const teamId = iosConfig.developmentTeam || "";
228
+ const swiftVersion = iosConfig.deployment?.swiftVersion || "5.9";
229
+ const deploymentTarget = iosConfig.deployment?.target || "17.0";
230
+ const marketingVersion = iosConfig.version ? `${iosConfig.version.major || 1}.${iosConfig.version.minor || 0}.${iosConfig.version.patch || 0}` : "1.0.0";
231
+ const buildNumber = iosConfig.version?.buildNumber || 1;
232
+ const provisioningProfile = iosConfig.provisioningProfile || "";
233
+ const xconfigContent = `// This file is auto-generated by buildAppIos.js. Do not edit.
11
234
  //
12
235
  // Shared configuration for both Debug and Release builds
13
236
  // All values are dynamically injected from config.json
@@ -64,8 +287,67 @@ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES
64
287
  INFOPLIST_KEY_UILaunchScreen_Generation = YES
65
288
  INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
66
289
  INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
67
- `;fs.writeFileSync(xconfigPath,xconfigContent,"utf8");console.log(`✅ Generated Shared.xcconfig with Swift ${swiftVersion}, iOS ${deploymentTarget}, Bundle ID: ${bundleId}`);}catch(error){console.error(`❌ Failed to generate Shared.xcconfig: ${error.message}`);throw error;}}async function updateInfoPlist(pluginComposition={}){const PROJECT_NAME=ctx.PROJECT_NAME;const isGoogleSignInEnabled=ctx.isGoogleSignInEnabled;const GOOGLE_SERVICES_FILENAME="GoogleService-Info.plist";const infoPlistPath=path.join(PROJECT_DIR,PROJECT_NAME,"Info.plist");// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
68
- const infoReleasePlistPath=path.join(PROJECT_DIR,PROJECT_NAME,"Info-Release.plist");// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
69
- const googleServicesPlistPath=path.join(PROJECT_DIR,PROJECT_NAME,GOOGLE_SERVICES_FILENAME);// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
70
- const googleClientId=WEBVIEW_CONFIG.googleSignIn?.clientId||WEBVIEW_CONFIG.googleSignIn?.webClientId||"";const iosClientId=WEBVIEW_CONFIG.googleSignIn?.iosClientId||"";const googleServicesContent=fs.existsSync(googleServicesPlistPath)?fs.readFileSync(googleServicesPlistPath,"utf8"):null;const reversedClientIdFromServices=googleServicesContent?(googleServicesContent.match(/<key>REVERSED_CLIENT_ID<\/key>\s*<string>([^<]+)<\/string>/)||[])[1]:null;const clientIdFromServices=googleServicesContent?(googleServicesContent.match(/<key>CLIENT_ID<\/key>\s*<string>([^<]+)<\/string>/)||[])[1]:null;const computeReversed=value=>!value?"":value.split(".").reverse().join(".");const resolvedClientIdForScheme=iosClientId||googleClientId||clientIdFromServices||"";const resolvedReversedClientId=reversedClientIdFromServices||computeReversed(resolvedClientIdForScheme);if(isGoogleSignInEnabled&&!resolvedReversedClientId){throw new Error("Google Sign-In enabled but no valid clientId found");}const plistTargets=[infoPlistPath,infoReleasePlistPath];const pluginUrlSchemes=pluginComposition.urlSchemes||[];const pluginQuerySchemes=pluginComposition.querySchemes||[];const pluginInfoPlist=pluginComposition.infoPlist||{};plistTargets.forEach(plistPath=>{if(!fs.existsSync(plistPath))return;restoreManagedFileFromBaseline(plistPath);const plistObject=readPlistObject(plistPath);plistObject.CFBundleDisplayName=iosConfig.appName||"Catalyst Application";mergeIntoTopLevelObject(plistObject,pluginInfoPlist,"ios.infoPlist");if(isGoogleSignInEnabled&&resolvedReversedClientId){mergeUrlSchemes(plistObject,[{name:"googleSignIn",schemes:[resolvedReversedClientId]}]);mergeQuerySchemes(plistObject,["google",resolvedReversedClientId]);}mergeUrlSchemes(plistObject,pluginUrlSchemes);mergeQuerySchemes(plistObject,pluginQuerySchemes);writePlistObject(plistPath,plistObject);});}async function updateEntitlements(pluginComposition={}){const PROJECT_NAME=ctx.PROJECT_NAME;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
71
- if(!fs.existsSync(entitlementsPath))return;restoreManagedFileFromBaseline(entitlementsPath);const entitlementsObject=readPlistObject(entitlementsPath);mergeIntoTopLevelObject(entitlementsObject,pluginComposition.entitlements||{},"ios.entitlements");writePlistObject(entitlementsPath,entitlementsObject);}return{generateConfigConstants,generateXCConfig,updateInfoPlist,updateEntitlements};};
290
+ `;
291
+ fs.writeFileSync(xconfigPath, xconfigContent, "utf8");
292
+ console.log(`✅ Generated Shared.xcconfig with Swift ${swiftVersion}, iOS ${deploymentTarget}, Bundle ID: ${bundleId}`);
293
+ } catch (error) {
294
+ console.error(`❌ Failed to generate Shared.xcconfig: ${error.message}`);
295
+ throw error;
296
+ }
297
+ }
298
+ async function updateInfoPlist(pluginComposition = {}) {
299
+ const PROJECT_NAME = ctx.PROJECT_NAME;
300
+ const isGoogleSignInEnabled = ctx.isGoogleSignInEnabled;
301
+ const GOOGLE_SERVICES_FILENAME = "GoogleService-Info.plist";
302
+ const infoPlistPath = path.join(PROJECT_DIR, PROJECT_NAME, "Info.plist"); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
303
+ const infoReleasePlistPath = path.join(PROJECT_DIR, PROJECT_NAME, "Info-Release.plist"); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
304
+ const googleServicesPlistPath = path.join(PROJECT_DIR, PROJECT_NAME, GOOGLE_SERVICES_FILENAME); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
305
+ const googleClientId = WEBVIEW_CONFIG.googleSignIn?.clientId || WEBVIEW_CONFIG.googleSignIn?.webClientId || "";
306
+ const iosClientId = WEBVIEW_CONFIG.googleSignIn?.iosClientId || "";
307
+ const googleServicesContent = fs.existsSync(googleServicesPlistPath) ? fs.readFileSync(googleServicesPlistPath, "utf8") : null;
308
+ const reversedClientIdFromServices = googleServicesContent ? (googleServicesContent.match(/<key>REVERSED_CLIENT_ID<\/key>\s*<string>([^<]+)<\/string>/) || [])[1] : null;
309
+ const clientIdFromServices = googleServicesContent ? (googleServicesContent.match(/<key>CLIENT_ID<\/key>\s*<string>([^<]+)<\/string>/) || [])[1] : null;
310
+ const computeReversed = value => !value ? "" : value.split(".").reverse().join(".");
311
+ const resolvedClientIdForScheme = iosClientId || googleClientId || clientIdFromServices || "";
312
+ const resolvedReversedClientId = reversedClientIdFromServices || computeReversed(resolvedClientIdForScheme);
313
+ if (isGoogleSignInEnabled && !resolvedReversedClientId) {
314
+ throw new Error("Google Sign-In enabled but no valid clientId found");
315
+ }
316
+ const plistTargets = [infoPlistPath, infoReleasePlistPath];
317
+ const pluginUrlSchemes = pluginComposition.urlSchemes || [];
318
+ const pluginQuerySchemes = pluginComposition.querySchemes || [];
319
+ const pluginInfoPlist = pluginComposition.infoPlist || {};
320
+ plistTargets.forEach(plistPath => {
321
+ if (!fs.existsSync(plistPath)) return;
322
+ restoreManagedFileFromBaseline(plistPath);
323
+ const plistObject = readPlistObject(plistPath);
324
+ plistObject.CFBundleDisplayName = iosConfig.appName || "Catalyst Application";
325
+ mergeIntoTopLevelObject(plistObject, pluginInfoPlist, "ios.infoPlist");
326
+ if (isGoogleSignInEnabled && resolvedReversedClientId) {
327
+ mergeUrlSchemes(plistObject, [{
328
+ name: "googleSignIn",
329
+ schemes: [resolvedReversedClientId]
330
+ }]);
331
+ mergeQuerySchemes(plistObject, ["google", resolvedReversedClientId]);
332
+ }
333
+ mergeUrlSchemes(plistObject, pluginUrlSchemes);
334
+ mergeQuerySchemes(plistObject, pluginQuerySchemes);
335
+ writePlistObject(plistPath, plistObject);
336
+ });
337
+ }
338
+ async function updateEntitlements(pluginComposition = {}) {
339
+ const PROJECT_NAME = ctx.PROJECT_NAME;
340
+ 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
341
+ if (!fs.existsSync(entitlementsPath)) return;
342
+ restoreManagedFileFromBaseline(entitlementsPath);
343
+ const entitlementsObject = readPlistObject(entitlementsPath);
344
+ mergeIntoTopLevelObject(entitlementsObject, pluginComposition.entitlements || {}, "ios.entitlements");
345
+ writePlistObject(entitlementsPath, entitlementsObject);
346
+ }
347
+ return {
348
+ generateConfigConstants,
349
+ generateXCConfig,
350
+ updateInfoPlist,
351
+ updateEntitlements
352
+ };
353
+ };