catalyst-core 0.3.0-beta.5 → 0.3.0-canary.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +185 -71
- package/dist/errors/generateDocs.js +100 -0
- package/dist/errors/index.js +243 -0
- package/dist/errors/registry.js +483 -0
- package/dist/errors-index.json +506 -0
- package/dist/globals.d.ts +62 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.jsx +5 -8
- package/dist/logger.js +1 -1
- package/dist/native/androidProject/app/build.gradle.kts +142 -7
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +4 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +2 -2
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +3 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +3 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +43 -8
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeUtilsTest.kt +181 -1
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CameraUtilsTest.kt +156 -236
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileSizeRouterUtilsTest.kt +471 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileUtilsTest.kt +222 -226
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FrameworkServerUtilsTest.kt +300 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/IntentUtilsTest.kt +244 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/MetricsMonitorTest.kt +359 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NativeBridgeTest.kt +108 -63
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NotificationUtilsTest.kt +329 -385
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/OfflineCacheServiceTest.kt +302 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/PerfEventBufferTest.kt +133 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/UtilsKtTest.kt +152 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/WebCacheManagerTest.kt +145 -324
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/BarcodeDetectorTest.kt +132 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/HoldControllerTest.kt +155 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/TorchControllerTest.kt +150 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateMachineTest.kt +120 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateTest.kt +62 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ViewfinderMapperTest.kt +143 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ZoomControllerTest.kt +421 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/design/DesignTokensTest.kt +207 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +368 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/FridaDetectorTest.kt +193 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckManagerTest.kt +260 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckSchedulerTest.kt +166 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DeviceInfoUtilsTest.kt +167 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DownloadUtilsTest.kt +197 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/NetworkMonitorTest.kt +271 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/PushNotificationUtilsTest.kt +119 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/SafeAreaUtilsTest.kt +108 -0
- package/dist/native/androidProject/build.gradle.kts +1 -0
- package/dist/native/androidProject/gradle/libs.versions.toml +26 -0
- package/dist/native/androidProject/settings.gradle.kts +11 -8
- package/dist/native/androidSetup.js +2 -2
- package/dist/native/bridge/WebBridge.d.ts +98 -0
- package/dist/native/bridge/WebBridge.js +40 -6
- package/dist/native/bridge/constants/NativeInterfaces.d.ts +391 -0
- package/dist/native/bridge/errors.d.ts +67 -0
- package/dist/native/bridge/errors.js +66 -151
- package/dist/native/bridge/hooks/index.d.ts +13 -0
- package/dist/native/bridge/hooks/index.js +0 -6
- package/dist/native/bridge/hooks/useAI.d.ts +1 -0
- package/dist/native/bridge/hooks/useAI.js +3 -1
- package/dist/native/bridge/hooks/useCamera.d.ts +86 -0
- package/dist/native/bridge/hooks/useCamera.js +35 -6
- package/dist/native/bridge/hooks/useDataProtection.d.ts +17 -0
- package/dist/native/bridge/hooks/useDeviceInfo.d.ts +34 -0
- package/dist/native/bridge/hooks/useDeviceInfo.js +5 -0
- package/dist/native/bridge/hooks/useFilePicker.d.ts +53 -0
- package/dist/native/bridge/hooks/useFilePicker.js +13 -0
- package/dist/native/bridge/hooks/useGoogleSignIn.d.ts +12 -0
- package/dist/native/bridge/hooks/useHapticFeedback.d.ts +76 -0
- package/dist/native/bridge/hooks/useHapticFeedback.js +6 -1
- package/dist/native/bridge/hooks/useIntent.d.ts +33 -0
- package/dist/native/bridge/hooks/useIntent.js +2 -0
- package/dist/native/bridge/hooks/useNativeTransition.d.ts +53 -0
- package/dist/native/bridge/hooks/useNativeTransition.js +12 -5
- package/dist/native/bridge/hooks/useNetworkStatus.d.ts +22 -0
- package/dist/native/bridge/hooks/useNetworkStatus.js +7 -0
- package/dist/native/bridge/hooks/useNotification.d.ts +83 -0
- package/dist/native/bridge/hooks/useNotification.js +81 -34
- package/dist/native/bridge/hooks/useSafeArea.d.ts +12 -0
- package/dist/native/bridge/hooks/useSafeArea.js +8 -0
- package/dist/native/bridge/hooks/useVideoStream.d.ts +67 -0
- package/dist/native/bridge/hooks/useVideoStream.js +38 -52
- package/dist/native/bridge/hooks/utils.d.ts +4 -0
- package/dist/native/bridge/hooks.d.ts +1 -0
- package/dist/native/bridge/perf/collectors/ApiCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/ApiCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.d.ts +43 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/HookCollector.d.ts +37 -0
- package/dist/native/bridge/perf/collectors/HookCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.d.ts +103 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.d.ts +53 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.d.ts +37 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.d.ts +47 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.js +8 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.d.ts +29 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.d.ts +31 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.js +4 -0
- package/dist/native/bridge/perf/core/constants.d.ts +76 -0
- package/dist/native/bridge/perf/core/measure.d.ts +39 -0
- package/dist/native/bridge/perf/core/measure.js +4 -0
- package/dist/native/bridge/perf/core/session.d.ts +49 -0
- package/dist/native/bridge/perf/core/session.js +4 -0
- package/dist/native/bridge/perf/core/store.d.ts +37 -0
- package/dist/native/bridge/perf/core/store.js +4 -0
- package/dist/native/bridge/perf/core/summary.d.ts +60 -0
- package/dist/native/bridge/perf/core/traceExport.d.ts +10 -0
- package/dist/native/bridge/perf/core/waterfalls.d.ts +2 -0
- package/dist/native/bridge/perf/index.d.ts +63 -0
- package/dist/native/bridge/perf/index.js +4 -0
- package/dist/native/bridge/safeArea.d.ts +11 -0
- package/dist/native/bridge/safeArea.js +2 -0
- package/dist/native/bridge/useBaseHook.d.ts +137 -0
- package/dist/native/bridge/useBaseHook.js +34 -4
- package/dist/native/bridge/utils/CameraUtils.d.ts +94 -0
- package/dist/native/bridge/utils/CameraUtils.js +4 -0
- package/dist/native/bridge/utils/FileObjectConverter.d.ts +36 -0
- package/dist/native/bridge/utils/NativeBridge.d.ts +270 -0
- package/dist/native/bridge/utils/NativeBridge.js +10 -0
- package/dist/native/buildAndroid/index.js +1 -1
- package/dist/native/buildAppAndroid.js +9 -0
- package/dist/native/buildAppIos.js +16 -1
- package/dist/native/buildErrorFormat.js +57 -0
- package/dist/native/buildIos/config.js +15 -1
- package/dist/native/buildIos/index.js +1 -1
- package/dist/native/buildIos/plugins.js +10 -1
- package/dist/native/iosnativeWebView/CoreLogic/Package.swift +36 -0
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamState.swift +1 -1
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamStateMachine.swift +6 -4
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/BootTimingUtility.swift +3 -3
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/FrameworkServerUtils.swift +14 -9
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NetworkMonitor.swift +11 -9
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NotificationHandlerProvider.swift +1 -1
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/URLWhitelistManager.swift +7 -7
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/AnyCodableTests.swift +154 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/BootTimingUtilityTests.swift +23 -5
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/CatalystConstantsTests.swift +192 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/ConfigMappingTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsErrorPathTests.swift +106 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsLoopbackTests.swift +243 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/FrameworkServerUtilsTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NetworkMonitorTests.swift +119 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NotificationHandlerProviderTests.swift +59 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/NotificationHandlerTests.swift +1 -1
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/URLWhitelistManagerTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateMachineTests.swift +171 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateTests.swift +91 -0
- package/dist/native/iosnativeWebView/Package.swift +10 -9
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/LocalNotificationHandler.swift +7 -2
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/NotificationManager.swift +1 -2
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/PushNotificationHandler.swift +5 -4
- package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +6 -5
- package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +31 -30
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CameraTransportUtils.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DeviceInfoUtils.swift +1 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/FilePickerHandler.swift +22 -19
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ImageHandler.swift +40 -39
- package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +15 -15
- package/dist/native/iosnativeWebView/Sources/Core/Utils/WebKitConfig.swift +4 -3
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +6 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeDelegateHandler.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeJavaScriptInterface.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +3 -5
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +20 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +6 -5
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +14 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +3 -3
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +9 -8
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +7 -6
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +24 -25
- package/dist/native/iosnativeWebView/iosnativeWebView/AppDelegate.swift +6 -1
- package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +4 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +2 -22
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/CacheManagerTests.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/FilePickerHandlerTests.swift +2 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/NativeBridgeTests.swift +15 -9
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +1 -0
- package/dist/native/setupEmulatorIos.js +2 -2
- package/dist/native/terminalProgress.js +17 -17
- package/dist/router/ClientRouter.d.ts +8 -0
- package/dist/router/ClientRouter.js +13 -11
- package/dist/router/ServerRouter.d.ts +13 -0
- package/dist/router/ServerRouter.js +19 -7
- package/dist/scripts/build.js +14 -2
- package/dist/scripts/loadEnvironmentVariables.js +3 -2
- package/dist/scripts/plugins.js +1 -1
- package/dist/scripts/preServerInit.js +49 -3
- package/dist/scripts/preflight.js +111 -0
- package/dist/scripts/scriptUtils.js +46 -1
- package/dist/scripts/serve.js +7 -3
- package/dist/scripts/start.js +8 -3
- package/dist/server/expressServer.d.ts +1 -0
- package/dist/server/expressServer.js +247 -271
- package/dist/server/manifestCache.d.ts +2 -0
- package/dist/server/manifestCache.js +25 -33
- package/dist/server/renderer/ChunkExtractor.d.ts +44 -0
- package/dist/server/renderer/ChunkExtractor.js +113 -125
- package/dist/server/renderer/document/Body.d.ts +21 -0
- package/dist/server/renderer/document/Body.jsx +45 -45
- package/dist/server/renderer/document/FastRefresh.d.ts +3 -0
- package/dist/server/renderer/document/FastRefresh.jsx +7 -13
- package/dist/server/renderer/document/Head.d.ts +20 -0
- package/dist/server/renderer/document/Head.jsx +32 -32
- package/dist/server/renderer/document/index.d.ts +2 -0
- package/dist/server/renderer/document/index.jsx +2 -2
- package/dist/server/renderer/extract.d.ts +67 -0
- package/dist/server/renderer/extract.js +92 -89
- package/dist/server/renderer/handler.d.ts +2 -0
- package/dist/server/renderer/handler.jsx +401 -404
- package/dist/server/renderer/index.d.ts +2 -0
- package/dist/server/renderer/index.js +5 -8
- package/dist/server/renderer/render.d.ts +35 -0
- package/dist/server/renderer/render.js +25 -32
- package/dist/server/utils/botDetectionMiddleware.d.ts +1 -0
- package/dist/server/utils/botDetectionMiddleware.js +33 -23
- package/dist/server/utils/cjsRequire.d.ts +8 -0
- package/dist/server/utils/cjsRequire.js +2 -2
- package/dist/server/utils/userAgentUtil.d.ts +7 -0
- package/dist/server/utils/userAgentUtil.js +68 -71
- package/dist/server/utils/validator.d.ts +31 -0
- package/dist/server/utils/validator.js +133 -158
- package/dist/vite/inject-cache-key-plugin.js +3 -27
- package/dist/vite/moduleAliases.js +36 -0
- package/dist/vite/node-loader.mjs +2 -29
- package/dist/vite/resolveDevServerConfig.js +4 -1
- package/dist/vite/vite.config.js +2 -29
- package/dist/web-router/components/MetaTag.d.ts +4 -0
- package/dist/web-router/components/MetaTag.jsx +26 -28
- package/dist/web-router/components/MetaTag.test.jsx +100 -0
- package/dist/web-router/components/RouterDataProvider.d.ts +96 -0
- package/dist/web-router/components/RouterDataProvider.jsx +260 -259
- package/dist/web-router/components/RouterDataProvider.server.test.jsx +98 -0
- package/dist/web-router/components/RouterDataProvider.test.jsx +310 -0
- package/dist/web-router/components/Split.d.ts +74 -0
- package/dist/web-router/components/Split.jsx +147 -156
- package/dist/web-router/components/Split.server.test.jsx +52 -0
- package/dist/web-router/components/Split.test.jsx +167 -0
- package/dist/web-router/components/SplitInview.d.ts +22 -0
- package/dist/web-router/components/SplitInview.jsx +78 -89
- package/dist/web-router/components/SplitInview.test.jsx +112 -0
- package/dist/web-router/components/SsrRequestContext.d.ts +20 -0
- package/dist/web-router/components/SsrRequestContext.jsx +17 -7
- package/dist/web-router/components/SsrRequestContext.test.jsx +53 -0
- package/dist/web-router/context.d.ts +15 -0
- package/dist/web-router/context.jsx +5 -5
- package/dist/web-router/context.test.jsx +10 -0
- package/dist/web-router/utils/metaDataUtils.d.ts +24 -0
- package/dist/web-router/utils/metaDataUtils.jsx +86 -86
- package/dist/web-router/utils/metaDataUtils.test.jsx +188 -0
- package/dist/web-router/vitest.setup.js +12 -0
- package/mcp_v2/knowledge-base.json +115 -17
- package/mcp_v2/mcp.js +141 -3
- package/mcp_v2/package-lock.json +2031 -0
- package/mcp_v2/package.json +13 -2
- package/mcp_v2/scripts/copyErrorsIndex.cjs +34 -0
- package/mcp_v2/test/errors.test.ts +123 -0
- package/mcp_v2/test/github.test.ts +177 -0
- package/mcp_v2/tools/errors.d.ts +20 -0
- package/mcp_v2/tools/errors.js +63 -0
- package/mcp_v2/tools/github.d.ts +41 -0
- package/mcp_v2/tools/github.js +231 -0
- package/mcp_v2/vitest.config.mjs +22 -0
- package/package.json +60 -11
- package/test/errors/docsDrift.test.ts +113 -0
- package/test/errors/index.test.ts +200 -0
- package/test/errors/registry.test.ts +108 -0
- package/test/scripts/preflight.test.ts +118 -0
- package/test/server/ChunkExtractor.test.ts +97 -0
- package/test/server/extract.test.ts +128 -0
- package/test/server/fixtures/template/server/document.jsx +19 -0
- package/test/server/fixtures/template/server/index.js +10 -0
- package/test/server/fixtures/template/src/js/containers/App/index.jsx +33 -0
- package/test/server/fixtures/template/src/js/routes/index.jsx +72 -0
- package/test/server/fixtures/template/src/js/routes/utils.jsx +39 -0
- package/test/server/fixtures/template/src/js/store/index.js +19 -0
- package/test/server/handler.assets.test.ts +330 -0
- package/test/server/handler.test.ts +208 -0
- package/test/server/manifestCache.test.ts +71 -0
- package/test/server/render.test.ts +71 -0
- package/test/server/scriptUtils.test.ts +103 -0
- package/test/server/userAgentUtil.test.ts +48 -0
- package/test/server/validator.test.ts +225 -0
- package/test/vite/resolveDevServerConfig.test.js +4 -1
- package/tsconfig.test.json +23 -0
- package/vitest.config.ts +93 -0
- package/dist/caching.js +0 -1
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/DeviceInfoUtilsTest.kt +0 -187
- package/dist/native/bridge/standardizedHooks.js +0 -611
- package/dist/router.js +0 -1
- package/dist/scripts/validator.js +0 -149
- package/dist/web-router/hooks.jsx +0 -72
- package/dist/web-router/types/components/MetaTag.d.ts +0 -2
- package/dist/web-router/types/components/RouterDataProvider.d.ts +0 -95
- package/dist/web-router/types/context.d.ts +0 -14
- package/dist/web-router/types/hooks.d.ts +0 -1
- package/dist/web-router/types/index.d.ts +0 -3
- package/dist/web-router/types/utils/metaDataUtils.d.ts +0 -40
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/CatalystConstants.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/ConfigConstants.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Notifications/NotificationModels.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Protocols/NotificationHandlerProtocol.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NullNotificationHandler.swift +0 -0
|
@@ -1,328 +1,333 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import { StaticRouter } from "react-router"
|
|
8
|
-
import ServerRouter from "../../router/ServerRouter.js"
|
|
9
|
-
import { renderToPipeableStream } from "react-dom/server"
|
|
10
|
-
import { getUserAgentDetails } from "../utils/userAgentUtil.js"
|
|
11
|
-
import { serverDataFetcher
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} from "
|
|
25
|
-
import
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
1
|
+
/* global globalThis */
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { renderStart, renderEnd } from "./render.js";
|
|
4
|
+
import { Provider } from "react-redux";
|
|
5
|
+
import { Body } from "./document/Body.jsx";
|
|
6
|
+
import { Head } from "./document/Head.jsx";
|
|
7
|
+
import { StaticRouter, matchRoutes as NestedMatchRoutes } from "react-router";
|
|
8
|
+
import ServerRouter from "../../router/ServerRouter.js";
|
|
9
|
+
import { renderToPipeableStream } from "react-dom/server";
|
|
10
|
+
import { getUserAgentDetails } from "../utils/userAgentUtil.js";
|
|
11
|
+
import { serverDataFetcher } from "../../web-router/components/RouterDataProvider.jsx";
|
|
12
|
+
import { getMetaData } from "../../web-router/utils/metaDataUtils.jsx";
|
|
13
|
+
import { validateConfigureStore, validateGetRoutes, validateCustomDocument, handleError, safeCall } from "../utils/validator.js";
|
|
14
|
+
import { ChunkExtractor } from "./ChunkExtractor.js";
|
|
15
|
+
import { readCssFromDisk, generateScriptElements, generateScriptStrings, getDeferredRouteKey, getCachedDeferredCssPathsForRoute, registerDeferredAssetsForRoute, getDeferredPreloadScriptUrls, generateModulePreloadLinkElements } from "./extract.js";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { Transform } from "node:stream";
|
|
18
|
+
import CustomDocument from "@catalyst/template/server/document";
|
|
19
|
+
import App from "@catalyst/template/src/js/containers/App/index";
|
|
20
|
+
import { getRoutes } from "@catalyst/template/src/js/routes/utils";
|
|
21
|
+
import createStore from "@catalyst/template/src/js/store/index.js";
|
|
22
|
+
import { SsrRequestProvider } from "../../web-router/components/SsrRequestContext.jsx";
|
|
23
|
+
import { getManifest, getAssetManifest } from "../manifestCache.js";
|
|
24
|
+
import { wrapSSRError, formatError } from "../../errors/index.js";
|
|
25
|
+
import { resolveOutputMode, getDebugEnvInfo } from "../../scripts/scriptUtils.js";
|
|
26
|
+
|
|
27
|
+
// Resolved once at module load — spawned via serve.js/start.js, which
|
|
28
|
+
// forward the mode as CATALYST_OUTPUT_MODE. Passed an empty argv on purpose:
|
|
29
|
+
// this process never sees the parent's CLI flags, so process.argv here would
|
|
30
|
+
// never contain --debug/--verbose — only CATALYST_OUTPUT_MODE is real input.
|
|
31
|
+
const outputMode = resolveOutputMode([], process.env);
|
|
32
|
+
// Debug mode's boxed output already embeds the full stack trace (see
|
|
33
|
+
// errors/index.js#formatDebug), so only default/verbose need the original
|
|
34
|
+
// error printed separately to avoid losing the stack.
|
|
35
|
+
const logSSRError = (stage, error) => {
|
|
36
|
+
const wrapped = wrapSSRError(stage, error);
|
|
37
|
+
const debugEnv = outputMode === "debug" ? getDebugEnvInfo() : undefined;
|
|
38
|
+
console.error(formatError(wrapped, outputMode, debugEnv));
|
|
39
|
+
if (outputMode !== "debug") console.error(error);
|
|
40
|
+
};
|
|
41
|
+
const DEFAULT_SAFE_AREA_INSETS = {
|
|
42
|
+
top: 0,
|
|
43
|
+
right: 0,
|
|
44
|
+
bottom: 0,
|
|
45
|
+
left: 0
|
|
46
|
+
};
|
|
47
|
+
const parseSafeAreaFromHeaders = req => {
|
|
48
|
+
const readEdge = header => {
|
|
49
|
+
const raw = req.get(header) ?? req.headers[header.toLowerCase()];
|
|
50
|
+
const value = Number(raw);
|
|
51
|
+
return Number.isFinite(value) && value >= 0 ? value : null;
|
|
52
|
+
};
|
|
53
|
+
const top = readEdge("X-Safe-Area-Top");
|
|
54
|
+
const right = readEdge("X-Safe-Area-Right");
|
|
55
|
+
const bottom = readEdge("X-Safe-Area-Bottom");
|
|
56
|
+
const left = readEdge("X-Safe-Area-Left");
|
|
57
|
+
if (![top, right, bottom, left].some(value => value !== null)) return null;
|
|
58
|
+
return {
|
|
59
|
+
top: top ?? 0,
|
|
60
|
+
right: right ?? 0,
|
|
61
|
+
bottom: bottom ?? 0,
|
|
62
|
+
left: left ?? 0
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// App contract checks run at module load, not per request. The validators
|
|
67
|
+
// log-and-continue (see server/utils/validator.ts): a violation is reported once
|
|
68
|
+
// through the shared structured-error formatter at module load — server startup
|
|
69
|
+
// in production, first SSR request in dev — rather than being re-reported on
|
|
70
|
+
// every request or swallowed into onRequestError by _handler's try/catch.
|
|
71
|
+
validateGetRoutes(getRoutes);
|
|
72
|
+
validateConfigureStore(createStore);
|
|
59
73
|
|
|
60
74
|
// Routes are static for the lifetime of the server — resolve once and reuse
|
|
61
75
|
// the same instance per request to avoid per-request allocation.
|
|
62
|
-
let _cachedRoutes
|
|
76
|
+
let _cachedRoutes;
|
|
63
77
|
const getCachedRoutes = () => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
78
|
+
if (_cachedRoutes === undefined) {
|
|
79
|
+
const getRoutesErr = validateGetRoutes(getRoutes);
|
|
80
|
+
if (getRoutesErr) handleError(getRoutesErr);
|
|
81
|
+
_cachedRoutes = getRoutesErr ? null : getRoutes();
|
|
82
|
+
}
|
|
83
|
+
return _cachedRoutes;
|
|
84
|
+
};
|
|
69
85
|
|
|
70
86
|
// Try to import user-defined hooks. These are optional — apps that don't export them
|
|
71
87
|
// will get undefined, and safeCall is a no-op for non-functions.
|
|
72
|
-
let _onRouteMatch,
|
|
73
|
-
_onFetcherSuccess,
|
|
74
|
-
_onFetcherError,
|
|
75
|
-
_onAppServerSideSuccess,
|
|
76
|
-
_onAppServerSideError,
|
|
77
|
-
_onRenderError,
|
|
78
|
-
_onRequestError
|
|
88
|
+
let _onRouteMatch, _onFetcherSuccess, _onFetcherError, _onAppServerSideSuccess, _onAppServerSideError, _onRenderError, _onRequestError;
|
|
79
89
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
const hooks = await import("@catalyst/template/server/index.js");
|
|
91
|
+
_onRouteMatch = hooks.onRouteMatch;
|
|
92
|
+
_onFetcherSuccess = hooks.onFetcherSuccess;
|
|
93
|
+
_onFetcherError = hooks.onFetcherError;
|
|
94
|
+
_onAppServerSideSuccess = hooks.onAppServerSideSuccess;
|
|
95
|
+
_onAppServerSideError = hooks.onAppServerSideError;
|
|
96
|
+
_onRenderError = hooks.onRenderError;
|
|
97
|
+
_onRequestError = hooks.onRequestError;
|
|
88
98
|
} catch {
|
|
89
|
-
|
|
99
|
+
// No hooks file — all hooks remain undefined, safeCall will skip them
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
// Passthrough no-ops used when OTEL_ENABLE is not set; replaced below if enabled.
|
|
93
|
-
let withObservability = (_service, fn) => fn
|
|
94
|
-
let withSyncObservability = (_service, fn) => fn
|
|
103
|
+
let withObservability = (_service, fn) => fn;
|
|
104
|
+
let withSyncObservability = (_service, fn) => fn;
|
|
95
105
|
|
|
106
|
+
// config.json booleans survive the process.env swap in loadEnvironmentVariables(),
|
|
107
|
+
// so this is genuinely true at runtime when config sets OTEL_ENABLE: true. The cast
|
|
108
|
+
// exists only because TS types process.env values as string | undefined.
|
|
96
109
|
if (process.env.OTEL_ENABLE === true) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
try {
|
|
111
|
+
const otel = await import("../../otel.js");
|
|
112
|
+
withObservability = otel.withObservability;
|
|
113
|
+
withSyncObservability = otel.withSyncObservability;
|
|
114
|
+
} catch {
|
|
115
|
+
// otel packages not installed — continue without tracing
|
|
116
|
+
}
|
|
104
117
|
}
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
const onAppServerSideSuccess = traceHook(_onAppServerSideSuccess, "onAppServerSideSuccess")
|
|
115
|
-
const onAppServerSideError = traceHook(_onAppServerSideError, "onAppServerSideError")
|
|
116
|
-
const onRenderError = traceHook(_onRenderError, "onRenderError")
|
|
117
|
-
const onRequestError = traceHook(_onRequestError, "onRequestError")
|
|
118
|
+
const SSR_SERVICE = process.env.SERVICE_NAME || `pwa-${process.env.APPLICATION}-node-server`;
|
|
119
|
+
const traceHook = (fn, spanName) => typeof fn === "function" ? withSyncObservability(SSR_SERVICE, fn, spanName) : fn;
|
|
120
|
+
const onRouteMatch = traceHook(_onRouteMatch, "onRouteMatch");
|
|
121
|
+
const onFetcherSuccess = traceHook(_onFetcherSuccess, "onFetcherSuccess");
|
|
122
|
+
const onFetcherError = traceHook(_onFetcherError, "onFetcherError");
|
|
123
|
+
const onAppServerSideSuccess = traceHook(_onAppServerSideSuccess, "onAppServerSideSuccess");
|
|
124
|
+
const onAppServerSideError = traceHook(_onAppServerSideError, "onAppServerSideError");
|
|
125
|
+
const onRenderError = traceHook(_onRenderError, "onRenderError");
|
|
126
|
+
const onRequestError = traceHook(_onRequestError, "onRequestError");
|
|
118
127
|
|
|
119
128
|
// ── Asset collection ───────────────────────────────────────────────────
|
|
120
129
|
const _collectAssets = (req, allMatches) => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
const chunkExtractor = new ChunkExtractor({
|
|
131
|
+
manifest: getManifest() || {},
|
|
132
|
+
assetManifest: getAssetManifest() || {}
|
|
133
|
+
});
|
|
125
134
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const collectAssets = withSyncObservability(SSR_SERVICE, _collectAssets, "collectAssets")
|
|
135
|
+
// Add route-matched CSS/JS to critical bucket (loaded in <head>)
|
|
136
|
+
chunkExtractor.preloadRouteCss(allMatches);
|
|
137
|
+
return chunkExtractor;
|
|
138
|
+
};
|
|
139
|
+
const collectAssets = withSyncObservability(SSR_SERVICE, _collectAssets, "collectAssets");
|
|
133
140
|
|
|
134
141
|
// ── JSX tree ───────────────────────────────────────────────────────────
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
// Three type-only escape hatches in the tree below. They are declared here as
|
|
143
|
+
// plain aliases rather than inline @ts-expect-error comments, whose placement
|
|
144
|
+
// inside JSX is unreliable. The aliases are erased at compile time and change
|
|
145
|
+
// nothing at runtime.
|
|
146
|
+
//
|
|
147
|
+
// - SsrRequestProvider / Provider are still .jsx, so TypeScript infers their
|
|
148
|
+
// props from the implementation and does not model implicit JSX children.
|
|
149
|
+
// Both aliases can go away once those modules are converted.
|
|
150
|
+
// - `context` is a react-router v5 prop that v7's StaticRouter no longer
|
|
151
|
+
// accepts or reads. It is kept because this conversion must not change
|
|
152
|
+
// runtime behaviour; removing it is a separate change.
|
|
153
|
+
const SsrRequestProviderAny = SsrRequestProvider;
|
|
154
|
+
const ProviderAny = Provider;
|
|
155
|
+
const StaticRouterAny = StaticRouter;
|
|
156
|
+
const getComponent = (store, context, req, fetcherData, isBot) => <div id="app">
|
|
157
|
+
<SsrRequestProviderAny value={{
|
|
158
|
+
isBot
|
|
159
|
+
}}>
|
|
160
|
+
<ProviderAny store={store}>
|
|
161
|
+
<StaticRouterAny context={context} location={req.originalUrl}>
|
|
140
162
|
<ServerRouter store={store} intialData={fetcherData} />
|
|
141
|
-
</
|
|
142
|
-
</
|
|
143
|
-
</
|
|
144
|
-
</div
|
|
145
|
-
)
|
|
163
|
+
</StaticRouterAny>
|
|
164
|
+
</ProviderAny>
|
|
165
|
+
</SsrRequestProviderAny>
|
|
166
|
+
</div>;
|
|
146
167
|
|
|
147
168
|
// ── Render and stream ──────────────────────────────────────────────────
|
|
148
|
-
const _renderMarkUp = async (
|
|
149
|
-
|
|
169
|
+
const _renderMarkUp = async (errorCode, req, res, metaTags, fetcherData, store, allMatches, context, chunkExtractor) => {
|
|
170
|
+
const deviceDetails = getUserAgentDetails(req.headers["user-agent"] || "");
|
|
171
|
+
// Match mweb's wider definition: synthetic monitors (StatusCake) and AI crawlers
|
|
172
|
+
// also need the bot code path — no JS, and split(... ssr: false ...) widgets that
|
|
173
|
+
// previously read state.shellReducer.isBot now read this via SsrRequestContext.
|
|
174
|
+
const isBot = !!(deviceDetails.googleBot || deviceDetails.aiBot || deviceDetails.statusCakeBot);
|
|
175
|
+
const safeArea = parseSafeAreaFromHeaders(req) || {
|
|
176
|
+
...DEFAULT_SAFE_AREA_INSETS
|
|
177
|
+
};
|
|
178
|
+
const previousSafeArea = globalThis.__SAFE_AREA_INITIAL__;
|
|
179
|
+
globalThis.__SAFE_AREA_INITIAL__ = safeArea;
|
|
180
|
+
const cleanupSafeArea = () => {
|
|
181
|
+
if (previousSafeArea === undefined) {
|
|
182
|
+
delete globalThis.__SAFE_AREA_INITIAL__;
|
|
183
|
+
} else {
|
|
184
|
+
globalThis.__SAFE_AREA_INITIAL__ = previousSafeArea;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Critical assets → <head>
|
|
189
|
+
const criticalAssets = chunkExtractor ? chunkExtractor.getCriticalAssets() : {
|
|
190
|
+
js: [],
|
|
191
|
+
css: []
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Inline critical CSS from disk (small thanks to natural code-splitting)
|
|
195
|
+
const buildDir = path.join(process.env.src_path, process.env.BUILD_OUTPUT_PATH || "build");
|
|
196
|
+
const inlineCss = readCssFromDisk(criticalAssets.css, buildDir);
|
|
197
|
+
const deferredRouteKey = getDeferredRouteKey(req, allMatches);
|
|
198
|
+
const deferredRouteInlineCss = readCssFromDisk(getCachedDeferredCssPathsForRoute(deferredRouteKey), buildDir);
|
|
199
|
+
const jsScripts = generateScriptElements(criticalAssets.js);
|
|
200
|
+
const criticalPreloadLinks = generateModulePreloadLinkElements(criticalAssets.js, "critical-js");
|
|
201
|
+
const deferredPreloadUrls = getDeferredPreloadScriptUrls(deferredRouteKey, criticalAssets.js);
|
|
202
|
+
const deferredPreloadLinks = generateModulePreloadLinkElements(deferredPreloadUrls, "deferred-js");
|
|
203
|
+
|
|
204
|
+
// Build Head props
|
|
205
|
+
const shellStart = renderStart({
|
|
206
|
+
inlineCss,
|
|
207
|
+
deferredRouteInlineCss,
|
|
208
|
+
jsScripts,
|
|
209
|
+
criticalPreloadLinks,
|
|
210
|
+
deferredPreloadLinks,
|
|
211
|
+
metaTags,
|
|
212
|
+
isBot,
|
|
213
|
+
fetcherData
|
|
214
|
+
});
|
|
215
|
+
const state = store.getState();
|
|
216
|
+
const jsx = getComponent(store, context, req, fetcherData, isBot);
|
|
217
|
+
const shellEnd = renderEnd(state, res, jsx, errorCode, fetcherData);
|
|
218
|
+
|
|
219
|
+
// The response status also reaches the document as `statusCode`, which Body
|
|
220
|
+
// exposes as window.__STATUS_CODE__. `errorCode` stays for custom documents.
|
|
221
|
+
const status = errorCode || (allMatches.length && allMatches[0]?.route?.path === "*" ? 404 : 200);
|
|
222
|
+
const finalProps = {
|
|
223
|
+
...shellStart,
|
|
224
|
+
...shellEnd,
|
|
225
|
+
jsx,
|
|
150
226
|
req,
|
|
151
227
|
res,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const isBot = !!(deviceDetails.googleBot || deviceDetails.aiBot || deviceDetails.statusCakeBot)
|
|
164
|
-
const safeArea = parseSafeAreaFromHeaders(req) || { ...DEFAULT_SAFE_AREA_INSETS }
|
|
165
|
-
const previousSafeArea = globalThis.__SAFE_AREA_INITIAL__
|
|
166
|
-
globalThis.__SAFE_AREA_INITIAL__ = safeArea
|
|
167
|
-
|
|
168
|
-
const cleanupSafeArea = () => {
|
|
169
|
-
if (previousSafeArea === undefined) {
|
|
170
|
-
delete globalThis.__SAFE_AREA_INITIAL__
|
|
171
|
-
} else {
|
|
172
|
-
globalThis.__SAFE_AREA_INITIAL__ = previousSafeArea
|
|
173
|
-
}
|
|
228
|
+
safeArea,
|
|
229
|
+
statusCode: status
|
|
230
|
+
};
|
|
231
|
+
const CompleteDocument = () => {
|
|
232
|
+
// CustomDocument is an OPTIONAL export (server/document.js) — its
|
|
233
|
+
// absence is valid, so PREFLIGHT-020 (missing) is not surfaced. If it
|
|
234
|
+
// IS exported but isn't callable, flag it (PREFLIGHT-021) and fall
|
|
235
|
+
// back to the built-in document rather than crashing the render.
|
|
236
|
+
if (CustomDocument) {
|
|
237
|
+
const customDocErr = validateCustomDocument(CustomDocument);
|
|
238
|
+
if (customDocErr) handleError(customDocErr);else return CustomDocument(finalProps);
|
|
174
239
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
240
|
+
return <html lang={finalProps.lang}>
|
|
241
|
+
<Head isBot={finalProps.isBot} inlineCss={finalProps.inlineCss} deferredRouteInlineCss={finalProps.deferredRouteInlineCss} jsScripts={finalProps.jsScripts} criticalPreloadLinks={finalProps.criticalPreloadLinks} deferredPreloadLinks={finalProps.deferredPreloadLinks} fetcherData={finalProps.fetcherData} metaTags={finalProps.metaTags} publicAssetPath={finalProps.publicAssetPath} />
|
|
242
|
+
<Body initialState={finalProps.initialState} jsx={finalProps.jsx} statusCode={finalProps.statusCode} fetcherData={finalProps.fetcherData} safeArea={finalProps.safeArea} />
|
|
243
|
+
</html>;
|
|
244
|
+
};
|
|
245
|
+
try {
|
|
246
|
+
res.set({
|
|
247
|
+
"content-type": "text/html; charset=utf-8"
|
|
248
|
+
});
|
|
249
|
+
res.status(status);
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
// Single completion path: React's pipe() auto-ends `tail`, and
|
|
252
|
+
// `flush()` appends the deferred asset tags before that end
|
|
253
|
+
// signal propagates to `res` via the plain pipe below. This
|
|
254
|
+
// avoids the onShellReady/onAllReady race where React's own
|
|
255
|
+
// stream-end and a manual res.end() compete to close `res`
|
|
256
|
+
// (see issue #320).
|
|
257
|
+
const tail = new Transform({
|
|
258
|
+
transform(chunk, _enc, cb) {
|
|
259
|
+
cb(null, chunk);
|
|
260
|
+
},
|
|
261
|
+
flush(cb) {
|
|
262
|
+
// Deferred assets — injected after body (non-blocking)
|
|
263
|
+
const deferredAssets = chunkExtractor ? chunkExtractor.getDeferredAssets() : {
|
|
264
|
+
js: [],
|
|
265
|
+
css: []
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// Tell client which components were SSR'd so split() can
|
|
269
|
+
// eagerly import them (prevents Suspense fallback flash)
|
|
270
|
+
this.push(`<script>window.__CATALYST_IS_BOT__=${isBot ? "true" : "false"};</script>`);
|
|
271
|
+
if (chunkExtractor) {
|
|
272
|
+
const renderedKeys = chunkExtractor.getRenderedComponentKeys();
|
|
273
|
+
this.push(
|
|
274
|
+
// nosemgrep: javascript.lang.security.audit.unknown-value-with-script-tag.unknown-value-with-script-tag - renderedKeys are internal bundler component-module keys tracked by ChunkExtractor, never request/user input, and are JSON.stringify-escaped before embedding.
|
|
275
|
+
`<script>window.__SSR_RENDERED_COMPONENTS__=new Set(${JSON.stringify(renderedKeys)})</script>`);
|
|
276
|
+
}
|
|
277
|
+
const {
|
|
278
|
+
newCssPaths
|
|
279
|
+
} = registerDeferredAssetsForRoute(deferredRouteKey, deferredAssets, isBot);
|
|
280
|
+
if (newCssPaths.length) {
|
|
281
|
+
this.push(`<style>${readCssFromDisk(newCssPaths, buildDir)}</style>`);
|
|
282
|
+
}
|
|
283
|
+
if (!isBot) {
|
|
284
|
+
this.push(generateScriptStrings(deferredAssets.js));
|
|
285
|
+
}
|
|
286
|
+
cb();
|
|
215
287
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
? chunkExtractor.getDeferredAssets()
|
|
260
|
-
: { js: [], css: [] }
|
|
261
|
-
|
|
262
|
-
// Tell client which components were SSR'd so split() can
|
|
263
|
-
// eagerly import them (prevents Suspense fallback flash)
|
|
264
|
-
this.push(`<script>window.__CATALYST_IS_BOT__=${isBot ? "true" : "false"};</script>`)
|
|
265
|
-
if (chunkExtractor) {
|
|
266
|
-
const renderedKeys = chunkExtractor.getRenderedComponentKeys()
|
|
267
|
-
this.push(
|
|
268
|
-
// nosemgrep: javascript.lang.security.audit.unknown-value-with-script-tag.unknown-value-with-script-tag - renderedKeys are internal bundler component-module keys tracked by ChunkExtractor, never request/user input, and are JSON.stringify-escaped before embedding.
|
|
269
|
-
`<script>window.__SSR_RENDERED_COMPONENTS__=new Set(${JSON.stringify(renderedKeys)})</script>`
|
|
270
|
-
)
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const { newCssPaths } = registerDeferredAssetsForRoute(
|
|
274
|
-
deferredRouteKey,
|
|
275
|
-
deferredAssets,
|
|
276
|
-
isBot
|
|
277
|
-
)
|
|
278
|
-
if (newCssPaths.length) {
|
|
279
|
-
this.push(`<style>${readCssFromDisk(newCssPaths, buildDir)}</style>`)
|
|
280
|
-
}
|
|
281
|
-
if (!isBot) {
|
|
282
|
-
this.push(generateScriptStrings(deferredAssets.js))
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
cb()
|
|
286
|
-
},
|
|
287
|
-
})
|
|
288
|
-
tail.pipe(res)
|
|
289
|
-
|
|
290
|
-
const { pipe } = renderToPipeableStream(<CompleteDocument />, {
|
|
291
|
-
onShellReady() {
|
|
292
|
-
res.setHeader("content-type", "text/html")
|
|
293
|
-
pipe(tail)
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
onAllReady() {
|
|
297
|
-
cleanupSafeArea()
|
|
298
|
-
resolve()
|
|
299
|
-
},
|
|
300
|
-
|
|
301
|
-
onError(error) {
|
|
302
|
-
console.error("Error in renderToPipeableStream:", error)
|
|
303
|
-
safeCall(onRenderError, { req, res, store, error })
|
|
304
|
-
cleanupSafeArea()
|
|
305
|
-
tail.destroy(error)
|
|
306
|
-
reject(error)
|
|
307
|
-
},
|
|
308
|
-
})
|
|
309
|
-
})
|
|
310
|
-
} catch (error) {
|
|
311
|
-
cleanupSafeArea()
|
|
312
|
-
console.error("Error in rendering document on server:", error)
|
|
313
|
-
safeCall(onRenderError, { req, res, store, error })
|
|
314
|
-
return Promise.reject(error)
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const tracedRenderMarkUp = withObservability(SSR_SERVICE, _renderMarkUp, "renderMarkUp")
|
|
319
|
-
const tracedAppServerSideFunction = withObservability(
|
|
320
|
-
SSR_SERVICE,
|
|
321
|
-
(args) => App.serverSideFunction(args),
|
|
322
|
-
"App.serverSideFunction"
|
|
323
|
-
)
|
|
324
|
-
const tracedServerDataFetcher = withObservability(SSR_SERVICE, serverDataFetcher, "serverDataFetcher")
|
|
325
|
-
const tracedGetMetaData = withSyncObservability(SSR_SERVICE, getMetaData, "getMetaData")
|
|
288
|
+
});
|
|
289
|
+
tail.pipe(res);
|
|
290
|
+
const {
|
|
291
|
+
pipe
|
|
292
|
+
} = renderToPipeableStream(<CompleteDocument />, {
|
|
293
|
+
onShellReady() {
|
|
294
|
+
res.setHeader("content-type", "text/html");
|
|
295
|
+
pipe(tail);
|
|
296
|
+
},
|
|
297
|
+
onAllReady() {
|
|
298
|
+
cleanupSafeArea();
|
|
299
|
+
resolve();
|
|
300
|
+
},
|
|
301
|
+
onError(error) {
|
|
302
|
+
logSSRError("RENDER", error);
|
|
303
|
+
safeCall(onRenderError, {
|
|
304
|
+
req,
|
|
305
|
+
res,
|
|
306
|
+
store,
|
|
307
|
+
error
|
|
308
|
+
});
|
|
309
|
+
cleanupSafeArea();
|
|
310
|
+
tail.destroy(error);
|
|
311
|
+
reject(error);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
} catch (error) {
|
|
316
|
+
cleanupSafeArea();
|
|
317
|
+
logSSRError("RENDER", error);
|
|
318
|
+
safeCall(onRenderError, {
|
|
319
|
+
req,
|
|
320
|
+
res,
|
|
321
|
+
store,
|
|
322
|
+
error
|
|
323
|
+
});
|
|
324
|
+
return Promise.reject(error);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
const tracedRenderMarkUp = withObservability(SSR_SERVICE, _renderMarkUp, "renderMarkUp");
|
|
328
|
+
const tracedAppServerSideFunction = withObservability(SSR_SERVICE, args => App.serverSideFunction(args), "App.serverSideFunction");
|
|
329
|
+
const tracedServerDataFetcher = withObservability(SSR_SERVICE, serverDataFetcher, "serverDataFetcher");
|
|
330
|
+
const tracedGetMetaData = withSyncObservability(SSR_SERVICE, getMetaData, "getMetaData");
|
|
326
331
|
|
|
327
332
|
// ── Express middleware ──────────────────────────────────────────────────
|
|
328
333
|
/**
|
|
@@ -337,115 +342,107 @@ const tracedGetMetaData = withSyncObservability(SSR_SERVICE, getMetaData, "getMe
|
|
|
337
342
|
* (e.g. a redirect), we bail out early without attempting another render.
|
|
338
343
|
*/
|
|
339
344
|
async function _handler(req, res) {
|
|
345
|
+
try {
|
|
346
|
+
let context = {};
|
|
347
|
+
let fetcherData = {};
|
|
348
|
+
// A missing / non-function configureStore is not recoverable per
|
|
349
|
+
// request: every downstream step (serverSideFunction, data fetchers,
|
|
350
|
+
// render) assumes a real store. Log the coded error and fail the
|
|
351
|
+
// request with a 500 rather than rendering with `store = null`, which
|
|
352
|
+
// surfaces as a confusing render-time crash further down.
|
|
353
|
+
const configureStoreErr = validateConfigureStore(createStore);
|
|
354
|
+
if (configureStoreErr) {
|
|
355
|
+
handleError(configureStoreErr);
|
|
356
|
+
if (!res.headersSent) res.status(500).send("Internal Server Error");
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const store = await createStore({}, req, res);
|
|
360
|
+
const cachedRoutes = getCachedRoutes();
|
|
361
|
+
const allMatches = cachedRoutes ? NestedMatchRoutes(cachedRoutes, req.originalUrl) || [] : [];
|
|
362
|
+
let allTags = [];
|
|
363
|
+
safeCall(onRouteMatch, {
|
|
364
|
+
req,
|
|
365
|
+
res,
|
|
366
|
+
matches: allMatches,
|
|
367
|
+
store
|
|
368
|
+
});
|
|
369
|
+
if (res.headersSent) return;
|
|
340
370
|
try {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
store,
|
|
384
|
-
allMatches,
|
|
385
|
-
context,
|
|
386
|
-
chunkExtractor
|
|
387
|
-
)
|
|
388
|
-
} else {
|
|
389
|
-
safeCall(onFetcherSuccess, { req, res, store })
|
|
390
|
-
|
|
391
|
-
if (res.headersSent) return
|
|
392
|
-
|
|
393
|
-
await tracedRenderMarkUp(
|
|
394
|
-
null,
|
|
395
|
-
req,
|
|
396
|
-
res,
|
|
397
|
-
allTags,
|
|
398
|
-
fetcherData,
|
|
399
|
-
store,
|
|
400
|
-
allMatches,
|
|
401
|
-
context,
|
|
402
|
-
chunkExtractor
|
|
403
|
-
)
|
|
404
|
-
}
|
|
405
|
-
} catch (error) {
|
|
406
|
-
console.error("Error in executing serverFetcher functions:", error)
|
|
407
|
-
safeCall(onFetcherError, { req, res, store, error })
|
|
408
|
-
|
|
409
|
-
if (res.headersSent) return
|
|
410
|
-
|
|
411
|
-
const chunkExtractor = collectAssets(req, allMatches)
|
|
412
|
-
await tracedRenderMarkUp(
|
|
413
|
-
404,
|
|
414
|
-
req,
|
|
415
|
-
res,
|
|
416
|
-
allTags,
|
|
417
|
-
fetcherData,
|
|
418
|
-
store,
|
|
419
|
-
allMatches,
|
|
420
|
-
context,
|
|
421
|
-
chunkExtractor
|
|
422
|
-
)
|
|
423
|
-
}
|
|
424
|
-
} catch (error) {
|
|
425
|
-
console.error("Error in executing serverSideFunction inside App:", error)
|
|
426
|
-
safeCall(onAppServerSideError, { req, res, store, error })
|
|
427
|
-
|
|
428
|
-
if (res.headersSent) return
|
|
429
|
-
|
|
430
|
-
const chunkExtractor = collectAssets(req, allMatches)
|
|
431
|
-
await tracedRenderMarkUp(
|
|
432
|
-
error.status_code,
|
|
433
|
-
req,
|
|
434
|
-
res,
|
|
435
|
-
allTags,
|
|
436
|
-
fetcherData,
|
|
437
|
-
store,
|
|
438
|
-
allMatches,
|
|
439
|
-
context,
|
|
440
|
-
chunkExtractor
|
|
441
|
-
)
|
|
371
|
+
await tracedAppServerSideFunction({
|
|
372
|
+
store,
|
|
373
|
+
req,
|
|
374
|
+
res
|
|
375
|
+
});
|
|
376
|
+
safeCall(onAppServerSideSuccess, {
|
|
377
|
+
req,
|
|
378
|
+
res,
|
|
379
|
+
store
|
|
380
|
+
});
|
|
381
|
+
if (res.headersSent) return;
|
|
382
|
+
try {
|
|
383
|
+
fetcherData = await tracedServerDataFetcher({
|
|
384
|
+
routes: cachedRoutes,
|
|
385
|
+
req,
|
|
386
|
+
res,
|
|
387
|
+
url: req.originalUrl
|
|
388
|
+
}, {
|
|
389
|
+
store
|
|
390
|
+
});
|
|
391
|
+
if (res.headersSent) return;
|
|
392
|
+
const err = fetcherData?.[req.originalUrl]?.error;
|
|
393
|
+
allTags = tracedGetMetaData(allMatches, fetcherData);
|
|
394
|
+
const chunkExtractor = collectAssets(req, allMatches);
|
|
395
|
+
if (err) {
|
|
396
|
+
safeCall(onFetcherError, {
|
|
397
|
+
req,
|
|
398
|
+
res,
|
|
399
|
+
store,
|
|
400
|
+
error: err
|
|
401
|
+
});
|
|
402
|
+
if (res.headersSent) return;
|
|
403
|
+
const statusCode = err.status_code || 404;
|
|
404
|
+
await tracedRenderMarkUp(statusCode, req, res, allTags, fetcherData, store, allMatches, context, chunkExtractor);
|
|
405
|
+
} else {
|
|
406
|
+
safeCall(onFetcherSuccess, {
|
|
407
|
+
req,
|
|
408
|
+
res,
|
|
409
|
+
store
|
|
410
|
+
});
|
|
411
|
+
if (res.headersSent) return;
|
|
412
|
+
await tracedRenderMarkUp(null, req, res, allTags, fetcherData, store, allMatches, context, chunkExtractor);
|
|
442
413
|
}
|
|
414
|
+
} catch (error) {
|
|
415
|
+
logSSRError("FETCHER", error);
|
|
416
|
+
safeCall(onFetcherError, {
|
|
417
|
+
req,
|
|
418
|
+
res,
|
|
419
|
+
store,
|
|
420
|
+
error
|
|
421
|
+
});
|
|
422
|
+
if (res.headersSent) return;
|
|
423
|
+
const chunkExtractor = collectAssets(req, allMatches);
|
|
424
|
+
await tracedRenderMarkUp(404, req, res, allTags, fetcherData, store, allMatches, context, chunkExtractor);
|
|
425
|
+
}
|
|
443
426
|
} catch (error) {
|
|
444
|
-
|
|
445
|
-
|
|
427
|
+
logSSRError("SERVER_SIDE_FUNCTION", error);
|
|
428
|
+
safeCall(onAppServerSideError, {
|
|
429
|
+
req,
|
|
430
|
+
res,
|
|
431
|
+
store,
|
|
432
|
+
error
|
|
433
|
+
});
|
|
434
|
+
if (res.headersSent) return;
|
|
435
|
+
const chunkExtractor = collectAssets(req, allMatches);
|
|
436
|
+
await tracedRenderMarkUp(error.status_code, req, res, allTags, fetcherData, store, allMatches, context, chunkExtractor);
|
|
446
437
|
}
|
|
438
|
+
} catch (error) {
|
|
439
|
+
logSSRError("REQUEST_HANDLING", error);
|
|
440
|
+
safeCall(onRequestError, {
|
|
441
|
+
req,
|
|
442
|
+
res,
|
|
443
|
+
error
|
|
444
|
+
});
|
|
445
|
+
}
|
|
447
446
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
export default handler
|
|
447
|
+
const handler = withObservability(SSR_SERVICE, _handler, "handler");
|
|
448
|
+
export default handler;
|