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.
- package/babel.native.config.cjs +8 -0
- package/bin/catalyst.js +49 -105
- package/changelog.md +8 -0
- package/dist/caching.js +1 -1
- package/dist/index.jsx +8 -0
- package/dist/logger.js +74 -3
- package/dist/native/androidProject/app/build.gradle.kts +1 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +143 -4
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +26 -6
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt +143 -17
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +6 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +26 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +56 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +48 -8
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfDeliveryState.kt +36 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfEventBuffer.kt +289 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/ProfilerNavigationState.kt +13 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ProfilerLifecycleTest.kt +51 -0
- package/dist/native/androidSetup.js +300 -8
- package/dist/native/bridge/WebBridge.js +481 -59
- package/dist/native/bridge/constants/NativeInterfaces.js +290 -44
- package/dist/native/bridge/errors.js +250 -26
- package/dist/native/bridge/hooks/index.js +125 -1
- package/dist/native/bridge/hooks/useCamera.js +347 -9
- package/dist/native/bridge/hooks/useDataProtection.js +200 -2
- package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
- package/dist/native/bridge/hooks/useFilePicker.js +429 -2
- package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
- package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
- package/dist/native/bridge/hooks/useIntent.js +95 -1
- package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
- package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
- package/dist/native/bridge/hooks/useNotification.js +318 -2
- package/dist/native/bridge/hooks/useSafeArea.js +84 -2
- package/dist/native/bridge/hooks/useVideoStream.js +434 -6
- package/dist/native/bridge/hooks/utils.js +27 -1
- package/dist/native/bridge/hooks.js +16 -1
- package/dist/native/bridge/perf/collectors/ApiCollector.js +85 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.js +215 -0
- package/dist/native/bridge/perf/collectors/HookCollector.js +165 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.js +505 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.js +273 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.js +124 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.js +294 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +183 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.js +284 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.js +111 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.js +111 -0
- package/dist/native/bridge/perf/core/constants.js +126 -0
- package/dist/native/bridge/perf/core/measure.js +189 -0
- package/dist/native/bridge/perf/core/session.js +100 -0
- package/dist/native/bridge/perf/core/store.js +554 -0
- package/dist/native/bridge/perf/core/summary.js +174 -0
- package/dist/native/bridge/perf/core/traceExport.js +240 -0
- package/dist/native/bridge/perf/core/waterfalls.js +146 -0
- package/dist/native/bridge/perf/index.js +402 -0
- package/dist/native/bridge/safeArea.js +53 -5
- package/dist/native/bridge/standardizedHooks.js +595 -82
- package/dist/native/bridge/useBaseHook.js +274 -38
- package/dist/native/bridge/utils/CameraUtils.js +201 -56
- package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
- package/dist/native/bridge/utils/NativeBridge.js +425 -141
- package/dist/native/buildAndroid/assets.js +510 -13
- package/dist/native/buildAndroid/build.js +296 -19
- package/dist/native/buildAndroid/config.js +87 -1
- package/dist/native/buildAndroid/index.js +374 -1
- package/dist/native/buildAppAndroid.js +34 -2
- package/dist/native/buildAppIos.js +62 -2
- package/dist/native/buildIos/assets.js +609 -8
- package/dist/native/buildIos/build.js +639 -20
- package/dist/native/buildIos/config.js +294 -12
- package/dist/native/buildIos/index.js +343 -6
- package/dist/native/buildIos/plugins.js +343 -16
- package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
- package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
- package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
- package/dist/native/internalPluginUtils.js +232 -7
- package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +19 -0
- package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +4 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/BootTimingUtility.swift +5 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +10 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DisplayLinkPerfMonitor.swift +89 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +373 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +35 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +24 -11
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +9 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +126 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +75 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +13 -0
- package/dist/native/package.json +3 -0
- package/dist/native/plugin-bridge/PluginBridge.js +227 -1
- package/dist/native/pluginComposerAndroid.js +407 -23
- package/dist/native/pluginComposerIos.js +435 -11
- package/dist/native/renameAndroidProject.js +1094 -88
- package/dist/native/setupEmulatorIos.js +289 -22
- package/dist/native/setupServer.js +160 -19
- package/dist/native/terminalProgress.js +199 -11
- package/dist/native/utils.js +193 -15
- package/dist/offline/catalyst-sw.js +145 -1
- package/dist/offline/registerServiceWorker.js +33 -1
- package/dist/otel.js +375 -46
- package/dist/router/ClientRouter.js +14 -2
- package/dist/router/ServerRouter.js +9 -1
- package/dist/router.js +1 -1
- package/dist/scripts/build.js +112 -3
- package/dist/scripts/checkVersion.js +3 -1
- package/dist/scripts/cleanCache.js +30 -1
- package/dist/scripts/generateOfflineManifest.js +116 -1
- package/dist/scripts/loadEnvironmentVariables.js +32 -6
- package/dist/scripts/plugins.js +309 -1
- package/dist/scripts/preServerInit.js +13 -0
- package/dist/scripts/scriptUtils.js +54 -3
- package/dist/scripts/serve.js +51 -4
- package/dist/scripts/start.js +47 -10
- package/dist/scripts/validator.js +149 -1
- package/dist/sentry.cjs +127 -0
- package/dist/sentry.js +125 -5
- package/dist/server/expressServer.js +250 -5
- package/dist/server/manifestCache.js +36 -0
- package/dist/server/renderer/ChunkExtractor.js +144 -0
- package/dist/server/renderer/document/Body.jsx +59 -0
- package/dist/server/renderer/document/FastRefresh.jsx +20 -0
- package/dist/server/renderer/document/Head.jsx +60 -0
- package/dist/server/renderer/document/index.jsx +2 -0
- package/dist/server/renderer/extract.js +149 -31
- package/dist/server/renderer/handler.jsx +433 -0
- package/dist/server/renderer/index.js +8 -1
- package/dist/server/renderer/render.js +47 -0
- package/dist/server/utils/botDetectionMiddleware.js +29 -0
- package/dist/server/utils/cjsRequire.js +10 -0
- package/dist/server/utils/userAgentUtil.js +87 -9
- package/dist/server/utils/validator.js +164 -1
- package/dist/vite/FastRefresh.jsx +20 -0
- package/dist/vite/inject-cache-key-plugin.js +276 -0
- package/dist/vite/loadCustomViteConfig.js +22 -0
- package/dist/vite/manifest-categorization-plugin.js +455 -0
- package/dist/vite/node-loader.mjs +458 -0
- package/dist/vite/scssParams.js +21 -0
- package/dist/vite/vite.config.client.js +130 -0
- package/dist/vite/vite.config.js +422 -0
- package/dist/vite/vite.config.server.js +84 -0
- package/dist/web-router/components/MetaTag.jsx +34 -0
- package/dist/web-router/components/RouterDataProvider.jsx +332 -0
- package/dist/web-router/components/Split.jsx +200 -0
- package/dist/web-router/components/SplitInview.jsx +106 -0
- package/dist/web-router/components/SsrRequestContext.jsx +14 -0
- package/dist/web-router/context.jsx +12 -0
- package/dist/web-router/hooks.jsx +72 -0
- package/dist/web-router/types/components/MetaTag.d.ts +2 -0
- package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
- package/dist/web-router/types/context.d.ts +14 -0
- package/dist/web-router/types/hooks.d.ts +1 -0
- package/dist/web-router/types/index.d.ts +3 -0
- package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
- package/dist/web-router/utils/metaDataUtils.jsx +119 -0
- package/mcp_v2/conversion-tasks.json +35 -17
- package/mcp_v2/knowledge-base.json +124 -13
- package/mcp_v2/lib/helpers.js +10 -0
- package/mcp_v2/mcp.js +4 -4
- package/mcp_v2/package.json +1 -0
- package/mcp_v2/tools/build.js +116 -20
- package/mcp_v2/tools/conversion.js +106 -5
- package/mcp_v2/tools/tasks.js +18 -14
- package/package.json +35 -68
- package/babel.config.js +0 -32
- package/dist/index.js +0 -1
- package/dist/scripts/devBuild.js +0 -3
- package/dist/scripts/devServe.js +0 -3
- package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
- package/dist/scripts/registerAliases.js +0 -6
- package/dist/server/renderer/document/Body.js +0 -11
- package/dist/server/renderer/document/Head.js +0 -9
- package/dist/server/renderer/document/index.js +0 -1
- package/dist/server/renderer/handler.js +0 -47
- package/dist/server/startServer.js +0 -57
- package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
- package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
- package/dist/webpack/babel.config.client.js +0 -1
- package/dist/webpack/babel.config.ssr.js +0 -1
- package/dist/webpack/base.babel.js +0 -7
- package/dist/webpack/development.client.babel.js +0 -10
- package/dist/webpack/production.client.babel.js +0 -4
- package/dist/webpack/production.ssr.babel.js +0 -8
- package/dist/webpack/scssParams.js +0 -9
|
@@ -1 +1,116 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
package/dist/scripts/plugins.js
CHANGED
|
@@ -1 +1,309 @@
|
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
+
}
|
package/dist/scripts/serve.js
CHANGED
|
@@ -1,4 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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()
|