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,5 +1,250 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import path from "path"
|
|
2
|
+
import express from "express"
|
|
3
|
+
import bodyParser from "body-parser"
|
|
4
|
+
import compression from "compression"
|
|
5
|
+
import cookieParser from "cookie-parser"
|
|
6
|
+
import expressStaticGzip from "express-static-gzip"
|
|
7
|
+
import { createServer as createViteServer } from "vite"
|
|
8
|
+
import util from "node:util"
|
|
9
|
+
import pc from "picocolors"
|
|
10
|
+
import fs from "fs"
|
|
11
|
+
const { cyan, yellow, green } = pc
|
|
12
|
+
|
|
13
|
+
import { validateMiddleware, safeCall } from "./utils/validator.js"
|
|
14
|
+
import { botDetectionMiddleware } from "./utils/botDetectionMiddleware.js"
|
|
15
|
+
const { addMiddlewares } = await import(path.join(process.env.src_path, "server/server.js"))
|
|
16
|
+
|
|
17
|
+
// OpenTelemetry is opt-in (OTEL_ENABLE) — mirrors server/renderer/handler.jsx.
|
|
18
|
+
// Passthrough no-op middleware when disabled or packages aren't installed.
|
|
19
|
+
let responseFlushMiddleware = () => (_req, _res, next) => next()
|
|
20
|
+
if (process.env.OTEL_ENABLE === true) {
|
|
21
|
+
try {
|
|
22
|
+
const otel = await import("../otel.js")
|
|
23
|
+
responseFlushMiddleware = otel.responseFlushMiddleware
|
|
24
|
+
} catch {
|
|
25
|
+
// otel packages not installed — continue without the flush span
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const SSR_SERVICE = process.env.SERVICE_NAME || `pwa-${process.env.APPLICATION}-node-server`
|
|
30
|
+
|
|
31
|
+
// ─── Load app-defined server lifecycle hooks ──────────────────────────────────
|
|
32
|
+
let onServerError
|
|
33
|
+
try {
|
|
34
|
+
const hooks = await import(path.join(process.env.src_path, "server/index.js"))
|
|
35
|
+
onServerError = hooks.onServerError
|
|
36
|
+
} catch {
|
|
37
|
+
// No hooks file — onServerError remains undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ─── Process-level error handlers ─────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
function safeStringify(err) {
|
|
43
|
+
try {
|
|
44
|
+
return JSON.stringify(err)
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.log("error in safeStringify", e)
|
|
47
|
+
return err
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
process.on("uncaughtException", (err, origin) => {
|
|
52
|
+
console.log(process.stderr.fd)
|
|
53
|
+
console.log(`Caught exception: ${err}\n` + `Exception origin: ${origin}`)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
process.on("uncaughtExceptionMonitor", (err, origin) => {
|
|
57
|
+
console.log(err, origin)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
process.on("unhandledRejection", (err) => console.log("unhandledRejection in Catalyst", safeStringify(err)))
|
|
61
|
+
|
|
62
|
+
process.on("SIGINT", function () {
|
|
63
|
+
console.log("SIGINT")
|
|
64
|
+
process.exit(0)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
process.on("message", function (msg) {
|
|
68
|
+
if (msg == "shutdown") {
|
|
69
|
+
console.log("Closing all connections...")
|
|
70
|
+
setTimeout(function () {
|
|
71
|
+
console.log("Finished closing connections")
|
|
72
|
+
process.exit(0)
|
|
73
|
+
}, 1500)
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
import { fileURLToPath } from "url"
|
|
78
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
79
|
+
const __dirname = path.dirname(__filename)
|
|
80
|
+
|
|
81
|
+
const isProduction = process.env.NODE_ENV === "production"
|
|
82
|
+
|
|
83
|
+
function serveBuildFile(app, buildPath, urlPath, fileName, headers = {}) {
|
|
84
|
+
app.get(urlPath, (_req, res, next) => {
|
|
85
|
+
const filePath = path.join(buildPath, fileName)
|
|
86
|
+
if (!fs.existsSync(filePath)) return next()
|
|
87
|
+
res.set(headers)
|
|
88
|
+
res.sendFile(filePath)
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function createServer() {
|
|
93
|
+
const port = process.env.NODE_SERVER_PORT ?? 3005
|
|
94
|
+
const host = process.env.NODE_SERVER_HOSTNAME ?? "localhost"
|
|
95
|
+
const app = express()
|
|
96
|
+
|
|
97
|
+
// This middleware is being used to extract the body of the request
|
|
98
|
+
app.use(bodyParser.json())
|
|
99
|
+
|
|
100
|
+
// // This middleware has been added to accommodate "byetstream array"
|
|
101
|
+
app.use(bodyParser.raw({ type: "application/*" }))
|
|
102
|
+
|
|
103
|
+
// This middleware is being used to parse cookies!
|
|
104
|
+
app.use(cookieParser())
|
|
105
|
+
|
|
106
|
+
// All the middlewares defined by the user will run here.
|
|
107
|
+
if (validateMiddleware(addMiddlewares)) addMiddlewares(app)
|
|
108
|
+
|
|
109
|
+
// response.compress + response.flush spans straddle compression — they
|
|
110
|
+
// attribute the time past the `handler` span (gzip/brotli, then egress).
|
|
111
|
+
// MUST be mounted immediately before compression() so its outer res.end
|
|
112
|
+
// hook reliably wraps compression's patch (no-op when OTEL off).
|
|
113
|
+
app.use(responseFlushMiddleware(SSR_SERVICE, "response.flush", "response.compress"))
|
|
114
|
+
|
|
115
|
+
// The middleware will attempt to compress response bodies for all request that traverse through the middleware
|
|
116
|
+
app.use(compression())
|
|
117
|
+
|
|
118
|
+
let vite
|
|
119
|
+
|
|
120
|
+
if (isProduction) {
|
|
121
|
+
// In production, serve built assets. Build manifests themselves are
|
|
122
|
+
// loaded once at startup by ./manifestCache.js — handler reads from
|
|
123
|
+
// that singleton instead of attaching them to every `req`.
|
|
124
|
+
const buildPath = path.join(process.env.src_path, process.env.BUILD_OUTPUT_PATH || "build")
|
|
125
|
+
const publicPath = path.join(buildPath, "client", "assets")
|
|
126
|
+
const publicAssetBase = `/${(process.env.PUBLIC_STATIC_ASSET_PATH || "/assets/")
|
|
127
|
+
.replace(/^\/+|\/+$/g, "")
|
|
128
|
+
.replace(/\/+/g, "/")}/client/assets`
|
|
129
|
+
|
|
130
|
+
serveBuildFile(app, buildPath, "/catalyst-offline-manifest.json", "catalyst-offline-manifest.json", {
|
|
131
|
+
"cache-control": "no-store",
|
|
132
|
+
})
|
|
133
|
+
serveBuildFile(app, buildPath, "/catalyst-sw.js", "catalyst-sw.js", {
|
|
134
|
+
"cache-control": "no-cache",
|
|
135
|
+
"service-worker-allowed": "/",
|
|
136
|
+
})
|
|
137
|
+
serveBuildFile(app, buildPath, "/offline.html", "offline.html", {
|
|
138
|
+
"cache-control": "no-cache",
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Serve static assets — prefers pre-compressed .br / .gz files generated at build time
|
|
142
|
+
app.use(
|
|
143
|
+
publicAssetBase,
|
|
144
|
+
expressStaticGzip(publicPath, {
|
|
145
|
+
enableBrotli: true,
|
|
146
|
+
orderPreference: ["br", "gz"],
|
|
147
|
+
serveStatic: {
|
|
148
|
+
maxAge: "1y",
|
|
149
|
+
etag: true,
|
|
150
|
+
lastModified: true,
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
)
|
|
154
|
+
} else {
|
|
155
|
+
// In development, use Vite middleware
|
|
156
|
+
vite = await createViteServer({
|
|
157
|
+
configFile: "./dist/vite/vite.config.js",
|
|
158
|
+
server: {
|
|
159
|
+
middlewareMode: true,
|
|
160
|
+
},
|
|
161
|
+
appType: "custom",
|
|
162
|
+
root: process.env.src_path,
|
|
163
|
+
})
|
|
164
|
+
app.use(vite.middlewares)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// In production, resolve the render module once at startup so the ESM
|
|
168
|
+
// loader and lazy-chunk cache are not re-entered on every request.
|
|
169
|
+
let productionRender
|
|
170
|
+
if (isProduction) {
|
|
171
|
+
const serverPath = path.join(
|
|
172
|
+
process.env.src_path,
|
|
173
|
+
process.env.BUILD_OUTPUT_PATH || "build",
|
|
174
|
+
"server",
|
|
175
|
+
"server.js"
|
|
176
|
+
)
|
|
177
|
+
if (fs.existsSync(serverPath)) {
|
|
178
|
+
productionRender = await import(serverPath)
|
|
179
|
+
} else {
|
|
180
|
+
const rendererPath = path.join(
|
|
181
|
+
process.env.src_path,
|
|
182
|
+
process.env.BUILD_OUTPUT_PATH || "build",
|
|
183
|
+
"server",
|
|
184
|
+
"index.js"
|
|
185
|
+
)
|
|
186
|
+
productionRender = await import(rendererPath)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
app.use(botDetectionMiddleware)
|
|
191
|
+
|
|
192
|
+
app.use("*", async (req, res, next) => {
|
|
193
|
+
try {
|
|
194
|
+
let render
|
|
195
|
+
|
|
196
|
+
if (isProduction) {
|
|
197
|
+
render = productionRender
|
|
198
|
+
} else {
|
|
199
|
+
// In development, load through Vite SSR per-request to support HMR
|
|
200
|
+
const rendererPath = path.join(__dirname, "./renderer/index.js")
|
|
201
|
+
render = await vite.ssrLoadModule(rendererPath)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Render your app
|
|
205
|
+
if (render && render.default) {
|
|
206
|
+
await render.default(req, res, next)
|
|
207
|
+
} else {
|
|
208
|
+
console.error("Renderer not found or invalid")
|
|
209
|
+
res.status(500).send("Error loading renderer")
|
|
210
|
+
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
console.error("SSR Error:", err)
|
|
213
|
+
if (vite) {
|
|
214
|
+
vite.ssrFixStacktrace(err)
|
|
215
|
+
}
|
|
216
|
+
res.status(500).send("Internal Server Error")
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
app.listen({ port, host }, (error) => {
|
|
221
|
+
const { APPLICATION, NODE_SERVER_HOSTNAME, NODE_SERVER_PORT } = process.env
|
|
222
|
+
|
|
223
|
+
if (error) {
|
|
224
|
+
console.log("An error occured while starting the Application server : ", error)
|
|
225
|
+
safeCall(onServerError)
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (process.env.NODE_ENV === "development") console.log(green("Compiled successfully!"))
|
|
230
|
+
|
|
231
|
+
console.log(`\nYou can now view ${APPLICATION} in the browser.\n`)
|
|
232
|
+
console.log(
|
|
233
|
+
util.format("\tLocal:".padEnd(8), cyan(`http://${NODE_SERVER_HOSTNAME}:${NODE_SERVER_PORT}`))
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if (process.env.NODE_ENV === "development") {
|
|
237
|
+
console.log("\nNote that the development build is not optimized.")
|
|
238
|
+
console.log("To create a production build, use " + cyan("npm run build"))
|
|
239
|
+
} else {
|
|
240
|
+
console.log(
|
|
241
|
+
green(`\nProduction server running in ${isProduction ? "production" : "development"} mode`)
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log("\nFind out more about deployment here:")
|
|
246
|
+
console.log(yellow("\n https://catalyst.1mg.com/public_docs/content/deployment\n"))
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
createServer()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from "fs"
|
|
2
|
+
import path from "path"
|
|
3
|
+
|
|
4
|
+
let manifest = null
|
|
5
|
+
let assetManifest = null
|
|
6
|
+
let loaded = false
|
|
7
|
+
|
|
8
|
+
const isProduction = process.env.NODE_ENV === "production"
|
|
9
|
+
|
|
10
|
+
function loadManifests() {
|
|
11
|
+
if (loaded) return
|
|
12
|
+
|
|
13
|
+
if (!isProduction) return
|
|
14
|
+
|
|
15
|
+
const buildPath = path.join(process.env.src_path, process.env.BUILD_OUTPUT_PATH || "build")
|
|
16
|
+
try {
|
|
17
|
+
const manifestPath = path.join(buildPath, ".vite", "manifest.json")
|
|
18
|
+
const assetManifestPath = path.join(buildPath, ".vite", "asset-categories.json")
|
|
19
|
+
|
|
20
|
+
if (fs.existsSync(manifestPath)) {
|
|
21
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"))
|
|
22
|
+
}
|
|
23
|
+
if (fs.existsSync(assetManifestPath)) {
|
|
24
|
+
assetManifest = JSON.parse(fs.readFileSync(assetManifestPath, "utf-8"))
|
|
25
|
+
}
|
|
26
|
+
loaded = true
|
|
27
|
+
} catch (error) {
|
|
28
|
+
loaded = false
|
|
29
|
+
console.warn("Could not load build manifests:", error.message)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
loadManifests()
|
|
34
|
+
|
|
35
|
+
export const getManifest = () => manifest
|
|
36
|
+
export const getAssetManifest = () => assetManifest
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChunkExtractor — collects JS and CSS assets needed for the current SSR render.
|
|
3
|
+
*
|
|
4
|
+
* Two buckets:
|
|
5
|
+
* critical — loaded in <head> (inline CSS via <style>, entry + route JS)
|
|
6
|
+
* deferred — injected after </body> via onAllReady (external <link> CSS)
|
|
7
|
+
*
|
|
8
|
+
* Critical CSS is inlined as <style> to avoid FOUC/CLS.
|
|
9
|
+
* With natural Vite code-splitting (no mega "main" chunk), critical CSS stays small (~15-25KB).
|
|
10
|
+
*/
|
|
11
|
+
export class ChunkExtractor {
|
|
12
|
+
constructor({ manifest = {}, assetManifest = {} } = {}) {
|
|
13
|
+
this.manifest = manifest
|
|
14
|
+
this.assetManifest = assetManifest
|
|
15
|
+
this.components = new Set()
|
|
16
|
+
|
|
17
|
+
const baseUrl = `${process.env.PUBLIC_STATIC_ASSET_URL || ""}${process.env.PUBLIC_STATIC_ASSET_PATH || ""}`
|
|
18
|
+
this.publicPath = `${baseUrl.replace(/\/+$/, "")}/`
|
|
19
|
+
|
|
20
|
+
// JS tracked as full URLs, CSS tracked as relative file paths (for disk reading)
|
|
21
|
+
this.critical = { js: new Set(), css: new Set() }
|
|
22
|
+
this.deferred = { js: new Set(), css: new Set() }
|
|
23
|
+
this._allCssPaths = new Set() // dedup across buckets
|
|
24
|
+
|
|
25
|
+
this._loadEssentialEntrypoints()
|
|
26
|
+
|
|
27
|
+
if (typeof global !== "undefined") {
|
|
28
|
+
global.__CHUNK_EXTRACTOR__ = this
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Build-time essential chunks (entry + static deps) ──────────────
|
|
33
|
+
_loadEssentialEntrypoints() {
|
|
34
|
+
for (const [, entry] of Object.entries(this.assetManifest.essential || {})) {
|
|
35
|
+
this._addAssets(entry, this.critical)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ── Route-matched split chunks → critical (blocks first paint) ─────
|
|
40
|
+
preloadRouteCss(allMatches = []) {
|
|
41
|
+
const list = allMatches == null ? [] : Array.isArray(allMatches) ? allMatches : []
|
|
42
|
+
for (const match of list) {
|
|
43
|
+
const route = match?.route
|
|
44
|
+
if (!route) continue
|
|
45
|
+
|
|
46
|
+
const component = route.Component || route.component
|
|
47
|
+
const cacheKey = component?.__cacheKey
|
|
48
|
+
if (!cacheKey) continue
|
|
49
|
+
|
|
50
|
+
const entry =
|
|
51
|
+
this.assetManifest.ssrTrue?.[cacheKey] ||
|
|
52
|
+
this.assetManifest.ssrFalse?.[cacheKey] ||
|
|
53
|
+
this.manifest[cacheKey]
|
|
54
|
+
|
|
55
|
+
if (entry) {
|
|
56
|
+
this._addAssets(entry, this.critical)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Components discovered during render → deferred ─────────────────
|
|
62
|
+
addComponent(cacheKey) {
|
|
63
|
+
this.components.add(cacheKey)
|
|
64
|
+
|
|
65
|
+
// Try assetManifest first by raw cacheKey — addSourcePathAliases writes
|
|
66
|
+
// source-path entries here even when the chunk is anonymous in manifest.json
|
|
67
|
+
// (shared / multi-importer dynamic chunks). Falling back to manifest.json
|
|
68
|
+
// last keeps the existing prefix-match behavior intact.
|
|
69
|
+
let entry =
|
|
70
|
+
this.assetManifest.ssrTrue?.[cacheKey] ||
|
|
71
|
+
this.assetManifest.ssrFalse?.[cacheKey] ||
|
|
72
|
+
this.manifest[cacheKey]
|
|
73
|
+
|
|
74
|
+
if (!entry) {
|
|
75
|
+
const resolvedKey = Object.keys(this.manifest).find((k) => k.startsWith(cacheKey + "."))
|
|
76
|
+
if (resolvedKey) {
|
|
77
|
+
entry =
|
|
78
|
+
this.assetManifest.ssrTrue?.[resolvedKey] ||
|
|
79
|
+
this.assetManifest.ssrFalse?.[resolvedKey] ||
|
|
80
|
+
this.manifest[resolvedKey]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (entry) {
|
|
85
|
+
this._addAssets(entry, this.deferred)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Internal: add JS URLs + CSS file paths to a bucket ─────────────
|
|
90
|
+
_addAssets(manifestEntry, bucket) {
|
|
91
|
+
if (!manifestEntry?.file) return
|
|
92
|
+
|
|
93
|
+
const jsUrl = this._toUrl(manifestEntry.file)
|
|
94
|
+
|
|
95
|
+
// Skip if already tracked in either bucket
|
|
96
|
+
if (this.critical.js.has(jsUrl) || this.deferred.js.has(jsUrl)) return
|
|
97
|
+
|
|
98
|
+
bucket.js.add(jsUrl)
|
|
99
|
+
|
|
100
|
+
// Collect direct + transitive CSS as relative file paths (not URLs)
|
|
101
|
+
const cssPart = Array.isArray(manifestEntry.css) ? manifestEntry.css : []
|
|
102
|
+
const allCssPart = Array.isArray(manifestEntry.allCss) ? manifestEntry.allCss : []
|
|
103
|
+
const cssFiles = [...cssPart, ...allCssPart]
|
|
104
|
+
for (const cssFile of cssFiles) {
|
|
105
|
+
if (!this._allCssPaths.has(cssFile)) {
|
|
106
|
+
this._allCssPaths.add(cssFile)
|
|
107
|
+
bucket.css.add(cssFile)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_toUrl(filePath) {
|
|
113
|
+
const cleaned = filePath.replace(/^\/+/, "")
|
|
114
|
+
return `${this.publicPath}${cleaned}`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_toCssUrl(filePath) {
|
|
118
|
+
const cleaned = filePath.replace(/^\/+/, "")
|
|
119
|
+
return `${this.publicPath}${cleaned}`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Public getters ─────────────────────────────────────────────────
|
|
123
|
+
/** Critical: CSS as relative file paths (for inlining from disk), JS as URLs */
|
|
124
|
+
getCriticalAssets() {
|
|
125
|
+
return {
|
|
126
|
+
js: Array.from(this.critical.js),
|
|
127
|
+
css: Array.from(this.critical.css),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Deferred: CSS as URLs (external <link>), JS as URLs */
|
|
132
|
+
getDeferredAssets() {
|
|
133
|
+
return {
|
|
134
|
+
js: Array.from(this.deferred.js),
|
|
135
|
+
css: Array.from(this.deferred.css),
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
getRenderedComponentKeys() {
|
|
140
|
+
return Array.from(this.components)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export default ChunkExtractor
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import PropTypes from "prop-types"
|
|
3
|
+
|
|
4
|
+
const DEFAULT_SAFE_AREA = { top: 0, right: 0, bottom: 0, left: 0 }
|
|
5
|
+
/**
|
|
6
|
+
* Body component which will be used in page component
|
|
7
|
+
* @param {object} jsx - page jsx code
|
|
8
|
+
* @param {object} statusCode - document request status code
|
|
9
|
+
* @param {object} initialState - initial state object for redux store
|
|
10
|
+
* @param {object} fetcherData - contains data from executing serverFetcher function
|
|
11
|
+
* @param {object} children - contains any child elements defined within the component
|
|
12
|
+
*/
|
|
13
|
+
export function Body(props) {
|
|
14
|
+
const {
|
|
15
|
+
jsx = "",
|
|
16
|
+
statusCode = "",
|
|
17
|
+
initialState = {},
|
|
18
|
+
|
|
19
|
+
fetcherData = {},
|
|
20
|
+
children,
|
|
21
|
+
safeArea = DEFAULT_SAFE_AREA,
|
|
22
|
+
nativeWebView = false,
|
|
23
|
+
} = props
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<body>
|
|
27
|
+
<script
|
|
28
|
+
/* eslint-disable-next-line risxss/catch-potential-xss-react */
|
|
29
|
+
dangerouslySetInnerHTML={{
|
|
30
|
+
__html: `window.__SAFE_AREA_INITIAL__ = ${JSON.stringify(safeArea)}; window.__CATALYST_NATIVE_WEBVIEW__ = ${nativeWebView ? "true" : "false"}`,
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
{process.env.NODE_ENV === "development" && <script type="module" src="/client/index.js"></script>}
|
|
34
|
+
{jsx}
|
|
35
|
+
<script
|
|
36
|
+
/* eslint-disable */
|
|
37
|
+
dangerouslySetInnerHTML={{
|
|
38
|
+
__html: `
|
|
39
|
+
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}
|
|
40
|
+
window.__STATUS_CODE__ = ${JSON.stringify(statusCode || null)}
|
|
41
|
+
window.__ROUTER_INITIAL_DATA__ = ${JSON.stringify(fetcherData)}
|
|
42
|
+
`,
|
|
43
|
+
}}
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
{children}
|
|
47
|
+
</body>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Body.propTypes = {
|
|
52
|
+
initialState: PropTypes.object,
|
|
53
|
+
jsx: PropTypes.any,
|
|
54
|
+
statusCode: PropTypes.string,
|
|
55
|
+
fetcherData: PropTypes.object,
|
|
56
|
+
children: PropTypes.node,
|
|
57
|
+
safeArea: PropTypes.object,
|
|
58
|
+
nativeWebView: PropTypes.bool,
|
|
59
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
/* eslint-disable risxss/catch-potential-xss-react */
|
|
4
|
+
const FastRefresh = () => {
|
|
5
|
+
return (
|
|
6
|
+
<script
|
|
7
|
+
type="module"
|
|
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,60 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import PropTypes from "prop-types"
|
|
3
|
+
import FastRefresh from "../../../vite/FastRefresh.jsx"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Head component — inlines critical CSS as <style> and renders JS as <script type="module">.
|
|
7
|
+
* Critical CSS is small (~15-25KB) thanks to natural Vite code-splitting.
|
|
8
|
+
* Route-cached deferred CSS is inlined here on repeat visits; new deferred CSS still appends after body.
|
|
9
|
+
*/
|
|
10
|
+
export function Head(props) {
|
|
11
|
+
const {
|
|
12
|
+
inlineCss,
|
|
13
|
+
deferredRouteInlineCss,
|
|
14
|
+
jsScripts,
|
|
15
|
+
criticalPreloadLinks,
|
|
16
|
+
deferredPreloadLinks,
|
|
17
|
+
metaTags,
|
|
18
|
+
isBot,
|
|
19
|
+
publicAssetPath,
|
|
20
|
+
children,
|
|
21
|
+
} = props
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<head>
|
|
25
|
+
<meta charSet="utf-8" />
|
|
26
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
27
|
+
{process.env.NODE_ENV === "development" && <FastRefresh />}
|
|
28
|
+
|
|
29
|
+
{publicAssetPath && <link rel="preconnect" href={publicAssetPath} />}
|
|
30
|
+
{publicAssetPath && <link rel="dns-prefetch" href={publicAssetPath} />}
|
|
31
|
+
{!isBot && criticalPreloadLinks}
|
|
32
|
+
{/* Warm-cache modulepreloads from prior SSRs (URLs not in critical scripts) */}
|
|
33
|
+
{!isBot && deferredPreloadLinks}
|
|
34
|
+
{metaTags && metaTags}
|
|
35
|
+
|
|
36
|
+
{/* Inline critical CSS — prevents FOUC/CLS */}
|
|
37
|
+
{inlineCss && <style dangerouslySetInnerHTML={{ __html: inlineCss }} />}
|
|
38
|
+
|
|
39
|
+
{/* Deferred CSS from prior SSRs on this route — avoids late layout from post-body <style> */}
|
|
40
|
+
{deferredRouteInlineCss && <style dangerouslySetInnerHTML={{ __html: deferredRouteInlineCss }} />}
|
|
41
|
+
|
|
42
|
+
{/* JS modules */}
|
|
43
|
+
{!isBot && jsScripts}
|
|
44
|
+
|
|
45
|
+
{children}
|
|
46
|
+
</head>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Head.propTypes = {
|
|
51
|
+
isBot: PropTypes.bool,
|
|
52
|
+
inlineCss: PropTypes.string,
|
|
53
|
+
deferredRouteInlineCss: PropTypes.string,
|
|
54
|
+
jsScripts: PropTypes.array,
|
|
55
|
+
criticalPreloadLinks: PropTypes.array,
|
|
56
|
+
deferredPreloadLinks: PropTypes.array,
|
|
57
|
+
metaTags: PropTypes.array,
|
|
58
|
+
publicAssetPath: PropTypes.string,
|
|
59
|
+
children: PropTypes.node,
|
|
60
|
+
}
|