catalyst-core 0.2.0-beta.2 → 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 (184) 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/androidProject/app/build.gradle.kts +1 -0
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +143 -4
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +26 -6
  10. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt +143 -17
  11. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +6 -0
  12. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +26 -0
  13. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +56 -1
  14. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +48 -8
  15. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfDeliveryState.kt +36 -0
  16. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfEventBuffer.kt +289 -0
  17. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/ProfilerNavigationState.kt +13 -0
  18. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ProfilerLifecycleTest.kt +51 -0
  19. package/dist/native/androidSetup.js +300 -8
  20. package/dist/native/bridge/WebBridge.js +481 -59
  21. package/dist/native/bridge/constants/NativeInterfaces.js +290 -44
  22. package/dist/native/bridge/errors.js +250 -26
  23. package/dist/native/bridge/hooks/index.js +125 -1
  24. package/dist/native/bridge/hooks/useCamera.js +347 -9
  25. package/dist/native/bridge/hooks/useDataProtection.js +200 -2
  26. package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
  27. package/dist/native/bridge/hooks/useFilePicker.js +429 -2
  28. package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
  29. package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
  30. package/dist/native/bridge/hooks/useIntent.js +95 -1
  31. package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
  32. package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
  33. package/dist/native/bridge/hooks/useNotification.js +318 -2
  34. package/dist/native/bridge/hooks/useSafeArea.js +84 -2
  35. package/dist/native/bridge/hooks/useVideoStream.js +434 -6
  36. package/dist/native/bridge/hooks/utils.js +27 -1
  37. package/dist/native/bridge/hooks.js +16 -1
  38. package/dist/native/bridge/perf/collectors/ApiCollector.js +85 -0
  39. package/dist/native/bridge/perf/collectors/CacheCollector.js +215 -0
  40. package/dist/native/bridge/perf/collectors/HookCollector.js +165 -0
  41. package/dist/native/bridge/perf/collectors/InsightsCollector.js +505 -0
  42. package/dist/native/bridge/perf/collectors/InteractionCollector.js +273 -0
  43. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +124 -0
  44. package/dist/native/bridge/perf/collectors/NavigationCollector.js +294 -0
  45. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +183 -0
  46. package/dist/native/bridge/perf/collectors/RenderCollector.js +284 -0
  47. package/dist/native/bridge/perf/collectors/ScrollCollector.js +111 -0
  48. package/dist/native/bridge/perf/collectors/ThreadCollector.js +111 -0
  49. package/dist/native/bridge/perf/core/constants.js +126 -0
  50. package/dist/native/bridge/perf/core/measure.js +189 -0
  51. package/dist/native/bridge/perf/core/session.js +100 -0
  52. package/dist/native/bridge/perf/core/store.js +554 -0
  53. package/dist/native/bridge/perf/core/summary.js +174 -0
  54. package/dist/native/bridge/perf/core/traceExport.js +240 -0
  55. package/dist/native/bridge/perf/core/waterfalls.js +146 -0
  56. package/dist/native/bridge/perf/index.js +402 -0
  57. package/dist/native/bridge/safeArea.js +53 -5
  58. package/dist/native/bridge/standardizedHooks.js +595 -82
  59. package/dist/native/bridge/useBaseHook.js +274 -38
  60. package/dist/native/bridge/utils/CameraUtils.js +201 -56
  61. package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
  62. package/dist/native/bridge/utils/NativeBridge.js +425 -141
  63. package/dist/native/buildAndroid/assets.js +510 -13
  64. package/dist/native/buildAndroid/build.js +296 -19
  65. package/dist/native/buildAndroid/config.js +87 -1
  66. package/dist/native/buildAndroid/index.js +374 -1
  67. package/dist/native/buildAppAndroid.js +34 -2
  68. package/dist/native/buildAppIos.js +62 -2
  69. package/dist/native/buildIos/assets.js +609 -8
  70. package/dist/native/buildIos/build.js +639 -20
  71. package/dist/native/buildIos/config.js +294 -12
  72. package/dist/native/buildIos/index.js +343 -6
  73. package/dist/native/buildIos/plugins.js +343 -16
  74. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  75. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  76. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  77. package/dist/native/internalPluginUtils.js +232 -7
  78. package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +19 -0
  79. package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +4 -0
  80. package/dist/native/iosnativeWebView/Sources/Core/Utils/BootTimingUtility.swift +5 -1
  81. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +10 -0
  82. package/dist/native/iosnativeWebView/Sources/Core/Utils/DisplayLinkPerfMonitor.swift +89 -0
  83. package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +373 -0
  84. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +35 -1
  85. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +24 -11
  86. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +9 -0
  87. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +126 -0
  88. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +75 -1
  89. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +13 -0
  90. package/dist/native/package.json +3 -0
  91. package/dist/native/plugin-bridge/PluginBridge.js +227 -1
  92. package/dist/native/pluginComposerAndroid.js +407 -23
  93. package/dist/native/pluginComposerIos.js +435 -11
  94. package/dist/native/renameAndroidProject.js +1094 -88
  95. package/dist/native/setupEmulatorIos.js +289 -22
  96. package/dist/native/setupServer.js +160 -19
  97. package/dist/native/terminalProgress.js +199 -11
  98. package/dist/native/utils.js +193 -15
  99. package/dist/offline/catalyst-sw.js +145 -1
  100. package/dist/offline/registerServiceWorker.js +33 -1
  101. package/dist/otel.js +375 -46
  102. package/dist/router/ClientRouter.js +14 -2
  103. package/dist/router/ServerRouter.js +9 -1
  104. package/dist/router.js +1 -1
  105. package/dist/scripts/build.js +112 -3
  106. package/dist/scripts/checkVersion.js +3 -1
  107. package/dist/scripts/cleanCache.js +30 -1
  108. package/dist/scripts/generateOfflineManifest.js +116 -1
  109. package/dist/scripts/loadEnvironmentVariables.js +32 -6
  110. package/dist/scripts/plugins.js +309 -1
  111. package/dist/scripts/preServerInit.js +13 -0
  112. package/dist/scripts/scriptUtils.js +54 -3
  113. package/dist/scripts/serve.js +51 -4
  114. package/dist/scripts/start.js +47 -10
  115. package/dist/scripts/validator.js +149 -1
  116. package/dist/sentry.cjs +127 -0
  117. package/dist/sentry.js +125 -5
  118. package/dist/server/expressServer.js +250 -5
  119. package/dist/server/manifestCache.js +36 -0
  120. package/dist/server/renderer/ChunkExtractor.js +144 -0
  121. package/dist/server/renderer/document/Body.jsx +59 -0
  122. package/dist/server/renderer/document/FastRefresh.jsx +20 -0
  123. package/dist/server/renderer/document/Head.jsx +60 -0
  124. package/dist/server/renderer/document/index.jsx +2 -0
  125. package/dist/server/renderer/extract.js +149 -31
  126. package/dist/server/renderer/handler.jsx +433 -0
  127. package/dist/server/renderer/index.js +8 -1
  128. package/dist/server/renderer/render.js +47 -0
  129. package/dist/server/utils/botDetectionMiddleware.js +29 -0
  130. package/dist/server/utils/cjsRequire.js +10 -0
  131. package/dist/server/utils/userAgentUtil.js +87 -9
  132. package/dist/server/utils/validator.js +164 -1
  133. package/dist/vite/FastRefresh.jsx +20 -0
  134. package/dist/vite/inject-cache-key-plugin.js +276 -0
  135. package/dist/vite/loadCustomViteConfig.js +22 -0
  136. package/dist/vite/manifest-categorization-plugin.js +455 -0
  137. package/dist/vite/node-loader.mjs +458 -0
  138. package/dist/vite/scssParams.js +21 -0
  139. package/dist/vite/vite.config.client.js +130 -0
  140. package/dist/vite/vite.config.js +422 -0
  141. package/dist/vite/vite.config.server.js +84 -0
  142. package/dist/web-router/components/MetaTag.jsx +34 -0
  143. package/dist/web-router/components/RouterDataProvider.jsx +332 -0
  144. package/dist/web-router/components/Split.jsx +200 -0
  145. package/dist/web-router/components/SplitInview.jsx +106 -0
  146. package/dist/web-router/components/SsrRequestContext.jsx +14 -0
  147. package/dist/web-router/context.jsx +12 -0
  148. package/dist/web-router/hooks.jsx +72 -0
  149. package/dist/web-router/types/components/MetaTag.d.ts +2 -0
  150. package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
  151. package/dist/web-router/types/context.d.ts +14 -0
  152. package/dist/web-router/types/hooks.d.ts +1 -0
  153. package/dist/web-router/types/index.d.ts +3 -0
  154. package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
  155. package/dist/web-router/utils/metaDataUtils.jsx +119 -0
  156. package/mcp_v2/conversion-tasks.json +35 -17
  157. package/mcp_v2/knowledge-base.json +124 -13
  158. package/mcp_v2/lib/helpers.js +10 -0
  159. package/mcp_v2/mcp.js +4 -4
  160. package/mcp_v2/package.json +1 -0
  161. package/mcp_v2/tools/build.js +116 -20
  162. package/mcp_v2/tools/conversion.js +106 -5
  163. package/mcp_v2/tools/tasks.js +18 -14
  164. package/package.json +35 -68
  165. package/babel.config.js +0 -32
  166. package/dist/index.js +0 -1
  167. package/dist/scripts/devBuild.js +0 -3
  168. package/dist/scripts/devServe.js +0 -3
  169. package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
  170. package/dist/scripts/registerAliases.js +0 -6
  171. package/dist/server/renderer/document/Body.js +0 -11
  172. package/dist/server/renderer/document/Head.js +0 -9
  173. package/dist/server/renderer/document/index.js +0 -1
  174. package/dist/server/renderer/handler.js +0 -47
  175. package/dist/server/startServer.js +0 -57
  176. package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
  177. package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
  178. package/dist/webpack/babel.config.client.js +0 -1
  179. package/dist/webpack/babel.config.ssr.js +0 -1
  180. package/dist/webpack/base.babel.js +0 -7
  181. package/dist/webpack/development.client.babel.js +0 -10
  182. package/dist/webpack/production.client.babel.js +0 -4
  183. package/dist/webpack/production.ssr.babel.js +0 -8
  184. package/dist/webpack/scssParams.js +0 -9
@@ -0,0 +1,29 @@
1
+ import { getUserAgentDetails } from "./userAgentUtil.js"
2
+
3
+ // Loaded once at startup when OTEL is enabled; null otherwise.
4
+ let _context, _trace, _IS_BOT_KEY
5
+ if (process.env.OTEL_ENABLE === true) {
6
+ try {
7
+ const { context, trace } = await import("@opentelemetry/api")
8
+ const { IS_BOT_KEY } = await import("../../otel.js")
9
+ _context = context
10
+ _trace = trace
11
+ _IS_BOT_KEY = IS_BOT_KEY
12
+ } catch {
13
+ // otel packages not installed — context injection skipped
14
+ }
15
+ }
16
+
17
+ export function botDetectionMiddleware(req, res, next) {
18
+ const ua = req.headers["user-agent"] || ""
19
+ const { googleBot, aiBot, statusCakeBot } = getUserAgentDetails(ua)
20
+ const isBot = !!(googleBot || aiBot || statusCakeBot)
21
+ res.locals.is_bot = isBot
22
+
23
+ if (_context && _IS_BOT_KEY) {
24
+ _trace?.getActiveSpan()?.setAttribute("http.response.is_bot", isBot)
25
+ _context.with(_context.active().setValue(_IS_BOT_KEY, isBot), next)
26
+ } else {
27
+ next()
28
+ }
29
+ }
@@ -0,0 +1,10 @@
1
+ import { createRequire } from "module"
2
+
3
+ /**
4
+ * Shared helper to bridge ESM server entry files with legacy CommonJS helpers.
5
+ *
6
+ * Usage (in an ESM server file):
7
+ * import { cjsRequire } from "catalyst-core/server/cjsRequire"
8
+ * const legacyHelper = cjsRequire("./legacy/helper")
9
+ */
10
+ export const cjsRequire = createRequire(import.meta.url)
@@ -1,15 +1,93 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.getUserAgentDetails=void 0;var _uaParserJs=_interopRequireDefault(require("ua-parser-js"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const googleBots={"APIs-Google":"APIs-Google",AdSense:"Mediapartners-Google","AdsBot Mobile Web Android":"AdsBot-Google-Mobile","AdsBot Mobile Web":"AdsBot-Google-Mobile",AdsBot:"AdsBot-Google","Mobile AdSense":"Mediapartners-Google","Mobile Apps Android":"AdsBot-Google-Mobile-Apps",Feedfetcher:"FeedFetcher-Google","Google Read Aloud":"Google-Read-Aloud","Duplex on the Web":"DuplexWeb-Google","Google Favicon":"Google Favicon","Web Light":"googleweblight","Amp crawler":"Google-AMPHTM",Googlebot:"Googlebot","Google Inspection Tool":"Google-InspectionTool","Google Agent":"Google-Agent"};// AI Crawler bots - for improved SEO with AI search engines
2
- const aiCrawlerBots={// OpenAI/ChatGPT bots
3
- GPTBot:"GPTBot","ChatGPT-User":"ChatGPT-User","OAI-SearchBot":"OAI-SearchBot",// Anthropic/Claude bots
4
- ClaudeBot:"ClaudeBot","Claude-User":"Claude-User","Claude-SearchBot":"Claude-SearchBot",// Perplexity bots
5
- PerplexityBot:"PerplexityBot","Perplexity-User":"Perplexity-User",// Microsoft Bing bot (also powers Bing Chat/Copilot)
6
- Bingbot:"bingbot",// Meta Crawler bot
7
- MetaBot:"meta-externalagent"};/**
1
+ import parser from "ua-parser-js"
2
+
3
+ const googleBots = {
4
+ "APIs-Google": "APIs-Google",
5
+ AdSense: "Mediapartners-Google",
6
+ "AdsBot Mobile Web Android": "AdsBot-Google-Mobile",
7
+ "AdsBot Mobile Web": "AdsBot-Google-Mobile",
8
+ AdsBot: "AdsBot-Google",
9
+ "Mobile AdSense": "Mediapartners-Google",
10
+ "Mobile Apps Android": "AdsBot-Google-Mobile-Apps",
11
+ Feedfetcher: "FeedFetcher-Google",
12
+ "Google Read Aloud": "Google-Read-Aloud",
13
+ "Duplex on the Web": "DuplexWeb-Google",
14
+ "Google Favicon": "Google Favicon",
15
+ "Web Light": "googleweblight",
16
+ "Amp crawler": "Google-AMPHTM",
17
+ Googlebot: "Googlebot",
18
+ "Google Inspection Tool": "Google-InspectionTool",
19
+ "Google Agent": "Google-Agent",
20
+ }
21
+
22
+ // AI Crawler bots - for improved SEO with AI search engines
23
+ const aiCrawlerBots = {
24
+ // OpenAI/ChatGPT bots
25
+ GPTBot: "GPTBot",
26
+ "ChatGPT-User": "ChatGPT-User",
27
+ "OAI-SearchBot": "OAI-SearchBot",
28
+
29
+ // Anthropic/Claude bots
30
+ ClaudeBot: "ClaudeBot",
31
+ "Claude-User": "Claude-User",
32
+ "Claude-SearchBot": "Claude-SearchBot",
33
+
34
+ // Perplexity bots
35
+ PerplexityBot: "PerplexityBot",
36
+ "Perplexity-User": "Perplexity-User",
37
+
38
+ // Microsoft Bing bot (also powers Bing Chat/Copilot)
39
+ Bingbot: "bingbot",
40
+
41
+ // Meta Crawler bot
42
+ MetaBot: "meta-externalagent",
43
+ }
44
+
45
+ // StatusCake synthetic monitoring user agents.
46
+ // STATUS_CAKE_USER_AGENT_MOBILE matches the mobile pagespeed monitor — kept as a
47
+ // named export so apps that compare UAs directly (mweb's document.js, App/index.js)
48
+ // can import the same constant catalyst uses for its bot detection.
49
+ export const STATUS_CAKE_USER_AGENT_MOBILE = "StatusCake_Pagespeed_Indev"
50
+ const statusCakeBots = {
51
+ "StatusCake Pagespeed Mobile": STATUS_CAKE_USER_AGENT_MOBILE,
52
+ StatusCake: "StatusCake",
53
+ }
54
+
55
+ /**
8
56
  * check if user agent contains bot data
9
57
  * @param {string} ua - user agent
10
58
  * @return {string|null}
11
- */const getGoogleBot=ua=>{for(let key in googleBots){if(ua.includes(googleBots[key]))return key;}return null;};const getAICrawlerBot=ua=>{for(let key in aiCrawlerBots){if(ua.includes(aiCrawlerBots[key]))return key;}return null;};/**
59
+ */
60
+ const getGoogleBot = (ua) => {
61
+ for (let key in googleBots) {
62
+ if (ua.includes(googleBots[key])) return key
63
+ }
64
+ return null
65
+ }
66
+
67
+ const getAICrawlerBot = (ua) => {
68
+ for (let key in aiCrawlerBots) {
69
+ if (ua.includes(aiCrawlerBots[key])) return key
70
+ }
71
+ return null
72
+ }
73
+
74
+ const getStatusCakeBot = (ua) => {
75
+ for (let key in statusCakeBots) {
76
+ if (ua.includes(statusCakeBots[key])) return key
77
+ }
78
+ return null
79
+ }
80
+
81
+ /**
12
82
  * returns object which contains google bot and user-agent info
13
83
  * @param {string} ua - user agent
14
84
  * @return {object} - returns user agent
15
- */const getUserAgentDetails=ua=>{const agentDetails=(0,_uaParserJs.default)(ua);const googleBot=getGoogleBot(ua);const aiBot=getAICrawlerBot(ua);return{...agentDetails,googleBot,aiBot};};exports.getUserAgentDetails=getUserAgentDetails;
85
+ */
86
+ export const getUserAgentDetails = (ua) => {
87
+ const agentDetails = parser(ua)
88
+ const googleBot = getGoogleBot(ua)
89
+ const aiBot = getAICrawlerBot(ua)
90
+ const statusCakeBot = getStatusCakeBot(ua)
91
+
92
+ return { ...agentDetails, googleBot, aiBot, statusCakeBot }
93
+ }
@@ -1 +1,164 @@
1
- "use strict";const pc=require("picocolors");const handleError=e=>{console.log(pc.red("Failed to start server: "),e);};const safeCall=(fn,...args)=>{try{if(!fn)return;if(typeof fn!=="function"){console.log(pc.red("Invalid lifecycle method defined in server/index.js"));return;}fn(...args);}catch(e){console.log(pc.red("Failed to execute lifecycle method: "),fn.name,e);}};const validatePreInitServer=fn=>{try{if(!fn)throw new Error("preServerInit named function should be defined in server/index.js");if(typeof fn!=="function")throw new Error("preServerInit should be function present in server/index.js");return true;}catch(e){handleError(e);}};const validateMiddleware=fn=>{try{if(!fn)throw new Error("addMiddlewares named function not found in server/server.js");if(typeof fn!=="function")throw new Error("addMiddlewares should be function present in server/server.js");return true;}catch(e){handleError(e);}};const validateReducerFunction=fn=>{try{if(!fn)throw new Error("reducer not found in src/js/containers/App/reducer");if(typeof fn!=="function")throw new Error("reducer should present in src/js/containers/App/reducer");return true;}catch(e){handleError(e);}};const validateConfigFile=obj=>{try{if(!obj)throw new Error("config not found in config folder");if(typeof obj!=="object")throw new Error("config object should be exported from config folder inside your project root directory");if(typeof obj==="object"){const requiredConfigKeys={NODE_SERVER_HOSTNAME:"",NODE_SERVER_PORT:"",WEBPACK_DEV_SERVER_HOSTNAME:"",WEBPACK_DEV_SERVER_PORT:"",BUILD_OUTPUT_PATH:"",PUBLIC_STATIC_ASSET_PATH:"",PUBLIC_STATIC_ASSET_URL:"",CLIENT_ENV_VARIABLES:[],ANALYZE_BUNDLE:""};for(let key in requiredConfigKeys){if(!(key in obj))throw new Error(`${key} key not found inside config.json`);}}return true;}catch(e){handleError(e);}};const validatePackageJson=obj=>{try{if(!obj)throw new Error("package.json not found in the project");if(typeof obj!=="object")throw new Error("package.json should be defined in project root directory");return true;}catch(e){handleError(e);}};const validateModuleAlias=obj=>{try{if(!obj)throw new Error("moduleAliases not found in package.json file present in root directory.");if(typeof obj!=="object")throw new Error("moduleAliases named object should be exported from package.json");if(typeof obj==="object"){const requiredModuleAliases={"@api":"api.js","@containers":"src/js/containers","@server":"server","@config":"config","@css":"src/static/css","@routes":"src/js/routes/"};for(let key in requiredModuleAliases){if(key.includes("catalyst"))throw new Error(`Catalyst keyword is restricted for defining aliases`);if(!(key in obj))throw new Error(`${key} module alias not defined inside package.json`);}}return true;}catch(e){handleError(e);}};const validateConfigureStore=fn=>{try{if(!fn)throw new Error("configureStore not found in file src/js/store/index.js");if(typeof fn!=="function")throw new Error("configureStore should be function exported from src/js/store/index.js");return true;}catch(e){handleError(e);}};const validateGetRoutes=fn=>{try{if(!fn)throw new Error("getRoutes not found in file src/js/routes/utils.js");if(typeof fn!=="function")throw new Error("getRoutes should be function exported from src/js/routers/index.js");return true;}catch(e){handleError(e);}};const validateCustomDocument=fn=>{try{if(!fn)throw new Error("document not found in file server/document.js");if(typeof fn!=="function")throw new Error("document should be a react component exported from server/document.js");return true;}catch(e){handleError(e);}};module.exports={safeCall,validateConfigFile,validateConfigureStore,validateCustomDocument,validateGetRoutes,validatePackageJson,validateReducerFunction,validateModuleAlias,validatePreInitServer,validateMiddleware};
1
+ import pc from "picocolors"
2
+
3
+ const handleError = (e) => {
4
+ console.log(pc.red("Failed to start server: "), e)
5
+ }
6
+
7
+ const validatePreInitServer = (fn) => {
8
+ try {
9
+ if (!fn) throw new Error("preServerInit named function should be defined in server/index.js")
10
+ if (typeof fn !== "function")
11
+ throw new Error("preServerInit should be function present in server/index.js")
12
+ return true
13
+ } catch (e) {
14
+ handleError(e)
15
+ }
16
+ }
17
+
18
+ const validateMiddleware = (fn) => {
19
+ try {
20
+ if (!fn) throw new Error("addMiddlewares named function not found in server/server.js")
21
+ if (typeof fn !== "function")
22
+ throw new Error("addMiddlewares should be function present in server/server.js")
23
+ return true
24
+ } catch (e) {
25
+ handleError(e)
26
+ }
27
+ }
28
+
29
+ const validateReducerFunction = (fn) => {
30
+ try {
31
+ if (!fn) throw new Error("reducer not found in src/js/containers/App/reducer")
32
+ if (typeof fn !== "function")
33
+ throw new Error("reducer should present in src/js/containers/App/reducer")
34
+ return true
35
+ } catch (e) {
36
+ handleError(e)
37
+ }
38
+ }
39
+
40
+ const validateConfigFile = (obj) => {
41
+ try {
42
+ if (!obj) throw new Error("config not found in config folder")
43
+ if (typeof obj !== "object")
44
+ throw new Error(
45
+ "config object should be exported from config folder inside your project root directory"
46
+ )
47
+ if (typeof obj === "object") {
48
+ const requiredConfigKeys = {
49
+ NODE_SERVER_HOSTNAME: "",
50
+ NODE_SERVER_PORT: "",
51
+ WEBPACK_DEV_SERVER_HOSTNAME: "",
52
+ WEBPACK_DEV_SERVER_PORT: "",
53
+ BUILD_OUTPUT_PATH: "",
54
+ PUBLIC_STATIC_ASSET_PATH: "",
55
+ PUBLIC_STATIC_ASSET_URL: "",
56
+ CLIENT_ENV_VARIABLES: [],
57
+ ANALYZE_BUNDLE: "",
58
+ }
59
+ for (let key in requiredConfigKeys) {
60
+ if (!(key in obj)) throw new Error(`${key} key not found inside config.json`)
61
+ }
62
+ }
63
+ return true
64
+ } catch (e) {
65
+ handleError(e)
66
+ }
67
+ }
68
+
69
+ const validatePackageJson = (obj) => {
70
+ try {
71
+ if (!obj) throw new Error("package.json not found in the project")
72
+ if (typeof obj !== "object")
73
+ throw new Error("package.json should be defined in project root directory")
74
+ return true
75
+ } catch (e) {
76
+ handleError(e)
77
+ }
78
+ }
79
+
80
+ const validateModuleAlias = (obj) => {
81
+ try {
82
+ if (!obj) throw new Error("moduleAliases not found in package.json file present in root directory.")
83
+ if (typeof obj !== "object")
84
+ throw new Error("moduleAliases named object should be exported from package.json")
85
+ if (typeof obj === "object") {
86
+ const requiredModuleAliases = {
87
+ "@api": "api.js",
88
+ "@containers": "src/js/containers",
89
+ "@server": "server",
90
+ "@config": "config",
91
+ "@css": "src/static/css",
92
+ "@routes": "src/js/routes/",
93
+ }
94
+ for (let key in requiredModuleAliases) {
95
+ if (key.includes("catalyst"))
96
+ throw new Error(`Catalyst keyword is restricted for defining aliases`)
97
+ if (!(key in obj)) throw new Error(`${key} module alias not defined inside package.json`)
98
+ }
99
+ }
100
+ return true
101
+ } catch (e) {
102
+ handleError(e)
103
+ }
104
+ }
105
+
106
+ const validateConfigureStore = (fn) => {
107
+ try {
108
+ if (!fn) throw new Error("configureStore not found in file src/js/store/index.js")
109
+ if (typeof fn !== "function")
110
+ throw new Error("configureStore should be function exported from src/js/store/index.js")
111
+ return true
112
+ } catch (e) {
113
+ handleError(e)
114
+ }
115
+ }
116
+
117
+ const validateGetRoutes = (fn) => {
118
+ try {
119
+ if (!fn) throw new Error("getRoutes not found in file src/js/routes/utils.js")
120
+ if (typeof fn !== "function")
121
+ throw new Error("getRoutes should be function exported from src/js/routers/index.js")
122
+ return true
123
+ } catch (e) {
124
+ handleError(e)
125
+ }
126
+ }
127
+
128
+ const validateCustomDocument = (fn) => {
129
+ try {
130
+ if (!fn) throw new Error("document not found in file server/document.js")
131
+ if (typeof fn !== "function")
132
+ throw new Error("document should be a react component exported from server/document.js")
133
+ return true
134
+ } catch (e) {
135
+ handleError(e)
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Safely call a function, catching and logging any errors.
141
+ * Used for user-defined hooks (onRouteMatch, onFetcherError, etc.)
142
+ * that should never crash the SSR pipeline.
143
+ */
144
+ const safeCall = (fn, ...args) => {
145
+ if (typeof fn !== "function") return
146
+ try {
147
+ return fn(...args)
148
+ } catch (e) {
149
+ console.error("Error in user hook:", e)
150
+ }
151
+ }
152
+
153
+ export {
154
+ validateConfigFile,
155
+ validateConfigureStore,
156
+ validateCustomDocument,
157
+ validateGetRoutes,
158
+ validatePackageJson,
159
+ validateReducerFunction,
160
+ validateModuleAlias,
161
+ validatePreInitServer,
162
+ validateMiddleware,
163
+ safeCall,
164
+ }
@@ -0,0 +1,20 @@
1
+ import React from "react"
2
+
3
+ const FastRefresh = () => {
4
+ return (
5
+ <script
6
+ type="module"
7
+ // eslint-disable-next-line risxss/catch-potential-xss-react
8
+ dangerouslySetInnerHTML={{
9
+ __html: `
10
+ import { injectIntoGlobalHook } from "/@react-refresh";
11
+ injectIntoGlobalHook(window);
12
+ window.$RefreshReg$ = () => {};
13
+ window.$RefreshSig$ = () => (type) => type;
14
+ `,
15
+ }}
16
+ />
17
+ )
18
+ }
19
+
20
+ export default FastRefresh
@@ -0,0 +1,276 @@
1
+ import path from "path"
2
+ import { readFileSync, existsSync, statSync } from "fs"
3
+ import { fileURLToPath } from "url"
4
+
5
+ const __filename = fileURLToPath(import.meta.url)
6
+ const __dirname = path.dirname(__filename)
7
+
8
+ // Cache for loaded aliases
9
+ let aliasCache = null
10
+
11
+ /**
12
+ * Load and resolve module aliases from both the consuming app and catalyst-core,
13
+ * using the same algorithm as node-loader.mjs.
14
+ */
15
+ function loadAliases() {
16
+ if (aliasCache) {
17
+ return aliasCache
18
+ }
19
+
20
+ const appRoot = process.env.src_path || process.cwd()
21
+ const appPackageJsonConfig = path.resolve(appRoot, "package.json")
22
+ const catalystPackageJsonConfig = path.resolve(__dirname, "../../package.json")
23
+
24
+ let appModuleAliases = {}
25
+ let catalystModuleAliases = {}
26
+
27
+ try {
28
+ const appPackageJson = JSON.parse(readFileSync(appPackageJsonConfig, "utf8"))
29
+ appModuleAliases = appPackageJson._moduleAliases || {}
30
+ } catch {
31
+ // Silently fail if app package.json doesn't exist or is invalid
32
+ }
33
+
34
+ try {
35
+ const catalystPackageJson = JSON.parse(readFileSync(catalystPackageJsonConfig, "utf8"))
36
+ catalystModuleAliases = catalystPackageJson._moduleAliases || {}
37
+ } catch {
38
+ // Silently fail if catalyst package.json doesn't exist or is invalid
39
+ }
40
+
41
+ // Application aliases take precedence over catalyst-core aliases
42
+ const allAliases = { ...catalystModuleAliases, ...appModuleAliases }
43
+
44
+ aliasCache = {}
45
+ for (const [alias, aliasPath] of Object.entries(allAliases)) {
46
+ if (aliasPath && typeof aliasPath === "string") {
47
+ try {
48
+ const resolvedPath = path.resolve(appRoot, ...aliasPath.split("/"))
49
+ aliasCache[alias] = resolvedPath
50
+ } catch (error) {
51
+ console.warn(`Failed to resolve alias ${alias}:`, error.message)
52
+ }
53
+ }
54
+ }
55
+
56
+ return aliasCache
57
+ }
58
+
59
+ /**
60
+ * Try to resolve a path by applying common JS/TS/ESM extensions and index fallbacks,
61
+ * matching the same resolution order as node-loader.mjs.
62
+ */
63
+ function resolveWithExtensions(basePath) {
64
+ if (existsSync(basePath) && statSync(basePath).isFile()) {
65
+ return basePath
66
+ }
67
+
68
+ const extensions = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".scss"]
69
+
70
+ for (const ext of extensions) {
71
+ const candidate = `${basePath}${ext}`
72
+ if (existsSync(candidate)) {
73
+ return candidate
74
+ }
75
+ }
76
+
77
+ const currentExt = path.extname(basePath)
78
+ if (currentExt) {
79
+ const withoutExt = basePath.slice(0, -currentExt.length)
80
+ for (const ext of extensions) {
81
+ if (ext === currentExt) continue
82
+ const candidate = `${withoutExt}${ext}`
83
+ if (existsSync(candidate)) {
84
+ return candidate
85
+ }
86
+ }
87
+ }
88
+
89
+ for (const ext of extensions) {
90
+ const candidate = path.join(basePath, `index${ext}`)
91
+ if (existsSync(candidate)) {
92
+ return candidate
93
+ }
94
+ }
95
+
96
+ return null
97
+ }
98
+
99
+ /**
100
+ * Resolve import path to an absolute file path using the same alias algorithm as node-loader.mjs.
101
+ * Falls back to relative resolution when no alias matches.
102
+ */
103
+ function resolveToAbsolutePath(importPath, importerId) {
104
+ const aliases = loadAliases()
105
+
106
+ // 1. Try alias resolution (same logic as node-loader resolveAlias)
107
+ for (const [alias, aliasPath] of Object.entries(aliases)) {
108
+ if (importPath === alias || importPath.startsWith(alias + "/")) {
109
+ const remainingPath = importPath === alias ? "" : importPath.slice(alias.length + 1)
110
+ const rawResolved = path.join(aliasPath, remainingPath)
111
+ const finalResolved = resolveWithExtensions(rawResolved)
112
+ if (finalResolved) {
113
+ return path.resolve(finalResolved)
114
+ }
115
+ }
116
+ }
117
+
118
+ // 2. Relative imports — resolve from the importer's directory
119
+ if (importPath.startsWith("./") || importPath.startsWith("../")) {
120
+ const importerDir = path.dirname(importerId)
121
+ const absolutePath = path.resolve(importerDir, importPath)
122
+ const resolved = resolveWithExtensions(absolutePath)
123
+ if (resolved) {
124
+ return resolved
125
+ }
126
+ return absolutePath
127
+ }
128
+
129
+ return null
130
+ }
131
+
132
+ /**
133
+ * Resolve import path to a manifest key
134
+ * @param {string} importPath - The import path to resolve
135
+ * @param {string} importerId - The file ID that imports this path
136
+ * @returns {string} - The resolved manifest key (relative to manifest location: build/.vite/)
137
+ */
138
+ function resolveImportPath(importPath, importerId) {
139
+ try {
140
+ const absolutePath = resolveToAbsolutePath(importPath, importerId)
141
+
142
+ if (absolutePath && process.env.src_path) {
143
+ // Manifest is located at: build/.vite/manifest.json
144
+ const manifestDir = path.join(process.env.src_path, "build", ".vite")
145
+ const relativePath = path.relative(manifestDir, absolutePath)
146
+ return relativePath.replace(/\\/g, "/")
147
+ }
148
+
149
+ // Fallback: strip leading ./ and return as-is
150
+ return importPath.replace(/^\.\//, "")
151
+ } catch (error) {
152
+ console.warn(`Error resolving import path ${importPath}:`, error.message)
153
+ return importPath.replace(/^\.\//, "")
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Walk an AST node and invoke the visitor for every node.
159
+ */
160
+ function walk(node, visitor) {
161
+ if (!node || typeof node !== "object") return
162
+
163
+ visitor(node)
164
+
165
+ for (const key of Object.keys(node)) {
166
+ const child = node[key]
167
+ if (Array.isArray(child)) {
168
+ for (const item of child) {
169
+ if (item && typeof item.type === "string") {
170
+ walk(item, visitor)
171
+ }
172
+ }
173
+ } else if (child && typeof child.type === "string") {
174
+ walk(child, visitor)
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Vite plugin to add cacheKey to split calls with manifest keys.
181
+ * Uses Rollup's this.parse() for robust AST-based detection instead of regex.
182
+ */
183
+ export function injectCacheKeyPlugin() {
184
+ return {
185
+ name: "split-cache-key",
186
+
187
+ async transform(code, id) {
188
+ if (!/\.(js|jsx|ts|tsx)$/.test(id)) return null
189
+ if (id.includes("node_modules")) return null
190
+ if (!code.includes("split")) return null
191
+
192
+ let ast
193
+ try {
194
+ ast = this.parse(code)
195
+ } catch {
196
+ return null
197
+ }
198
+
199
+ // Collect split() call sites that need a cacheKey injected.
200
+ // Process in reverse source order so earlier indices stay valid after splicing.
201
+ const sites = []
202
+
203
+ walk(ast, (node) => {
204
+ // Match: split(<arrow>, <options?>, <cacheKey?>)
205
+ // Also supports: split(<arrow>, <options?>, <rootOptions>, <cacheKey?>)
206
+ if (node.type !== "CallExpression") return
207
+ if (node.callee.type !== "Identifier" || node.callee.name !== "split") return
208
+
209
+ const args = node.arguments
210
+ if (args.length === 0) return
211
+
212
+ // First arg must be a function that returns import("...")
213
+ // Supports both: () => import("...") and function() { return import("...") }
214
+ const loader = args[0]
215
+ let importExpr
216
+
217
+ if (loader.type === "ArrowFunctionExpression") {
218
+ // () => import("...")
219
+ importExpr = loader.body
220
+ } else if (loader.type === "FunctionExpression") {
221
+ // function() { return import("...") }
222
+ const stmts = loader.body.body
223
+ if (stmts.length !== 1 || stmts[0].type !== "ReturnStatement") return
224
+ importExpr = stmts[0].argument
225
+ } else {
226
+ return
227
+ }
228
+
229
+ if (!importExpr || importExpr.type !== "ImportExpression") return
230
+ if (importExpr.source.type !== "Literal" || typeof importExpr.source.value !== "string")
231
+ return
232
+
233
+ const importPath = importExpr.source.value
234
+
235
+ // Already has a cacheKey (3rd or 4th arg) -> skip
236
+ if (args.length >= 4) {
237
+ const fourth = args[3]
238
+ if (fourth?.type === "Literal" && typeof fourth.value === "string") return
239
+ }
240
+ if (args.length >= 3) {
241
+ const third = args[2]
242
+ if (third?.type === "Literal" && typeof third.value === "string") return
243
+ }
244
+
245
+ sites.push({
246
+ importPath,
247
+ callEnd: node.end, // position of the closing )
248
+ argCount: args.length,
249
+ })
250
+ })
251
+
252
+ if (sites.length === 0) return null
253
+
254
+ // Sort descending by position so splicing doesn't shift earlier indices
255
+ sites.sort((a, b) => b.callEnd - a.callEnd)
256
+
257
+ let transformed = code
258
+ for (const { importPath, callEnd, argCount } of sites) {
259
+ let manifestKey = resolveImportPath(importPath, id)
260
+ if (manifestKey && !manifestKey.endsWith(".js")) {
261
+ manifestKey = manifestKey + ".js"
262
+ }
263
+
264
+ // Insert just before the closing ) of the split() call
265
+ let insertion = `, "${manifestKey}"`
266
+ if (argCount === 1) {
267
+ insertion = `, {}, "${manifestKey}"`
268
+ }
269
+
270
+ transformed = transformed.slice(0, callEnd - 1) + insertion + transformed.slice(callEnd - 1)
271
+ }
272
+
273
+ return { code: transformed, map: null }
274
+ },
275
+ }
276
+ }
@@ -0,0 +1,22 @@
1
+ import { existsSync } from "fs"
2
+ import path from "path"
3
+ import { pathToFileURL } from "url"
4
+
5
+ const emptyConfig = {
6
+ ssrPlugins: [],
7
+ clientPlugins: [],
8
+ }
9
+
10
+ export async function loadCustomViteConfig() {
11
+ const configNames = ["buildConfig.js", "webpackConfig.js"]
12
+ const configPath = configNames
13
+ .map((configName) => path.join(process.env.src_path, configName))
14
+ .find((candidatePath) => existsSync(candidatePath))
15
+
16
+ if (!configPath) {
17
+ return emptyConfig
18
+ }
19
+
20
+ const configModule = await import(pathToFileURL(configPath).href)
21
+ return configModule.default || configModule
22
+ }