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
package/dist/otel.js
CHANGED
|
@@ -1,72 +1,401 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if
|
|
25
|
-
|
|
1
|
+
import { metrics } from "@opentelemetry/api"
|
|
2
|
+
import { NodeSDK } from "@opentelemetry/sdk-node"
|
|
3
|
+
import { resourceFromAttributes } from "@opentelemetry/resources"
|
|
4
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-node"
|
|
5
|
+
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"
|
|
6
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc"
|
|
7
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc"
|
|
8
|
+
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"
|
|
9
|
+
import { TraceIdRatioBasedSampler, ParentBasedSampler } from "@opentelemetry/sdk-trace-node"
|
|
10
|
+
import { OTLPTraceExporter as OTLPTraceExporterHTTP } from "@opentelemetry/exporter-trace-otlp-http"
|
|
11
|
+
import { OTLPMetricExporter as OTLPMetricExporterHTTP } from "@opentelemetry/exporter-metrics-otlp-http"
|
|
12
|
+
import { trace, context, createContextKey } from "@opentelemetry/api"
|
|
13
|
+
|
|
14
|
+
export const IS_BOT_KEY = createContextKey("catalyst.is_bot")
|
|
15
|
+
|
|
16
|
+
import semanticConventions from "@opentelemetry/semantic-conventions"
|
|
17
|
+
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, ATTR_DEPLOYMENT_ENVIRONMENT } = semanticConventions
|
|
18
|
+
|
|
19
|
+
function init(config = {}) {
|
|
20
|
+
// OpenTelemetry is opt-in โ bail out unless explicitly enabled. Mirrors the
|
|
21
|
+
// OTEL_ENABLE guard in server/expressServer.js and server/renderer/handler.jsx
|
|
22
|
+
// so the SDK, exporters, instrumentations and signal handlers are never set
|
|
23
|
+
// up when tracing is off. Returns the same shape so callers can destructure.
|
|
24
|
+
if (process.env.OTEL_ENABLE !== true) {
|
|
25
|
+
return { sdk: null, meter: null }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
serviceName = "catalyst-server",
|
|
30
|
+
serviceVersion = "1.0.0",
|
|
31
|
+
environment = "development",
|
|
32
|
+
traceUrl = "http://localhost:4317",
|
|
33
|
+
metricUrl = "http://localhost:4317",
|
|
34
|
+
traceProtocol = "grpc", // "grpc" or "http"
|
|
35
|
+
metricProtocol, // "grpc" or "http" - if not provided, metrics will be disabled
|
|
36
|
+
traceHeaders = {},
|
|
37
|
+
metricHeaders = {},
|
|
38
|
+
batchProcessorConfig = {},
|
|
39
|
+
exportIntervalMillis = 10000,
|
|
40
|
+
instrumentations,
|
|
41
|
+
samplingRate = 1.0,
|
|
42
|
+
grpcCredentials,
|
|
43
|
+
} = config
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const otlpTraceExporter = createTraceExporter(traceProtocol, traceUrl, traceHeaders, grpcCredentials)
|
|
47
|
+
|
|
48
|
+
// Create metric exporter only if metricProtocol is specified
|
|
49
|
+
let otlpMetricExporter = null
|
|
50
|
+
let metricReader = null
|
|
51
|
+
if (metricProtocol) {
|
|
52
|
+
otlpMetricExporter = createMetricExporter(
|
|
53
|
+
metricProtocol,
|
|
54
|
+
metricUrl,
|
|
55
|
+
metricHeaders,
|
|
56
|
+
grpcCredentials
|
|
57
|
+
)
|
|
58
|
+
metricReader = new PeriodicExportingMetricReader({
|
|
59
|
+
exporter: otlpMetricExporter,
|
|
60
|
+
exportIntervalMillis,
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const sampler = new ParentBasedSampler({
|
|
65
|
+
root: new TraceIdRatioBasedSampler(samplingRate),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const sdkConfig = {
|
|
69
|
+
resource: resourceFromAttributes({
|
|
70
|
+
[ATTR_SERVICE_NAME]: serviceName,
|
|
71
|
+
[ATTR_SERVICE_VERSION]: serviceVersion,
|
|
72
|
+
[ATTR_DEPLOYMENT_ENVIRONMENT]: environment,
|
|
73
|
+
}),
|
|
74
|
+
spanProcessor: new BatchSpanProcessor(otlpTraceExporter, batchProcessorConfig),
|
|
75
|
+
instrumentations: instrumentations ?? [getNodeAutoInstrumentations()],
|
|
76
|
+
sampler,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Add metric reader only if metrics are enabled
|
|
80
|
+
if (metricReader) {
|
|
81
|
+
sdkConfig.metricReader = metricReader
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const sdk = new NodeSDK(sdkConfig)
|
|
85
|
+
|
|
86
|
+
sdk.start()
|
|
87
|
+
logger.info("โ
OpenTelemetry started successfully")
|
|
88
|
+
|
|
89
|
+
// Initialize custom metrics only if metrics are enabled
|
|
90
|
+
let meter = null
|
|
91
|
+
if (metricProtocol) {
|
|
92
|
+
meter = initializeCustomMetrics(serviceName, serviceVersion)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const gracefulShutdown = (signal) => {
|
|
96
|
+
logger.info(`๐ก Received ${signal}, shutting down OpenTelemetry gracefully...`)
|
|
97
|
+
sdk.shutdown()
|
|
98
|
+
.then(() => logger.info("โ
OpenTelemetry shutdown completed"))
|
|
99
|
+
.catch((error) => {
|
|
100
|
+
logger.error("โ Error terminating OpenTelemetry:", error)
|
|
101
|
+
})
|
|
102
|
+
.finally(() => process.exit())
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"))
|
|
106
|
+
process.on("SIGINT", () => gracefulShutdown("SIGINT"))
|
|
107
|
+
|
|
108
|
+
return { sdk, meter }
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger.error("โ Failed to initialize OpenTelemetry:", error)
|
|
111
|
+
throw error
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
26
116
|
* Creates a trace exporter based on the specified protocol
|
|
27
|
-
* @param {object} exporters - Lazily-loaded exporter constructors { OTLPTraceExporter, OTLPTraceExporterHTTP }
|
|
28
117
|
* @param {string} protocol - "grpc" or "http"
|
|
29
118
|
* @param {string} url - Exporter endpoint URL
|
|
30
119
|
* @param {object} headers - Headers to include in requests
|
|
31
120
|
* @param {Function} [grpcCredentials] - gRPC Credentials (optional)
|
|
32
|
-
* @returns {
|
|
33
|
-
*/
|
|
121
|
+
* @returns {OTLPTraceExporter|OTLPTraceExporterHTTP} Configured trace exporter
|
|
122
|
+
*/
|
|
123
|
+
function createTraceExporter(protocol, url, headers = {}, grpcCredentials) {
|
|
124
|
+
if (protocol.toLowerCase() === "http") {
|
|
125
|
+
logger.info(`๐ก Creating HTTP trace exporter for URL: ${url}`)
|
|
126
|
+
return new OTLPTraceExporterHTTP({
|
|
127
|
+
url: url,
|
|
128
|
+
headers: headers,
|
|
129
|
+
})
|
|
130
|
+
} else if (protocol.toLowerCase() === "grpc") {
|
|
131
|
+
logger.info(`๐ก Creating gRPC trace exporter for URL: ${url}`)
|
|
132
|
+
return new OTLPTraceExporter({
|
|
133
|
+
url: url,
|
|
134
|
+
headers: headers,
|
|
135
|
+
credentials: grpcCredentials,
|
|
136
|
+
})
|
|
137
|
+
} else {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`โ Unsupported trace protocol: ${protocol}. Supported protocols are "grpc" and "http"`
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
34
145
|
* Creates a metric exporter based on the specified protocol
|
|
35
|
-
* @param {object} exporters - Lazily-loaded exporter constructors { OTLPMetricExporter, OTLPMetricExporterHTTP }
|
|
36
146
|
* @param {string} protocol - "grpc" or "http"
|
|
37
147
|
* @param {string} url - Exporter endpoint URL
|
|
38
148
|
* @param {object} headers - Headers to include in requests
|
|
39
149
|
* @param {Function} [grpcCredentials] - gRPC Credentials (optional)
|
|
40
|
-
* @returns {
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
150
|
+
* @returns {OTLPMetricExporter|OTLPMetricExporterHTTP} Configured metric exporter
|
|
151
|
+
*/
|
|
152
|
+
function createMetricExporter(protocol, url, headers = {}, grpcCredentials) {
|
|
153
|
+
if (protocol.toLowerCase() === "http") {
|
|
154
|
+
logger.info(`๐ Creating HTTP metric exporter for URL: ${url}`)
|
|
155
|
+
return new OTLPMetricExporterHTTP({
|
|
156
|
+
url: url,
|
|
157
|
+
headers: headers,
|
|
158
|
+
})
|
|
159
|
+
} else if (protocol.toLowerCase() === "grpc") {
|
|
160
|
+
logger.info(`๐ Creating gRPC metric exporter for URL: ${url}`)
|
|
161
|
+
return new OTLPMetricExporter({
|
|
162
|
+
url: url,
|
|
163
|
+
headers: headers,
|
|
164
|
+
credentials: grpcCredentials,
|
|
165
|
+
})
|
|
166
|
+
} else {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`โ Unsupported metric protocol: ${protocol}. Supported protocols are "grpc" and "http"`
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function initializeCustomMetrics(serviceName, serviceVersion) {
|
|
174
|
+
let customMetrics = {}
|
|
175
|
+
const meter = metrics.getMeter(serviceName, serviceVersion)
|
|
176
|
+
|
|
177
|
+
// CPU usage gauge
|
|
178
|
+
customMetrics.cpuUsage = meter.createObservableGauge("process_cpu_usage_percent", {
|
|
179
|
+
description: "Current CPU usage percentage",
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// Memory usage gauges
|
|
183
|
+
customMetrics.memoryUsage = meter.createObservableGauge("process_memory_usage_bytes", {
|
|
184
|
+
description: "Current memory usage in bytes",
|
|
185
|
+
unit: "bytes",
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
customMetrics.memoryHeapUsed = meter.createObservableGauge("process_memory_heap_used_bytes", {
|
|
189
|
+
description: "Current heap memory used in bytes",
|
|
190
|
+
unit: "bytes",
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
customMetrics.memoryHeapTotal = meter.createObservableGauge("process_memory_heap_total_bytes", {
|
|
194
|
+
description: "Current heap memory total in bytes",
|
|
195
|
+
unit: "bytes",
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
let lastCpuUsage = process.cpuUsage()
|
|
199
|
+
let lastMeasureTime = process.hrtime.bigint()
|
|
200
|
+
|
|
201
|
+
customMetrics.cpuUsage.addCallback((result) => {
|
|
202
|
+
const currentCpuUsage = process.cpuUsage(lastCpuUsage)
|
|
203
|
+
const currentTime = process.hrtime.bigint()
|
|
204
|
+
const timeDiff = Number(currentTime - lastMeasureTime) / 1000000 // Convert to milliseconds
|
|
205
|
+
|
|
206
|
+
const cpuPercent = ((currentCpuUsage.user + currentCpuUsage.system) / 1000 / timeDiff) * 100
|
|
207
|
+
|
|
208
|
+
result.observe(cpuPercent)
|
|
209
|
+
|
|
210
|
+
lastCpuUsage = process.cpuUsage()
|
|
211
|
+
lastMeasureTime = currentTime
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
customMetrics.memoryUsage.addCallback((result) => {
|
|
215
|
+
const memUsage = process.memoryUsage()
|
|
216
|
+
result.observe(memUsage.rss, { type: "rss" })
|
|
217
|
+
result.observe(memUsage.external, { type: "external" })
|
|
218
|
+
result.observe(memUsage.arrayBuffers, { type: "arrayBuffers" })
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
customMetrics.memoryHeapUsed.addCallback((result) => {
|
|
222
|
+
const memUsage = process.memoryUsage()
|
|
223
|
+
result.observe(memUsage.heapUsed)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
customMetrics.memoryHeapTotal.addCallback((result) => {
|
|
227
|
+
const memUsage = process.memoryUsage()
|
|
228
|
+
result.observe(memUsage.heapTotal)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
return meter
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
47
235
|
* Wraps a synchronous function and measures total execution time.
|
|
48
236
|
* Creates a single OpenTelemetry span per function call.
|
|
49
237
|
* Use this instead of withObservability when the wrapped function is synchronous,
|
|
50
238
|
* so the return type and call sites remain unchanged.
|
|
51
239
|
*
|
|
52
|
-
* When OTEL is disabled the original function is returned unchanged, so there is
|
|
53
|
-
* zero overhead and no dependency on any @opentelemetry/* package.
|
|
54
|
-
*
|
|
55
240
|
* @param {string} serviceName - The name of the service
|
|
56
241
|
* @param {Function} fn - The synchronous function to wrap
|
|
57
242
|
* @param {string} name - Span name (optional)
|
|
58
243
|
* @returns {Function} Wrapped function (still synchronous)
|
|
59
|
-
*/
|
|
60
|
-
|
|
244
|
+
*/
|
|
245
|
+
export function withSyncObservability(serviceName, fn, name) {
|
|
246
|
+
const tracer = trace.getTracer(serviceName)
|
|
247
|
+
const spanName = name || fn.name || "anonymousFunction"
|
|
248
|
+
|
|
249
|
+
return function (...args) {
|
|
250
|
+
const isBot = context.active().getValue(IS_BOT_KEY)
|
|
251
|
+
return tracer.startActiveSpan(spanName, (span) => {
|
|
252
|
+
if (isBot !== undefined) span.setAttribute("http.response.is_bot", isBot)
|
|
253
|
+
try {
|
|
254
|
+
return fn(...args)
|
|
255
|
+
} catch (err) {
|
|
256
|
+
span.recordException(err)
|
|
257
|
+
span.setStatus({ code: 2, message: err.message })
|
|
258
|
+
throw err
|
|
259
|
+
} finally {
|
|
260
|
+
span.end()
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
61
267
|
* Wraps a function (sync or async) and measures total execution time.
|
|
62
268
|
* Creates a single OpenTelemetry span per function call.
|
|
63
269
|
*
|
|
64
|
-
* When OTEL is disabled the original function is returned unchanged, so there is
|
|
65
|
-
* zero overhead and no dependency on any @opentelemetry/* package.
|
|
66
|
-
*
|
|
67
270
|
* @param {string} serviceName - The name of the service
|
|
68
271
|
* @param {Function} fn - The function to wrap
|
|
69
272
|
* @param {string} name - Span name (optional)
|
|
70
273
|
* @returns {Function} Wrapped function
|
|
71
|
-
*/
|
|
72
|
-
|
|
274
|
+
*/
|
|
275
|
+
export function withObservability(serviceName, fn, name) {
|
|
276
|
+
const tracer = trace.getTracer(serviceName)
|
|
277
|
+
const spanName = name || fn.name || "anonymousFunction"
|
|
278
|
+
|
|
279
|
+
return async function (...args) {
|
|
280
|
+
const isBot = context.active().getValue(IS_BOT_KEY)
|
|
281
|
+
return tracer.startActiveSpan(spanName, async (span) => {
|
|
282
|
+
if (isBot !== undefined) span.setAttribute("http.response.is_bot", isBot)
|
|
283
|
+
try {
|
|
284
|
+
const result = await fn(...args)
|
|
285
|
+
return result
|
|
286
|
+
} catch (err) {
|
|
287
|
+
span.recordException(err)
|
|
288
|
+
span.setStatus({ code: 2, message: err.message })
|
|
289
|
+
throw err
|
|
290
|
+
} finally {
|
|
291
|
+
span.end()
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Express middleware that emits two spans describing what happens to the
|
|
299
|
+
* response body AFTER the app calls res.end() โ the work that lives past the
|
|
300
|
+
* `handler` span and makes the HTTP server span run longer:
|
|
301
|
+
*
|
|
302
|
+
* โข response.compress โ gzip/brotli of the body. Starts when the app calls
|
|
303
|
+
* res.end() (which returns immediately, since compression is async) and
|
|
304
|
+
* ends when the compressed bytes are handed to the real socket end.
|
|
305
|
+
* โข response.flush โ network egress. Starts when those bytes hit the socket
|
|
306
|
+
* and ends on 'finish' (all bytes handed to the OS) or 'close' (connection
|
|
307
|
+
* torn down first).
|
|
308
|
+
*
|
|
309
|
+
* Implementation: the gap is bounded by the *app's* res.end() call (outer) and
|
|
310
|
+
* the *real* socket end after compression (inner), so the middleware wraps
|
|
311
|
+
* res.end on BOTH sides of the compression middleware. It installs an inner
|
|
312
|
+
* hook before next() (which compression then wraps) to time the END of
|
|
313
|
+
* compression, and after next() โ once compression has synchronously patched
|
|
314
|
+
* res.end โ installs an outer hook on top to time the START.
|
|
315
|
+
*
|
|
316
|
+
* MOUNT THIS IMMEDIATELY BEFORE compression() so the post-next() outer hook
|
|
317
|
+
* reliably wraps compression's patch (no async middleware in between). If
|
|
318
|
+
* compression isn't present (or skips the response) only response.flush is
|
|
319
|
+
* emitted. Uses startSpan (not startActiveSpan), so the spans are siblings of
|
|
320
|
+
* `handler` under the same request span. No span is emitted if the app never
|
|
321
|
+
* writes (e.g. the client aborts first).
|
|
322
|
+
*
|
|
323
|
+
* @param {string} serviceName
|
|
324
|
+
* @param {string} [flushName] - egress span name
|
|
325
|
+
* @param {string} [compressName] - compression span name
|
|
326
|
+
* @returns {Function} Express middleware
|
|
327
|
+
*/
|
|
328
|
+
export function responseFlushMiddleware(
|
|
329
|
+
serviceName,
|
|
330
|
+
flushName = "response.flush",
|
|
331
|
+
compressName = "response.compress"
|
|
332
|
+
) {
|
|
333
|
+
const tracer = trace.getTracer(serviceName)
|
|
334
|
+
|
|
335
|
+
return function (req, res, next) {
|
|
336
|
+
// Captured while the request span is active; both spans parent to it.
|
|
337
|
+
const parentContext = context.active()
|
|
338
|
+
|
|
339
|
+
let compressSpan = null
|
|
340
|
+
let flushSpan = null
|
|
341
|
+
let finished = false
|
|
342
|
+
|
|
343
|
+
const finalize = (endEvent) => {
|
|
344
|
+
if (finished) return
|
|
345
|
+
finished = true
|
|
346
|
+
// Whichever span is still open when the response ends gets tagged.
|
|
347
|
+
const open = flushSpan || compressSpan
|
|
348
|
+
if (open) {
|
|
349
|
+
open.setAttribute("http.response.end_event", endEvent) // "finish" | "close"
|
|
350
|
+
open.end()
|
|
351
|
+
}
|
|
352
|
+
compressSpan = null
|
|
353
|
+
flushSpan = null
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Inner hook: wraps the real res.end (installed before compression), so
|
|
357
|
+
// it fires once compression has produced the final bytes โ close the
|
|
358
|
+
// compression span and open the egress span.
|
|
359
|
+
const realEnd = res.end
|
|
360
|
+
const innerEnd = function (...args) {
|
|
361
|
+
if (!finished && !flushSpan) {
|
|
362
|
+
if (compressSpan) {
|
|
363
|
+
compressSpan.end()
|
|
364
|
+
compressSpan = null
|
|
365
|
+
}
|
|
366
|
+
flushSpan = tracer.startSpan(
|
|
367
|
+
flushName,
|
|
368
|
+
{ attributes: { "http.response.is_bot": res.locals.is_bot } },
|
|
369
|
+
parentContext
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
return realEnd.apply(this, args)
|
|
373
|
+
}
|
|
374
|
+
res.end = innerEnd
|
|
375
|
+
|
|
376
|
+
res.once("finish", () => finalize("finish"))
|
|
377
|
+
res.once("close", () => finalize("close"))
|
|
378
|
+
|
|
379
|
+
next()
|
|
380
|
+
|
|
381
|
+
// After next(): compression has synchronously wrapped innerEnd. Install
|
|
382
|
+
// an outer hook on top to catch the app's res.end() call โ open the
|
|
383
|
+
// compression span. Skipped if nothing wrapped innerEnd (no compression),
|
|
384
|
+
// in which case innerEnd alone times the flush from the app's res.end().
|
|
385
|
+
if (res.end !== innerEnd) {
|
|
386
|
+
const chainEnd = res.end
|
|
387
|
+
res.end = function (...args) {
|
|
388
|
+
if (!finished && !compressSpan && !flushSpan) {
|
|
389
|
+
compressSpan = tracer.startSpan(
|
|
390
|
+
compressName,
|
|
391
|
+
{ attributes: { "http.response.is_bot": res.locals.is_bot } },
|
|
392
|
+
parentContext
|
|
393
|
+
)
|
|
394
|
+
}
|
|
395
|
+
return chainEnd.apply(this, args)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export default { init, withObservability, withSyncObservability, responseFlushMiddleware }
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import { createBrowserRouter } from "../index.jsx"
|
|
2
|
+
import { preparedRoutes } from "@catalyst/template/src/js/routes/utils"
|
|
3
|
+
import { registerCatalystServiceWorker } from "../offline/registerServiceWorker.js"
|
|
4
|
+
|
|
5
|
+
registerCatalystServiceWorker()
|
|
6
|
+
|
|
7
|
+
/**
|
|
2
8
|
* @returns returns browsers routers (client routes)
|
|
3
|
-
*/
|
|
9
|
+
*/
|
|
10
|
+
const clientRouter = ({ routerInitialState }) => {
|
|
11
|
+
const browserRouter = createBrowserRouter(preparedRoutes({ routerInitialState }))
|
|
12
|
+
return browserRouter
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default clientRouter
|
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { useRoutes } from "../index.jsx"
|
|
2
|
+
import { preparedRoutes } from "@catalyst/template/src/js/routes/utils"
|
|
3
|
+
|
|
4
|
+
const ServerRouter = (reduxData) => {
|
|
5
|
+
const { store, intialData } = reduxData
|
|
6
|
+
return useRoutes(preparedRoutes({ store, routerInitialState: intialData }))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default ServerRouter
|
package/dist/router.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export * from "@tata1mg/router"
|
package/dist/scripts/build.js
CHANGED
|
@@ -1,3 +1,112 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import path from "path"
|
|
2
|
+
import { spawn } from "child_process"
|
|
3
|
+
import { arrayToObject } from "./scriptUtils.js"
|
|
4
|
+
import { fileURLToPath } from "url"
|
|
5
|
+
import { dirname } from "path"
|
|
6
|
+
import { readFileSync, existsSync, rmSync } 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 configPath = path.join(process.env.PWD, "config/config.json")
|
|
12
|
+
const configJSON = JSON.parse(readFileSync(configPath), "utf-8")
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} command
|
|
16
|
+
* @param {import('child_process').SpawnOptions} options
|
|
17
|
+
*/
|
|
18
|
+
function runBuildStep(command, options) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const child = spawn(command, [], {
|
|
21
|
+
...options,
|
|
22
|
+
shell: true,
|
|
23
|
+
})
|
|
24
|
+
child.on("close", (code) => {
|
|
25
|
+
if (code === 0) {
|
|
26
|
+
resolve()
|
|
27
|
+
} else {
|
|
28
|
+
reject(Object.assign(new Error(`Build step failed with exit code ${code}`), { code }))
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
child.on("error", reject)
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @description - builds the application for production
|
|
37
|
+
*/
|
|
38
|
+
async function build() {
|
|
39
|
+
const commandLineArguments = process.argv.slice(2)
|
|
40
|
+
const argumentsObject = arrayToObject(commandLineArguments)
|
|
41
|
+
const dirname = path.resolve(__dirname, "../../")
|
|
42
|
+
|
|
43
|
+
// Read package.json
|
|
44
|
+
const packageJson = JSON.parse(readFileSync(path.join(process.env.PWD, "package.json"), "utf-8"))
|
|
45
|
+
const { name } = packageJson
|
|
46
|
+
|
|
47
|
+
const buildOutputPath = path.join(process.env.PWD, configJSON.BUILD_OUTPUT_PATH || "build")
|
|
48
|
+
if (existsSync(buildOutputPath)) {
|
|
49
|
+
console.log("๐งน Clearing previous build output...")
|
|
50
|
+
rmSync(buildOutputPath, { recursive: true, force: true })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log("๐๏ธ Building application for production...")
|
|
54
|
+
|
|
55
|
+
const baseEnv = {
|
|
56
|
+
...process.env,
|
|
57
|
+
src_path: process.env.PWD,
|
|
58
|
+
NODE_ENV: "production",
|
|
59
|
+
VITE_BUILD_MODE: "true",
|
|
60
|
+
APPLICATION: name || "catalyst_app",
|
|
61
|
+
NODE_OPTIONS: `--loader ${loaderPath}`,
|
|
62
|
+
...argumentsObject,
|
|
63
|
+
filterKeys: JSON.stringify([
|
|
64
|
+
"src_path",
|
|
65
|
+
"NODE_ENV",
|
|
66
|
+
"VITE_BUILD_MODE",
|
|
67
|
+
"APPLICATION",
|
|
68
|
+
...Object.keys(argumentsObject),
|
|
69
|
+
]),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const serverBuildCommand = `vite build --config ./dist/vite/vite.config.server.js --ssr`
|
|
73
|
+
const clientBuildCommand = `vite build --config ./dist/vite/vite.config.client.js`
|
|
74
|
+
const spawnBase = {
|
|
75
|
+
cwd: dirname,
|
|
76
|
+
stdio: "inherit",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log("๐ง๐ฆ Building server and client bundles in parallel...")
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
await Promise.all([
|
|
83
|
+
runBuildStep(serverBuildCommand, {
|
|
84
|
+
...spawnBase,
|
|
85
|
+
env: { ...baseEnv, CATALYST_VITE_CACHE_ID: "ssr" },
|
|
86
|
+
}),
|
|
87
|
+
runBuildStep(clientBuildCommand, {
|
|
88
|
+
...spawnBase,
|
|
89
|
+
env: { ...baseEnv, CATALYST_VITE_CACHE_ID: "client" },
|
|
90
|
+
}),
|
|
91
|
+
])
|
|
92
|
+
} catch {
|
|
93
|
+
console.error("โ Build failed!")
|
|
94
|
+
process.exit(1)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log("โ
Server and client builds completed!")
|
|
98
|
+
|
|
99
|
+
await runBuildStep("node ./dist/scripts/generateOfflineManifest.js", {
|
|
100
|
+
...spawnBase,
|
|
101
|
+
env: baseEnv,
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
console.log("๐ Build completed successfully!")
|
|
105
|
+
console.log("๐ Built files are located in the 'build' directory")
|
|
106
|
+
console.log("๐ Run 'npm run serve' to start the production server")
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
build().catch((err) => {
|
|
110
|
+
console.error(err)
|
|
111
|
+
process.exit(1)
|
|
112
|
+
})
|
|
@@ -1 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
const fs = require("fs")
|
|
2
|
+
const path = require("path")
|
|
3
|
+
|
|
4
|
+
function cleanCache() {
|
|
5
|
+
const catalystCacheDir = path.join(process.cwd(), "node_modules/catalyst-core")
|
|
6
|
+
const webpackCacheDir = path.join(catalystCacheDir, ".cache/webpack")
|
|
7
|
+
const loadableStats = path.join(catalystCacheDir, "loadable-stats.json")
|
|
8
|
+
|
|
9
|
+
let cleaned = false
|
|
10
|
+
|
|
11
|
+
if (fs.existsSync(webpackCacheDir)) {
|
|
12
|
+
fs.rmSync(webpackCacheDir, { recursive: true, force: true })
|
|
13
|
+
console.log("[Catalyst] Removed webpack filesystem cache.")
|
|
14
|
+
cleaned = true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (fs.existsSync(loadableStats)) {
|
|
18
|
+
fs.rmSync(loadableStats)
|
|
19
|
+
console.log("[Catalyst] Removed loadable-stats.json.")
|
|
20
|
+
cleaned = true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!cleaned) {
|
|
24
|
+
console.log("[Catalyst] Nothing to clean โ cache is already empty.")
|
|
25
|
+
} else {
|
|
26
|
+
console.log("[Catalyst] Clean complete. Next dev start will be a full rebuild.")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
cleanCache()
|