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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) 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/globals.d.ts +7 -0
  6. package/dist/index.jsx +8 -0
  7. package/dist/logger.js +74 -3
  8. package/dist/native/androidProject/app/build.gradle.kts +45 -4
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +86 -0
  10. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +8 -0
  11. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +173 -13
  12. package/dist/native/androidProject/settings.gradle.kts +10 -0
  13. package/dist/native/androidSetup.js +300 -8
  14. package/dist/native/bridge/WebBridge.js +481 -59
  15. package/dist/native/bridge/constants/NativeInterfaces.js +304 -45
  16. package/dist/native/bridge/errors.js +250 -26
  17. package/dist/native/bridge/hooks/index.js +132 -1
  18. package/dist/native/bridge/hooks/useAI.js +94 -0
  19. package/dist/native/bridge/hooks/useCamera.js +347 -9
  20. package/dist/native/bridge/hooks/useDataProtection.js +200 -2
  21. package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
  22. package/dist/native/bridge/hooks/useFilePicker.js +429 -2
  23. package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
  24. package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
  25. package/dist/native/bridge/hooks/useIntent.js +95 -1
  26. package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
  27. package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
  28. package/dist/native/bridge/hooks/useNotification.js +318 -2
  29. package/dist/native/bridge/hooks/useSafeArea.js +84 -2
  30. package/dist/native/bridge/hooks/useVideoStream.js +434 -6
  31. package/dist/native/bridge/hooks/utils.js +27 -1
  32. package/dist/native/bridge/hooks.js +16 -1
  33. package/dist/native/bridge/perf/collectors/ApiCollector.js +66 -3
  34. package/dist/native/bridge/perf/collectors/CacheCollector.js +186 -11
  35. package/dist/native/bridge/perf/collectors/HookCollector.js +141 -19
  36. package/dist/native/bridge/perf/collectors/InsightsCollector.js +481 -23
  37. package/dist/native/bridge/perf/collectors/InteractionCollector.js +245 -10
  38. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +107 -8
  39. package/dist/native/bridge/perf/collectors/NavigationCollector.js +273 -32
  40. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +178 -2
  41. package/dist/native/bridge/perf/collectors/RenderCollector.js +258 -14
  42. package/dist/native/bridge/perf/collectors/ScrollCollector.js +95 -9
  43. package/dist/native/bridge/perf/collectors/ThreadCollector.js +90 -6
  44. package/dist/native/bridge/perf/core/constants.js +103 -20
  45. package/dist/native/bridge/perf/core/measure.js +179 -30
  46. package/dist/native/bridge/perf/core/session.js +89 -23
  47. package/dist/native/bridge/perf/core/store.js +554 -1
  48. package/dist/native/bridge/perf/core/summary.js +174 -1
  49. package/dist/native/bridge/perf/core/traceExport.js +240 -1
  50. package/dist/native/bridge/perf/core/waterfalls.js +146 -1
  51. package/dist/native/bridge/perf/index.js +359 -38
  52. package/dist/native/bridge/safeArea.js +53 -5
  53. package/dist/native/bridge/standardizedHooks.js +595 -82
  54. package/dist/native/bridge/useBaseHook.js +274 -38
  55. package/dist/native/bridge/utils/CameraUtils.js +201 -56
  56. package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
  57. package/dist/native/bridge/utils/NativeBridge.js +425 -141
  58. package/dist/native/buildAndroid/assets.js +510 -13
  59. package/dist/native/buildAndroid/build.js +296 -19
  60. package/dist/native/buildAndroid/config.js +87 -1
  61. package/dist/native/buildAndroid/index.js +406 -1
  62. package/dist/native/buildAppAndroid.js +34 -2
  63. package/dist/native/buildAppIos.js +62 -2
  64. package/dist/native/buildIos/assets.js +609 -8
  65. package/dist/native/buildIos/build.js +639 -20
  66. package/dist/native/buildIos/config.js +294 -12
  67. package/dist/native/buildIos/index.js +343 -6
  68. package/dist/native/buildIos/plugins.js +343 -16
  69. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  70. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  71. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  72. package/dist/native/internalPluginUtils.js +232 -7
  73. package/dist/native/package.json +3 -0
  74. package/dist/native/plugin-bridge/PluginBridge.js +227 -1
  75. package/dist/native/pluginComposerAndroid.js +407 -23
  76. package/dist/native/pluginComposerIos.js +435 -11
  77. package/dist/native/renameAndroidProject.js +1094 -88
  78. package/dist/native/setupEmulatorIos.js +289 -22
  79. package/dist/native/setupServer.js +160 -19
  80. package/dist/native/terminalProgress.js +199 -11
  81. package/dist/native/utils.js +193 -15
  82. package/dist/offline/catalyst-sw.js +145 -1
  83. package/dist/offline/registerServiceWorker.js +33 -1
  84. package/dist/otel.js +375 -46
  85. package/dist/router/ClientRouter.js +14 -2
  86. package/dist/router/ServerRouter.js +9 -1
  87. package/dist/router.js +1 -1
  88. package/dist/scripts/build.js +112 -3
  89. package/dist/scripts/checkVersion.js +3 -1
  90. package/dist/scripts/cleanCache.js +30 -1
  91. package/dist/scripts/generateOfflineManifest.js +116 -1
  92. package/dist/scripts/loadEnvironmentVariables.js +32 -6
  93. package/dist/scripts/plugins.js +309 -1
  94. package/dist/scripts/preServerInit.js +13 -0
  95. package/dist/scripts/scriptUtils.js +54 -3
  96. package/dist/scripts/serve.js +51 -4
  97. package/dist/scripts/start.js +47 -10
  98. package/dist/scripts/validator.js +149 -1
  99. package/dist/sentry.cjs +127 -0
  100. package/dist/sentry.js +125 -5
  101. package/dist/server/expressServer.js +296 -5
  102. package/dist/server/manifestCache.js +36 -0
  103. package/dist/server/renderer/ChunkExtractor.js +144 -0
  104. package/dist/server/renderer/document/Body.jsx +59 -0
  105. package/dist/server/renderer/document/FastRefresh.jsx +20 -0
  106. package/dist/server/renderer/document/Head.jsx +60 -0
  107. package/dist/server/renderer/document/index.jsx +2 -0
  108. package/dist/server/renderer/extract.js +149 -31
  109. package/dist/server/renderer/handler.jsx +450 -0
  110. package/dist/server/renderer/index.js +8 -1
  111. package/dist/server/renderer/render.js +47 -0
  112. package/dist/server/utils/botDetectionMiddleware.js +29 -0
  113. package/dist/server/utils/cjsRequire.js +10 -0
  114. package/dist/server/utils/userAgentUtil.js +87 -9
  115. package/dist/server/utils/validator.js +164 -1
  116. package/dist/vite/FastRefresh.jsx +20 -0
  117. package/dist/vite/inject-cache-key-plugin.js +276 -0
  118. package/dist/vite/loadCustomViteConfig.js +22 -0
  119. package/dist/vite/manifest-categorization-plugin.js +455 -0
  120. package/dist/vite/node-loader.mjs +458 -0
  121. package/dist/vite/scssParams.js +21 -0
  122. package/dist/vite/vite.config.client.js +130 -0
  123. package/dist/vite/vite.config.js +498 -0
  124. package/dist/vite/vite.config.server.js +84 -0
  125. package/dist/web-router/components/MetaTag.jsx +34 -0
  126. package/dist/web-router/components/RouterDataProvider.jsx +332 -0
  127. package/dist/web-router/components/Split.jsx +200 -0
  128. package/dist/web-router/components/SplitInview.jsx +106 -0
  129. package/dist/web-router/components/SsrRequestContext.jsx +14 -0
  130. package/dist/web-router/context.jsx +12 -0
  131. package/dist/web-router/hooks.jsx +72 -0
  132. package/dist/web-router/types/components/MetaTag.d.ts +2 -0
  133. package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
  134. package/dist/web-router/types/context.d.ts +14 -0
  135. package/dist/web-router/types/hooks.d.ts +1 -0
  136. package/dist/web-router/types/index.d.ts +3 -0
  137. package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
  138. package/dist/web-router/utils/metaDataUtils.jsx +119 -0
  139. package/mcp_v2/conversion-tasks.json +35 -17
  140. package/mcp_v2/knowledge-base.json +176 -17
  141. package/mcp_v2/lib/helpers.js +10 -0
  142. package/mcp_v2/mcp.js +40 -8
  143. package/mcp_v2/package.json +1 -0
  144. package/mcp_v2/tools/build.js +161 -37
  145. package/mcp_v2/tools/conversion.js +106 -5
  146. package/mcp_v2/tools/tasks.js +18 -14
  147. package/package.json +35 -68
  148. package/babel.config.js +0 -32
  149. package/dist/index.js +0 -1
  150. package/dist/scripts/devBuild.js +0 -3
  151. package/dist/scripts/devServe.js +0 -3
  152. package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
  153. package/dist/scripts/registerAliases.js +0 -6
  154. package/dist/server/renderer/document/Body.js +0 -11
  155. package/dist/server/renderer/document/Head.js +0 -9
  156. package/dist/server/renderer/document/index.js +0 -1
  157. package/dist/server/renderer/handler.js +0 -47
  158. package/dist/server/startServer.js +0 -57
  159. package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
  160. package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
  161. package/dist/webpack/babel.config.client.js +0 -1
  162. package/dist/webpack/babel.config.ssr.js +0 -1
  163. package/dist/webpack/base.babel.js +0 -7
  164. package/dist/webpack/development.client.babel.js +0 -10
  165. package/dist/webpack/production.client.babel.js +0 -4
  166. package/dist/webpack/production.ssr.babel.js +0 -8
  167. package/dist/webpack/scssParams.js +0 -9
@@ -1 +1,116 @@
1
- "use strict";const fs=require("fs");const path=require("path");function toRoutePath(basePath,routePath,isIndex){if(isIndex||routePath===undefined||routePath===null||routePath===""){return basePath||"/";}const nextPath=String(routePath).startsWith("/")?routePath:`${basePath}/${routePath}`;return`/${nextPath}`.replace(/\/+/g,"/").replace(/\/$/,"")||"/";}function routeRegex(pattern,exact=false){if(pattern==="*"||pattern==="/*")return"^/.*$";if(pattern==="/")return exact?"^/$":"^/.*$";const parts=pattern.split("/").filter(Boolean);const regex=parts.map(part=>{if(part==="*")return".*";if(part.startsWith(":"))return"[^/]+";return part.replace(/[|\\{}()[\]^$+?.]/g,"\\$&");}).join("/");return exact?`^/${regex}$`:`^/${regex}(?:/.*)?$`;}function collectOfflineRoutes(routes,basePath="",parentOffline=false,result=[]){for(const route of Array.isArray(routes)?routes:[]){if(!route||typeof route!=="object")continue;const offline=Object.prototype.hasOwnProperty.call(route,"offline")?route.offline===true:parentOffline;const isIndexRoute=route.index===true||route.path==="";const pattern=toRoutePath(basePath,route.path,isIndexRoute);const regex=routeRegex(pattern,isIndexRoute);if(offline&&!result.some(item=>item.pattern===pattern&&item.regex===regex)){result.push({pattern,regex});}collectOfflineRoutes(route.children,pattern,offline,result);}return result;}function loadAppRoutes(){require("./loadScriptsBeforeServerStarts.js");const emptyModule=()=>null;[".css",".scss",".sass",".less",".png",".jpg",".jpeg",".gif",".svg",".webp"].forEach(ext=>{require.extensions[ext]=emptyModule;});require("@babel/register")({babelrc:false,extensions:[".js",".jsx",".ts",".tsx"],ignore:[/node_modules/],presets:[["@babel/preset-env",{targets:{node:"current"}}],["@babel/preset-react",{runtime:"automatic"}],["@babel/preset-typescript",{isTSX:true,allExtensions:true}]],plugins:["@loadable/babel-plugin"]});const{getRoutes}=require(path.join(process.env.src_path,"src/js/routes/utils.js"));return typeof getRoutes==="function"?getRoutes():[];}function copyIfExists(source,destination){if(fs.existsSync(source))fs.copyFileSync(source,destination);}function main(){const appConfig=require(path.join(process.env.src_path,"config/config.json"));const publicDir=path.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH||appConfig.BUILD_OUTPUT_PATH,"public");const routes=collectOfflineRoutes(loadAppRoutes());fs.mkdirSync(publicDir,{recursive:true});fs.writeFileSync(path.join(publicDir,"catalyst-offline-manifest.json"),`${JSON.stringify({schemaVersion:1,buildId:Date.now().toString(36),routes},null,2)}\n`);copyIfExists(path.join(process.env.src_path,"public/offline.html"),path.join(publicDir,"offline.html"));fs.copyFileSync(path.join(__dirname,"../offline/catalyst-sw.js"),path.join(publicDir,"catalyst-sw.js"));console.log(`Catalyst offline manifest generated: ${routes.length} route(s)`);}main();
1
+ import fs from "fs"
2
+ import path from "path"
3
+ import { createServer as createViteServer } from "vite"
4
+ import { fileURLToPath } from "url"
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = path.dirname(__filename)
8
+
9
+ function toRoutePath(basePath, routePath, isIndex) {
10
+ if (isIndex || routePath === undefined || routePath === null || routePath === "") {
11
+ return basePath || "/"
12
+ }
13
+
14
+ const nextPath = String(routePath).startsWith("/") ? routePath : `${basePath}/${routePath}`
15
+ return `/${nextPath}`.replace(/\/+/g, "/").replace(/\/$/, "") || "/"
16
+ }
17
+
18
+ function routeRegex(pattern, exact = false) {
19
+ if (pattern === "*" || pattern === "/*") return "^/.*$"
20
+ if (pattern === "/") return exact ? "^/$" : "^/.*$"
21
+
22
+ const parts = pattern.split("/").filter(Boolean)
23
+ const regex = parts
24
+ .map((part) => {
25
+ if (part === "*") return ".*"
26
+ if (part.startsWith(":")) return "[^/]+"
27
+ return part.replace(/[|\\{}()[\]^$+?.]/g, "\\$&")
28
+ })
29
+ .join("/")
30
+
31
+ return exact ? `^/${regex}$` : `^/${regex}(?:/.*)?$`
32
+ }
33
+
34
+ export function collectOfflineRoutes(routes, basePath = "", parentOffline = false, result = []) {
35
+ for (const route of Array.isArray(routes) ? routes : []) {
36
+ if (!route || typeof route !== "object") continue
37
+
38
+ const offline = Object.prototype.hasOwnProperty.call(route, "offline")
39
+ ? route.offline === true
40
+ : parentOffline
41
+ const isIndexRoute = route.index === true || route.path === ""
42
+ const pattern = toRoutePath(basePath, route.path, isIndexRoute)
43
+ const regex = routeRegex(pattern, isIndexRoute)
44
+
45
+ if (offline && !result.some((item) => item.pattern === pattern && item.regex === regex)) {
46
+ result.push({ pattern, regex })
47
+ }
48
+
49
+ collectOfflineRoutes(route.children, pattern, offline, result)
50
+ }
51
+
52
+ return result
53
+ }
54
+
55
+ async function loadAppRoutes(appRoot) {
56
+ const vite = await createViteServer({
57
+ configFile: path.resolve(__dirname, "../vite/vite.config.js"),
58
+ root: appRoot,
59
+ appType: "custom",
60
+ logLevel: "error",
61
+ server: {
62
+ middlewareMode: true,
63
+ hmr: false,
64
+ },
65
+ optimizeDeps: {
66
+ noDiscovery: true,
67
+ },
68
+ })
69
+
70
+ try {
71
+ const routesModule = await vite.ssrLoadModule(path.join(appRoot, "src/js/routes/utils.js"))
72
+ return typeof routesModule.getRoutes === "function" ? routesModule.getRoutes() : []
73
+ } finally {
74
+ await vite.close()
75
+ }
76
+ }
77
+
78
+ function copyIfExists(source, destination) {
79
+ if (fs.existsSync(source)) fs.copyFileSync(source, destination)
80
+ }
81
+
82
+ export async function generateOfflineManifest() {
83
+ const appRoot = process.env.src_path
84
+ const appConfig = JSON.parse(fs.readFileSync(path.join(appRoot, "config/config.json"), "utf8"))
85
+ const buildDir = path.join(
86
+ appRoot,
87
+ process.env.BUILD_OUTPUT_PATH || appConfig.BUILD_OUTPUT_PATH || "build"
88
+ )
89
+ const routes = collectOfflineRoutes(await loadAppRoutes(appRoot))
90
+
91
+ fs.mkdirSync(buildDir, { recursive: true })
92
+ fs.writeFileSync(
93
+ path.join(buildDir, "catalyst-offline-manifest.json"),
94
+ `${JSON.stringify(
95
+ {
96
+ schemaVersion: 1,
97
+ buildId: Date.now().toString(36),
98
+ routes,
99
+ },
100
+ null,
101
+ 2
102
+ )}\n`
103
+ )
104
+
105
+ copyIfExists(path.join(appRoot, "public/offline.html"), path.join(buildDir, "offline.html"))
106
+ fs.copyFileSync(path.join(__dirname, "../offline/catalyst-sw.js"), path.join(buildDir, "catalyst-sw.js"))
107
+
108
+ console.log(`Catalyst offline manifest generated: ${routes.length} route(s)`)
109
+ }
110
+
111
+ if (process.argv[1] && path.resolve(process.argv[1]) === __filename) {
112
+ generateOfflineManifest().catch((error) => {
113
+ console.error("Failed to generate Catalyst offline manifest:", error)
114
+ process.exit(1)
115
+ })
116
+ }
@@ -1,7 +1,33 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;const appConfig=require("@catalyst/template/config/config.json");const{validateConfigFile}=require("@catalyst/scripts/validator.js");/**
1
+ import path from "path"
2
+ import fs from "fs"
3
+
4
+ const appConfigPath = path.resolve(process.env.src_path, "config/config.json")
5
+ // import { validateConfigFile } from "../scripts/validator"
6
+ /**
2
7
  * @description stores all config.json key value into process.env before server starts.
3
- */const loadEnvironmentVariables=()=>{if(validateConfigFile(appConfig)){for(let k in appConfig){// below code provides support for object handling present in config.
4
- // However, for usage on client the client logic in define plugin needs to be checked and updated
5
- process.env[k]=typeof appConfig[k]==="object"?JSON.stringify(appConfig[k]):appConfig[k];}}// Load Sentry configuration if it exists
6
- try{const sentryConfig=require("@catalyst/template/config/sentry.config.json");process.env.SENTRY_CONFIG=JSON.stringify(sentryConfig);}catch(error){// Sentry config is optional
7
- }};var _default=exports.default=loadEnvironmentVariables;
8
+ */
9
+ const loadEnvironmentVariables = async () => {
10
+ try {
11
+ const filterKeys = JSON.parse(process.env.filterKeys)
12
+ const configContent = fs.readFileSync(appConfigPath, "utf8")
13
+ const appConfig = JSON.parse(configContent)
14
+ const newConfig = {}
15
+ // Set environment variables from config
16
+ for (let k in appConfig) {
17
+ // Handle both primitive values and objects
18
+ newConfig[k] = typeof appConfig[k] === "object" ? JSON.stringify(appConfig[k]) : appConfig[k]
19
+ }
20
+ for (let i = 0; i < filterKeys.length; i++) {
21
+ const key = filterKeys[i]
22
+ // Handle both primitive values and objects
23
+ newConfig[key] =
24
+ typeof process.env[key] === "object" ? JSON.stringify(process.env[key]) : process.env[key]
25
+ }
26
+ process.env = newConfig
27
+ } catch (error) {
28
+ console.error("Error loading environment variables:", error)
29
+ throw error
30
+ }
31
+ }
32
+
33
+ export default loadEnvironmentVariables
@@ -1 +1,309 @@
1
- "use strict";const fs=require("fs");const path=require("path");const readline=require("node:readline");const{bold,cyan,dim,green,red,yellow}=require("picocolors");const{discoverInternalPlugins,resolveInternalPluginsRoot}=require("../native/internalPluginUtils.js");const CONFIG_PATH=path.join(process.cwd(),"config","config.json");const TICK="x";function detectIndent(rawConfig){const indentMatch=rawConfig.match(/\n([ \t]+)"/);return indentMatch?indentMatch[1]:" ";}function readJsonFile(filePath,label){if(!fs.existsSync(filePath)){throw new Error(`${label} not found at ${filePath}`);}const raw=fs.readFileSync(filePath,"utf8");try{return{raw,json:JSON.parse(raw)};}catch(error){throw new Error(`Invalid JSON in ${filePath}: ${error.message}`);}}function ensureObject(value,label){if(value==null){return{};}if(typeof value!=="object"||Array.isArray(value)){throw new Error(`${label} must be an object`);}return value;}function splitPluginConfig(pluginConfig,plugins){const selectorToPlugin=new Map();const matchesByConfigKey=new Map();const staleToggleKeys=[];plugins.forEach(plugin=>{selectorToPlugin.set(plugin.configKey,plugin);selectorToPlugin.set(plugin.id,plugin);});for(const[key,value]of Object.entries(pluginConfig)){if(typeof value!=="boolean"){throw new Error(`WEBVIEW_CONFIG.plugins.${key} must be boolean`);}const plugin=selectorToPlugin.get(key);if(!plugin){staleToggleKeys.push(key);continue;}const matches=matchesByConfigKey.get(plugin.configKey)||[];matches.push({key,value,plugin});matchesByConfigKey.set(plugin.configKey,matches);}const knownToggles={};for(const plugin of plugins){const matches=matchesByConfigKey.get(plugin.configKey)||[];const uniqueValues=[...new Set(matches.map(entry=>entry.value))];if(uniqueValues.length>1){throw new Error(`Conflicting toggle values for plugin '${plugin.id}' across keys: ${matches.map(entry=>`${entry.key}=${entry.value}`).join(", ")}`);}if(matches.length>0){knownToggles[plugin.configKey]=matches[0].value;}}return{knownToggles,staleToggleKeys};}function createSession({configPath,rawConfig,config,plugins,pluginsRoot}){const webviewConfig=config.WEBVIEW_CONFIG==null?{}:ensureObject(config.WEBVIEW_CONFIG,"WEBVIEW_CONFIG");const pluginConfig=ensureObject(webviewConfig.plugins,"WEBVIEW_CONFIG.plugins");const{knownToggles,staleToggleKeys}=splitPluginConfig(pluginConfig,plugins);return{configPath,config,indent:detectIndent(rawConfig),pluginsRoot,plugins:plugins.map(plugin=>({...plugin,enabled:knownToggles[plugin.configKey]??false})),staleToggleKeys,notice:"",selectedIndex:0};}function buildPersistedPluginConfig(session){const nextConfig={};for(const plugin of session.plugins){nextConfig[plugin.configKey]=plugin.enabled;}return nextConfig;}function renderSession(session){if(typeof console.clear==="function"){console.clear();}console.log(bold("Catalyst Internal Plugins"));console.log(dim(`Config: ${session.configPath}`));console.log(dim(`Catalog: ${session.pluginsRoot}`));console.log("");if(session.staleToggleKeys.length>0){console.log(yellow(`Stale plugin toggle keys will be removed on save: ${session.staleToggleKeys.join(", ")}`));console.log("");}session.plugins.forEach((plugin,index)=>{const isSelected=index===session.selectedIndex;const pointer=isSelected?cyan(">"):" ";const checkbox=plugin.enabled?green(`[${TICK}]`):dim("[ ]");const title=isSelected?bold(plugin.displayName):plugin.displayName;const meta=dim(`(${plugin.configKey})`);console.log(`${pointer} ${checkbox} ${title} ${meta}`);console.log(` ${plugin.description}`);console.log(dim(` category: ${plugin.category} | platforms: ${plugin.platforms.join(", ")}`));console.log(dim(` id: ${plugin.id}`));console.log("");});console.log("Controls: up/down move, space or enter toggle, a enable all, n disable all, s save, q quit");if(session.notice){console.log("");console.log(session.notice);}}function saveSession(session){if(session.config.WEBVIEW_CONFIG==null){session.config.WEBVIEW_CONFIG={};}session.config.WEBVIEW_CONFIG.plugins=buildPersistedPluginConfig(session);fs.writeFileSync(session.configPath,`${JSON.stringify(session.config,null,session.indent)}\n`);}async function runInteractiveSession(session){if(!process.stdin.isTTY||!process.stdout.isTTY){renderSession(session);console.log("");console.log(yellow("Interactive mode requires a TTY. Re-run this command in a terminal session."));return;}const rl=readline.createInterface({input:process.stdin,output:process.stdout});readline.emitKeypressEvents(process.stdin,rl);if(typeof process.stdin.setRawMode==="function"){process.stdin.setRawMode(true);}try{renderSession(session);await new Promise(resolve=>{const finish=()=>{process.stdin.off("keypress",onKeypress);resolve();};const onKeypress=(_,key={})=>{if(key.ctrl&&key.name==="c"){session.notice=yellow("Exited without saving.");renderSession(session);finish();return;}if(key.name==="up"){session.selectedIndex=session.selectedIndex===0?session.plugins.length-1:session.selectedIndex-1;session.notice="";renderSession(session);return;}if(key.name==="down"){session.selectedIndex=session.selectedIndex===session.plugins.length-1?0:session.selectedIndex+1;session.notice="";renderSession(session);return;}if(key.name==="space"||key.name==="return"){const plugin=session.plugins[session.selectedIndex];plugin.enabled=!plugin.enabled;session.notice=green(`${plugin.displayName} is now ${plugin.enabled?"enabled":"disabled"} in the pending config.`);renderSession(session);return;}if(key.name==="a"){session.plugins.forEach(plugin=>{plugin.enabled=true;});session.notice=green("Enabled all discovered plugins.");renderSession(session);return;}if(key.name==="n"){session.plugins.forEach(plugin=>{plugin.enabled=false;});session.notice=yellow("Disabled all discovered plugins.");renderSession(session);return;}if(key.name==="s"){saveSession(session);session.notice=session.staleToggleKeys.length>0?green(`Saved plugin toggles to config/config.json and removed stale keys: ${session.staleToggleKeys.join(", ")}.`):green("Saved plugin toggles to config/config.json.");renderSession(session);finish();return;}if(key.name==="q"){session.notice=yellow("Exited without saving.");renderSession(session);finish();return;}session.notice=red(`Unsupported key: ${key.name||"unknown"}`);renderSession(session);};process.stdin.on("keypress",onKeypress);});}finally{if(typeof process.stdin.setRawMode==="function"){process.stdin.setRawMode(false);}rl.close();}}async function main(){const{raw:rawConfig,json:config}=readJsonFile(CONFIG_PATH,"App config");const catalystCoreRoot=path.dirname(require.resolve("catalyst-core/package.json"));const pluginsRoot=resolveInternalPluginsRoot(catalystCoreRoot);const plugins=discoverInternalPlugins(pluginsRoot);if(plugins.length===0){console.log(yellow(`No internal plugins were discovered at ${pluginsRoot}`));return;}const session=createSession({configPath:CONFIG_PATH,rawConfig,config,plugins,pluginsRoot});await runInteractiveSession(session);}main().catch(error=>{console.error(red(`Plugin manager failed: ${error.message}`));process.exit(1);});
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+ import readline from "node:readline"
4
+ import { createRequire } from "node:module"
5
+ import { fileURLToPath } from "node:url"
6
+ import { bold, cyan, dim, green, red, yellow } from "picocolors"
7
+
8
+ const require = createRequire(import.meta.url)
9
+ const { discoverInternalPlugins, resolveInternalPluginsRoot } = require("../native/internalPluginUtils.js")
10
+
11
+ const CONFIG_PATH = path.join(process.cwd(), "config", "config.json")
12
+ const TICK = "x"
13
+
14
+ function detectIndent(rawConfig) {
15
+ const indentMatch = rawConfig.match(/\n([ \t]+)"/)
16
+ return indentMatch ? indentMatch[1] : " "
17
+ }
18
+
19
+ function readJsonFile(filePath, label) {
20
+ if (!fs.existsSync(filePath)) {
21
+ throw new Error(`${label} not found at ${filePath}`)
22
+ }
23
+
24
+ const raw = fs.readFileSync(filePath, "utf8")
25
+
26
+ try {
27
+ return {
28
+ raw,
29
+ json: JSON.parse(raw),
30
+ }
31
+ } catch (error) {
32
+ throw new Error(`Invalid JSON in ${filePath}: ${error.message}`)
33
+ }
34
+ }
35
+
36
+ function ensureObject(value, label) {
37
+ if (value == null) {
38
+ return {}
39
+ }
40
+
41
+ if (typeof value !== "object" || Array.isArray(value)) {
42
+ throw new Error(`${label} must be an object`)
43
+ }
44
+
45
+ return value
46
+ }
47
+
48
+ function splitPluginConfig(pluginConfig, plugins) {
49
+ const selectorToPlugin = new Map()
50
+ const matchesByConfigKey = new Map()
51
+ const staleToggleKeys = []
52
+
53
+ plugins.forEach((plugin) => {
54
+ selectorToPlugin.set(plugin.configKey, plugin)
55
+ selectorToPlugin.set(plugin.id, plugin)
56
+ })
57
+
58
+ for (const [key, value] of Object.entries(pluginConfig)) {
59
+ if (typeof value !== "boolean") {
60
+ throw new Error(`WEBVIEW_CONFIG.plugins.${key} must be boolean`)
61
+ }
62
+
63
+ const plugin = selectorToPlugin.get(key)
64
+ if (!plugin) {
65
+ staleToggleKeys.push(key)
66
+ continue
67
+ }
68
+
69
+ const matches = matchesByConfigKey.get(plugin.configKey) || []
70
+ matches.push({ key, value, plugin })
71
+ matchesByConfigKey.set(plugin.configKey, matches)
72
+ }
73
+
74
+ const knownToggles = {}
75
+ for (const plugin of plugins) {
76
+ const matches = matchesByConfigKey.get(plugin.configKey) || []
77
+ const uniqueValues = [...new Set(matches.map((entry) => entry.value))]
78
+ if (uniqueValues.length > 1) {
79
+ throw new Error(
80
+ `Conflicting toggle values for plugin '${plugin.id}' across keys: ${matches
81
+ .map((entry) => `${entry.key}=${entry.value}`)
82
+ .join(", ")}`
83
+ )
84
+ }
85
+ if (matches.length > 0) {
86
+ knownToggles[plugin.configKey] = matches[0].value
87
+ }
88
+ }
89
+
90
+ return { knownToggles, staleToggleKeys }
91
+ }
92
+
93
+ function createSession({ configPath, rawConfig, config, plugins, pluginsRoot }) {
94
+ const webviewConfig =
95
+ config.WEBVIEW_CONFIG == null ? {} : ensureObject(config.WEBVIEW_CONFIG, "WEBVIEW_CONFIG")
96
+ const pluginConfig = ensureObject(webviewConfig.plugins, "WEBVIEW_CONFIG.plugins")
97
+ const { knownToggles, staleToggleKeys } = splitPluginConfig(pluginConfig, plugins)
98
+
99
+ return {
100
+ configPath,
101
+ config,
102
+ indent: detectIndent(rawConfig),
103
+ pluginsRoot,
104
+ plugins: plugins.map((plugin) => ({
105
+ ...plugin,
106
+ enabled: knownToggles[plugin.configKey] ?? false,
107
+ })),
108
+ staleToggleKeys,
109
+ notice: "",
110
+ selectedIndex: 0,
111
+ }
112
+ }
113
+
114
+ function buildPersistedPluginConfig(session) {
115
+ const nextConfig = {}
116
+
117
+ for (const plugin of session.plugins) {
118
+ nextConfig[plugin.configKey] = plugin.enabled
119
+ }
120
+
121
+ return nextConfig
122
+ }
123
+
124
+ function renderSession(session) {
125
+ if (typeof console.clear === "function") {
126
+ console.clear()
127
+ }
128
+
129
+ console.log(bold("Catalyst Internal Plugins"))
130
+ console.log(dim(`Config: ${session.configPath}`))
131
+ console.log(dim(`Catalog: ${session.pluginsRoot}`))
132
+ console.log("")
133
+
134
+ if (session.staleToggleKeys.length > 0) {
135
+ console.log(
136
+ yellow(`Stale plugin toggle keys will be removed on save: ${session.staleToggleKeys.join(", ")}`)
137
+ )
138
+ console.log("")
139
+ }
140
+
141
+ session.plugins.forEach((plugin, index) => {
142
+ const isSelected = index === session.selectedIndex
143
+ const pointer = isSelected ? cyan(">") : " "
144
+ const checkbox = plugin.enabled ? green(`[${TICK}]`) : dim("[ ]")
145
+ const title = isSelected ? bold(plugin.displayName) : plugin.displayName
146
+ const meta = dim(`(${plugin.configKey})`)
147
+
148
+ console.log(`${pointer} ${checkbox} ${title} ${meta}`)
149
+ console.log(` ${plugin.description}`)
150
+ console.log(dim(` category: ${plugin.category} | platforms: ${plugin.platforms.join(", ")}`))
151
+ console.log(dim(` id: ${plugin.id}`))
152
+ console.log("")
153
+ })
154
+
155
+ console.log("Controls: up/down move, space or enter toggle, a enable all, n disable all, s save, q quit")
156
+ if (session.notice) {
157
+ console.log("")
158
+ console.log(session.notice)
159
+ }
160
+ }
161
+
162
+ function saveSession(session) {
163
+ if (session.config.WEBVIEW_CONFIG == null) {
164
+ session.config.WEBVIEW_CONFIG = {}
165
+ }
166
+
167
+ session.config.WEBVIEW_CONFIG.plugins = buildPersistedPluginConfig(session)
168
+ fs.writeFileSync(session.configPath, `${JSON.stringify(session.config, null, session.indent)}\n`)
169
+ }
170
+
171
+ async function runInteractiveSession(session) {
172
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
173
+ renderSession(session)
174
+ console.log("")
175
+ console.log(yellow("Interactive mode requires a TTY. Re-run this command in a terminal session."))
176
+ return
177
+ }
178
+
179
+ const rl = readline.createInterface({
180
+ input: process.stdin,
181
+ output: process.stdout,
182
+ })
183
+
184
+ readline.emitKeypressEvents(process.stdin, rl)
185
+ if (typeof process.stdin.setRawMode === "function") {
186
+ process.stdin.setRawMode(true)
187
+ }
188
+
189
+ try {
190
+ renderSession(session)
191
+
192
+ await new Promise((resolve) => {
193
+ const finish = () => {
194
+ process.stdin.off("keypress", onKeypress)
195
+ resolve()
196
+ }
197
+
198
+ const onKeypress = (_, key = {}) => {
199
+ if (key.ctrl && key.name === "c") {
200
+ session.notice = yellow("Exited without saving.")
201
+ renderSession(session)
202
+ finish()
203
+ return
204
+ }
205
+
206
+ if (key.name === "up") {
207
+ session.selectedIndex =
208
+ session.selectedIndex === 0 ? session.plugins.length - 1 : session.selectedIndex - 1
209
+ session.notice = ""
210
+ renderSession(session)
211
+ return
212
+ }
213
+
214
+ if (key.name === "down") {
215
+ session.selectedIndex =
216
+ session.selectedIndex === session.plugins.length - 1 ? 0 : session.selectedIndex + 1
217
+ session.notice = ""
218
+ renderSession(session)
219
+ return
220
+ }
221
+
222
+ if (key.name === "space" || key.name === "return") {
223
+ const plugin = session.plugins[session.selectedIndex]
224
+ plugin.enabled = !plugin.enabled
225
+ session.notice = green(
226
+ `${plugin.displayName} is now ${plugin.enabled ? "enabled" : "disabled"} in the pending config.`
227
+ )
228
+ renderSession(session)
229
+ return
230
+ }
231
+
232
+ if (key.name === "a") {
233
+ session.plugins.forEach((plugin) => {
234
+ plugin.enabled = true
235
+ })
236
+ session.notice = green("Enabled all discovered plugins.")
237
+ renderSession(session)
238
+ return
239
+ }
240
+
241
+ if (key.name === "n") {
242
+ session.plugins.forEach((plugin) => {
243
+ plugin.enabled = false
244
+ })
245
+ session.notice = yellow("Disabled all discovered plugins.")
246
+ renderSession(session)
247
+ return
248
+ }
249
+
250
+ if (key.name === "s") {
251
+ saveSession(session)
252
+ session.notice =
253
+ session.staleToggleKeys.length > 0
254
+ ? green(
255
+ `Saved plugin toggles to config/config.json and removed stale keys: ${session.staleToggleKeys.join(", ")}.`
256
+ )
257
+ : green("Saved plugin toggles to config/config.json.")
258
+ renderSession(session)
259
+ finish()
260
+ return
261
+ }
262
+
263
+ if (key.name === "q") {
264
+ session.notice = yellow("Exited without saving.")
265
+ renderSession(session)
266
+ finish()
267
+ return
268
+ }
269
+
270
+ session.notice = red(`Unsupported key: ${key.name || "unknown"}`)
271
+ renderSession(session)
272
+ }
273
+
274
+ process.stdin.on("keypress", onKeypress)
275
+ })
276
+ } finally {
277
+ if (typeof process.stdin.setRawMode === "function") {
278
+ process.stdin.setRawMode(false)
279
+ }
280
+ rl.close()
281
+ }
282
+ }
283
+
284
+ async function main() {
285
+ const { raw: rawConfig, json: config } = readJsonFile(CONFIG_PATH, "App config")
286
+ const catalystCoreRoot = fileURLToPath(new URL("../..", import.meta.url))
287
+ const pluginsRoot = resolveInternalPluginsRoot(catalystCoreRoot)
288
+ const plugins = discoverInternalPlugins(pluginsRoot)
289
+
290
+ if (plugins.length === 0) {
291
+ console.log(yellow(`No internal plugins were discovered at ${pluginsRoot}`))
292
+ return
293
+ }
294
+
295
+ const session = createSession({
296
+ configPath: CONFIG_PATH,
297
+ rawConfig,
298
+ config,
299
+ plugins,
300
+ pluginsRoot,
301
+ })
302
+
303
+ await runInteractiveSession(session)
304
+ }
305
+
306
+ main().catch((error) => {
307
+ console.error(red(`Plugin manager failed: ${error.message}`))
308
+ process.exit(1)
309
+ })
@@ -0,0 +1,13 @@
1
+ import path from "path"
2
+ import loadEnvironmentVariables from "./loadEnvironmentVariables.js"
3
+ import { safeCall } from "../server/utils/validator.js"
4
+
5
+ let preServerInit
6
+ try {
7
+ const hooks = await import(path.join(process.env.src_path, "server/index.js"))
8
+ preServerInit = hooks.preServerInit
9
+ } catch {
10
+ // No hooks file — preServerInit remains undefined
11
+ }
12
+ await loadEnvironmentVariables()
13
+ safeCall(preServerInit)
@@ -1,3 +1,54 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.arrayToObject=arrayToObject;exports.runBuildCommands=exports.printBundleInformation=void 0;const fs=require("fs");const path=require("path");const util=require("node:util");const{spawnSync}=require("child_process");const{gray,cyan}=require("picocolors");const{BUILD_OUTPUT_PATH}=require(`${process.cwd()}/config/config.json`);// Function to get file size synchronously
2
- function getFileSizeSync(filePath){try{const stats=fs.statSync(filePath);return stats.size;}catch(err){console.error(`Error getting bundle information for file ${filePath}: ${err}`);return null;}}const printBundleInformation=()=>{let bundleList=[];const directoryPath=path.join(process.cwd(),`${BUILD_OUTPUT_PATH}/public`);try{const files=fs.readdirSync(directoryPath);files.forEach(file=>{if(!file.includes("txt")&&!file.includes("json")){const filePath=`${directoryPath}${path.sep}${file}`;const fileSize=getFileSizeSync(filePath);if(fileSize!==null){bundleList.push({file,fileSize});}}});}catch(err){console.error("Unable to scan build folder: "+err);}bundleList.sort((a,b)=>b.fileSize-a.fileSize);bundleList.forEach(({file,fileSize})=>{const fileName=`${gray(`${BUILD_OUTPUT_PATH}/public/`)}${cyan(file)}`;const fileSizeInKb=(fileSize/1024).toFixed(2);const size=`\t${fileSizeInKb} kB`.padEnd(16);console.log(util.format(size,fileName));});};exports.printBundleInformation=printBundleInformation;function arrayToObject(array){const obj={};array.forEach(item=>{const[key,value]=item.split("=");if(value)obj[key]=value;});return obj;}const BUILD_FAILURE_MESSAGE="\nBuild Failed!";function shouldFailBuild(result){return result.error||result.signal||result.status!==null&&result.status!==0;}function logBuildFailure(result,failureMessage=BUILD_FAILURE_MESSAGE){console.error(failureMessage);if(result.error){console.error(`Error: ${result.error?.message||result.error}\n`);}if(result.signal){console.error(`Signal: ${result.signal}\n`);}if(result.status!==null){console.error(`Exit code: ${result.status}\n`);}}const runBuildCommands=({commands,cwd,env,failureMessage=BUILD_FAILURE_MESSAGE})=>{const command=commands.join(" && ");// nosemgrep
3
- const result=spawnSync(command,[],{cwd,stdio:"inherit",shell:true,env});if(shouldFailBuild(result)){logBuildFailure(result,failureMessage);process.exit(result.status||1);}return result;};exports.runBuildCommands=runBuildCommands;
1
+ import fs from "fs"
2
+ import path from "path"
3
+ import util from "node:util"
4
+ import pkg from "picocolors"
5
+ const { gray, cyan } = pkg
6
+
7
+ // Function to get file size synchronously
8
+ function getFileSizeSync(filePath) {
9
+ try {
10
+ const stats = fs.statSync(filePath)
11
+ return stats.size
12
+ } catch (err) {
13
+ console.error(`Error getting bundle information for file ${filePath}: ${err}`)
14
+ return null
15
+ }
16
+ }
17
+
18
+ export const printBundleInformation = () => {
19
+ let bundleList = []
20
+ const directoryPath = path.join(process.env.src_path, `build/public`)
21
+
22
+ try {
23
+ const files = fs.readdirSync(directoryPath)
24
+ files.forEach((file) => {
25
+ if (!file.includes("txt") && !file.includes("json")) {
26
+ const filePath = path.join(directoryPath, file)
27
+ const fileSize = getFileSizeSync(filePath)
28
+ if (fileSize !== null) {
29
+ bundleList.push({ file, fileSize })
30
+ }
31
+ }
32
+ })
33
+ } catch (err) {
34
+ console.error("Unable to scan build folder: " + err)
35
+ }
36
+
37
+ bundleList.sort((a, b) => b.fileSize - a.fileSize)
38
+ bundleList.forEach(({ file, fileSize }) => {
39
+ const fileName = `${gray(`build/public/`)}${cyan(file)}`
40
+ const fileSizeInKb = (fileSize / 1024).toFixed(2)
41
+ const size = `\t${fileSizeInKb} kB`.padEnd(16)
42
+
43
+ console.log(util.format(size, fileName))
44
+ })
45
+ }
46
+
47
+ export function arrayToObject(array) {
48
+ const obj = {}
49
+ array.forEach((item) => {
50
+ const [key, value] = item.split("=")
51
+ if (value) obj[key] = value
52
+ })
53
+ return obj
54
+ }
@@ -1,4 +1,51 @@
1
- "use strict";const path=require("path");const{spawnSync}=require("child_process");const{arrayToObject}=require("./scriptUtils");const{name}=require(`${process.cwd()}/package.json`);const{BUILD_OUTPUT_PATH}=require(`${process.cwd()}/config/config.json`);/**
2
- * @description - Serves production build of the application.
3
- */function serve(){const commandLineArguments=process.argv.slice(2);const argumentsObject=arrayToObject(commandLineArguments);const dirname=path.resolve(__dirname,"../../");// nosemgrep: javascript.lang.security.audit.spawn-shell-true.spawn-shell-true - Production serve keeps shell execution for existing script compatibility.
4
- spawnSync("node",["-r","./dist/scripts/loadScriptsBeforeServerStarts.js",path.join(process.cwd(),BUILD_OUTPUT_PATH,"startServer.js")],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,NODE_ENV:"production",IS_DEV_COMMAND:false,APPLICATION:name||"catalyst_app",...argumentsObject}});}serve();
1
+ import path from "path"
2
+ import { spawnSync } from "child_process"
3
+ import { arrayToObject } from "./scriptUtils.js"
4
+ import { fileURLToPath } from "url"
5
+ import { dirname } from "path"
6
+ import { readFileSync } from "fs"
7
+
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = dirname(__filename)
10
+ const loaderPath = path.resolve(__dirname, "../../dist/vite/node-loader.mjs")
11
+ const preInitPath = path.resolve(__dirname, "preServerInit.js")
12
+
13
+ /**
14
+ * @description - starts the application in production mode
15
+ */
16
+ function startProd() {
17
+ const commandLineArguments = process.argv.slice(2)
18
+ const argumentsObject = arrayToObject(commandLineArguments)
19
+ const dirname = path.resolve(__dirname, "../../")
20
+
21
+ // Read package.json
22
+ const packageJson = JSON.parse(readFileSync(path.join(process.env.PWD, "package.json"), "utf-8"))
23
+ const { name } = packageJson
24
+
25
+ console.log("🚀 Starting production server...")
26
+
27
+ const inspectFlag = commandLineArguments.includes("--inspect") ? "--inspect" : ""
28
+ const command = `node ${inspectFlag} --import ${preInitPath} --loader ${loaderPath} ./dist/server/expressServer.js`
29
+ spawnSync(command, [], {
30
+ cwd: dirname,
31
+ stdio: "inherit",
32
+ shell: true,
33
+ env: {
34
+ ...process.env,
35
+ src_path: process.env.PWD,
36
+ NODE_ENV: "production",
37
+ IS_DEV_COMMAND: false,
38
+ APPLICATION: name || "catalyst_app",
39
+ ...argumentsObject,
40
+ filterKeys: JSON.stringify([
41
+ "src_path",
42
+ "NODE_ENV",
43
+ "IS_DEV_COMMAND",
44
+ "APPLICATION",
45
+ ...Object.keys(argumentsObject),
46
+ ]),
47
+ },
48
+ })
49
+ }
50
+
51
+ startProd()