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.
Files changed (316) hide show
  1. package/changelog.md +185 -71
  2. package/dist/errors/generateDocs.js +100 -0
  3. package/dist/errors/index.js +243 -0
  4. package/dist/errors/registry.js +483 -0
  5. package/dist/errors-index.json +506 -0
  6. package/dist/globals.d.ts +62 -0
  7. package/dist/index.d.ts +5 -0
  8. package/dist/index.jsx +5 -8
  9. package/dist/logger.js +1 -1
  10. package/dist/native/androidProject/app/build.gradle.kts +142 -7
  11. package/dist/native/androidProject/app/src/main/AndroidManifest.xml +4 -0
  12. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +2 -2
  13. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +3 -0
  14. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +3 -0
  15. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +43 -8
  16. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeUtilsTest.kt +181 -1
  17. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CameraUtilsTest.kt +156 -236
  18. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileSizeRouterUtilsTest.kt +471 -0
  19. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileUtilsTest.kt +222 -226
  20. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FrameworkServerUtilsTest.kt +300 -0
  21. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/IntentUtilsTest.kt +244 -0
  22. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/MetricsMonitorTest.kt +359 -0
  23. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NativeBridgeTest.kt +108 -63
  24. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NotificationUtilsTest.kt +329 -385
  25. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/OfflineCacheServiceTest.kt +302 -0
  26. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/PerfEventBufferTest.kt +133 -0
  27. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/UtilsKtTest.kt +152 -0
  28. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/WebCacheManagerTest.kt +145 -324
  29. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/BarcodeDetectorTest.kt +132 -0
  30. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/HoldControllerTest.kt +155 -0
  31. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/TorchControllerTest.kt +150 -0
  32. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateMachineTest.kt +120 -0
  33. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateTest.kt +62 -0
  34. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ViewfinderMapperTest.kt +143 -0
  35. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ZoomControllerTest.kt +421 -0
  36. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/design/DesignTokensTest.kt +207 -0
  37. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +368 -0
  38. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/FridaDetectorTest.kt +193 -0
  39. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckManagerTest.kt +260 -0
  40. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckSchedulerTest.kt +166 -0
  41. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DeviceInfoUtilsTest.kt +167 -0
  42. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DownloadUtilsTest.kt +197 -0
  43. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/NetworkMonitorTest.kt +271 -0
  44. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/PushNotificationUtilsTest.kt +119 -0
  45. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/SafeAreaUtilsTest.kt +108 -0
  46. package/dist/native/androidProject/build.gradle.kts +1 -0
  47. package/dist/native/androidProject/gradle/libs.versions.toml +26 -0
  48. package/dist/native/androidProject/settings.gradle.kts +11 -8
  49. package/dist/native/androidSetup.js +2 -2
  50. package/dist/native/bridge/WebBridge.d.ts +98 -0
  51. package/dist/native/bridge/WebBridge.js +40 -6
  52. package/dist/native/bridge/constants/NativeInterfaces.d.ts +391 -0
  53. package/dist/native/bridge/errors.d.ts +67 -0
  54. package/dist/native/bridge/errors.js +66 -151
  55. package/dist/native/bridge/hooks/index.d.ts +13 -0
  56. package/dist/native/bridge/hooks/index.js +0 -6
  57. package/dist/native/bridge/hooks/useAI.d.ts +1 -0
  58. package/dist/native/bridge/hooks/useAI.js +3 -1
  59. package/dist/native/bridge/hooks/useCamera.d.ts +86 -0
  60. package/dist/native/bridge/hooks/useCamera.js +35 -6
  61. package/dist/native/bridge/hooks/useDataProtection.d.ts +17 -0
  62. package/dist/native/bridge/hooks/useDeviceInfo.d.ts +34 -0
  63. package/dist/native/bridge/hooks/useDeviceInfo.js +5 -0
  64. package/dist/native/bridge/hooks/useFilePicker.d.ts +53 -0
  65. package/dist/native/bridge/hooks/useFilePicker.js +13 -0
  66. package/dist/native/bridge/hooks/useGoogleSignIn.d.ts +12 -0
  67. package/dist/native/bridge/hooks/useHapticFeedback.d.ts +76 -0
  68. package/dist/native/bridge/hooks/useHapticFeedback.js +6 -1
  69. package/dist/native/bridge/hooks/useIntent.d.ts +33 -0
  70. package/dist/native/bridge/hooks/useIntent.js +2 -0
  71. package/dist/native/bridge/hooks/useNativeTransition.d.ts +53 -0
  72. package/dist/native/bridge/hooks/useNativeTransition.js +12 -5
  73. package/dist/native/bridge/hooks/useNetworkStatus.d.ts +22 -0
  74. package/dist/native/bridge/hooks/useNetworkStatus.js +7 -0
  75. package/dist/native/bridge/hooks/useNotification.d.ts +83 -0
  76. package/dist/native/bridge/hooks/useNotification.js +81 -34
  77. package/dist/native/bridge/hooks/useSafeArea.d.ts +12 -0
  78. package/dist/native/bridge/hooks/useSafeArea.js +8 -0
  79. package/dist/native/bridge/hooks/useVideoStream.d.ts +67 -0
  80. package/dist/native/bridge/hooks/useVideoStream.js +38 -52
  81. package/dist/native/bridge/hooks/utils.d.ts +4 -0
  82. package/dist/native/bridge/hooks.d.ts +1 -0
  83. package/dist/native/bridge/perf/collectors/ApiCollector.d.ts +30 -0
  84. package/dist/native/bridge/perf/collectors/ApiCollector.js +4 -0
  85. package/dist/native/bridge/perf/collectors/CacheCollector.d.ts +43 -0
  86. package/dist/native/bridge/perf/collectors/CacheCollector.js +4 -0
  87. package/dist/native/bridge/perf/collectors/HookCollector.d.ts +37 -0
  88. package/dist/native/bridge/perf/collectors/HookCollector.js +4 -0
  89. package/dist/native/bridge/perf/collectors/InsightsCollector.d.ts +103 -0
  90. package/dist/native/bridge/perf/collectors/InsightsCollector.js +4 -0
  91. package/dist/native/bridge/perf/collectors/InteractionCollector.d.ts +53 -0
  92. package/dist/native/bridge/perf/collectors/InteractionCollector.js +4 -0
  93. package/dist/native/bridge/perf/collectors/KeyboardCollector.d.ts +30 -0
  94. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +4 -0
  95. package/dist/native/bridge/perf/collectors/NavigationCollector.d.ts +37 -0
  96. package/dist/native/bridge/perf/collectors/NavigationCollector.js +4 -0
  97. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.d.ts +30 -0
  98. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +4 -0
  99. package/dist/native/bridge/perf/collectors/RenderCollector.d.ts +47 -0
  100. package/dist/native/bridge/perf/collectors/RenderCollector.js +8 -0
  101. package/dist/native/bridge/perf/collectors/ScrollCollector.d.ts +29 -0
  102. package/dist/native/bridge/perf/collectors/ScrollCollector.js +4 -0
  103. package/dist/native/bridge/perf/collectors/ThreadCollector.d.ts +31 -0
  104. package/dist/native/bridge/perf/collectors/ThreadCollector.js +4 -0
  105. package/dist/native/bridge/perf/core/constants.d.ts +76 -0
  106. package/dist/native/bridge/perf/core/measure.d.ts +39 -0
  107. package/dist/native/bridge/perf/core/measure.js +4 -0
  108. package/dist/native/bridge/perf/core/session.d.ts +49 -0
  109. package/dist/native/bridge/perf/core/session.js +4 -0
  110. package/dist/native/bridge/perf/core/store.d.ts +37 -0
  111. package/dist/native/bridge/perf/core/store.js +4 -0
  112. package/dist/native/bridge/perf/core/summary.d.ts +60 -0
  113. package/dist/native/bridge/perf/core/traceExport.d.ts +10 -0
  114. package/dist/native/bridge/perf/core/waterfalls.d.ts +2 -0
  115. package/dist/native/bridge/perf/index.d.ts +63 -0
  116. package/dist/native/bridge/perf/index.js +4 -0
  117. package/dist/native/bridge/safeArea.d.ts +11 -0
  118. package/dist/native/bridge/safeArea.js +2 -0
  119. package/dist/native/bridge/useBaseHook.d.ts +137 -0
  120. package/dist/native/bridge/useBaseHook.js +34 -4
  121. package/dist/native/bridge/utils/CameraUtils.d.ts +94 -0
  122. package/dist/native/bridge/utils/CameraUtils.js +4 -0
  123. package/dist/native/bridge/utils/FileObjectConverter.d.ts +36 -0
  124. package/dist/native/bridge/utils/NativeBridge.d.ts +270 -0
  125. package/dist/native/bridge/utils/NativeBridge.js +10 -0
  126. package/dist/native/buildAndroid/index.js +1 -1
  127. package/dist/native/buildAppAndroid.js +9 -0
  128. package/dist/native/buildAppIos.js +16 -1
  129. package/dist/native/buildErrorFormat.js +57 -0
  130. package/dist/native/buildIos/config.js +15 -1
  131. package/dist/native/buildIos/index.js +1 -1
  132. package/dist/native/buildIos/plugins.js +10 -1
  133. package/dist/native/iosnativeWebView/CoreLogic/Package.swift +36 -0
  134. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamState.swift +1 -1
  135. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamStateMachine.swift +6 -4
  136. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/BootTimingUtility.swift +3 -3
  137. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/FrameworkServerUtils.swift +14 -9
  138. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NetworkMonitor.swift +11 -9
  139. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NotificationHandlerProvider.swift +1 -1
  140. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/URLWhitelistManager.swift +7 -7
  141. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/AnyCodableTests.swift +154 -0
  142. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/BootTimingUtilityTests.swift +23 -5
  143. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/CatalystConstantsTests.swift +192 -0
  144. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/ConfigMappingTests.swift +1 -1
  145. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsErrorPathTests.swift +106 -0
  146. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsLoopbackTests.swift +243 -0
  147. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/FrameworkServerUtilsTests.swift +1 -1
  148. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NetworkMonitorTests.swift +119 -0
  149. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NotificationHandlerProviderTests.swift +59 -0
  150. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/NotificationHandlerTests.swift +1 -1
  151. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/URLWhitelistManagerTests.swift +1 -1
  152. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateMachineTests.swift +171 -0
  153. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateTests.swift +91 -0
  154. package/dist/native/iosnativeWebView/Package.swift +10 -9
  155. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/LocalNotificationHandler.swift +7 -2
  156. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/NotificationManager.swift +1 -2
  157. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/PushNotificationHandler.swift +5 -4
  158. package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +1 -0
  159. package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +1 -0
  160. package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +6 -5
  161. package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +1 -0
  162. package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +1 -0
  163. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +31 -30
  164. package/dist/native/iosnativeWebView/Sources/Core/Utils/CameraTransportUtils.swift +2 -1
  165. package/dist/native/iosnativeWebView/Sources/Core/Utils/DeviceInfoUtils.swift +1 -1
  166. package/dist/native/iosnativeWebView/Sources/Core/Utils/FilePickerHandler.swift +22 -19
  167. package/dist/native/iosnativeWebView/Sources/Core/Utils/ImageHandler.swift +40 -39
  168. package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +1 -0
  169. package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +15 -15
  170. package/dist/native/iosnativeWebView/Sources/Core/Utils/WebKitConfig.swift +4 -3
  171. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +6 -1
  172. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeDelegateHandler.swift +1 -0
  173. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +2 -1
  174. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeJavaScriptInterface.swift +2 -1
  175. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +3 -5
  176. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +20 -13
  177. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +6 -5
  178. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +14 -13
  179. package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +3 -3
  180. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +9 -8
  181. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +7 -6
  182. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +24 -25
  183. package/dist/native/iosnativeWebView/iosnativeWebView/AppDelegate.swift +6 -1
  184. package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +1 -0
  185. package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +4 -0
  186. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +2 -22
  187. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +1 -0
  188. package/dist/native/iosnativeWebView/iosnativeWebViewTests/CacheManagerTests.swift +1 -0
  189. package/dist/native/iosnativeWebView/iosnativeWebViewTests/FilePickerHandlerTests.swift +2 -1
  190. package/dist/native/iosnativeWebView/iosnativeWebViewTests/NativeBridgeTests.swift +15 -9
  191. package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +1 -0
  192. package/dist/native/setupEmulatorIos.js +2 -2
  193. package/dist/native/terminalProgress.js +17 -17
  194. package/dist/router/ClientRouter.d.ts +8 -0
  195. package/dist/router/ClientRouter.js +13 -11
  196. package/dist/router/ServerRouter.d.ts +13 -0
  197. package/dist/router/ServerRouter.js +19 -7
  198. package/dist/scripts/build.js +14 -2
  199. package/dist/scripts/loadEnvironmentVariables.js +3 -2
  200. package/dist/scripts/plugins.js +1 -1
  201. package/dist/scripts/preServerInit.js +49 -3
  202. package/dist/scripts/preflight.js +111 -0
  203. package/dist/scripts/scriptUtils.js +46 -1
  204. package/dist/scripts/serve.js +7 -3
  205. package/dist/scripts/start.js +8 -3
  206. package/dist/server/expressServer.d.ts +1 -0
  207. package/dist/server/expressServer.js +247 -271
  208. package/dist/server/manifestCache.d.ts +2 -0
  209. package/dist/server/manifestCache.js +25 -33
  210. package/dist/server/renderer/ChunkExtractor.d.ts +44 -0
  211. package/dist/server/renderer/ChunkExtractor.js +113 -125
  212. package/dist/server/renderer/document/Body.d.ts +21 -0
  213. package/dist/server/renderer/document/Body.jsx +45 -45
  214. package/dist/server/renderer/document/FastRefresh.d.ts +3 -0
  215. package/dist/server/renderer/document/FastRefresh.jsx +7 -13
  216. package/dist/server/renderer/document/Head.d.ts +20 -0
  217. package/dist/server/renderer/document/Head.jsx +32 -32
  218. package/dist/server/renderer/document/index.d.ts +2 -0
  219. package/dist/server/renderer/document/index.jsx +2 -2
  220. package/dist/server/renderer/extract.d.ts +67 -0
  221. package/dist/server/renderer/extract.js +92 -89
  222. package/dist/server/renderer/handler.d.ts +2 -0
  223. package/dist/server/renderer/handler.jsx +401 -404
  224. package/dist/server/renderer/index.d.ts +2 -0
  225. package/dist/server/renderer/index.js +5 -8
  226. package/dist/server/renderer/render.d.ts +35 -0
  227. package/dist/server/renderer/render.js +25 -32
  228. package/dist/server/utils/botDetectionMiddleware.d.ts +1 -0
  229. package/dist/server/utils/botDetectionMiddleware.js +33 -23
  230. package/dist/server/utils/cjsRequire.d.ts +8 -0
  231. package/dist/server/utils/cjsRequire.js +2 -2
  232. package/dist/server/utils/userAgentUtil.d.ts +7 -0
  233. package/dist/server/utils/userAgentUtil.js +68 -71
  234. package/dist/server/utils/validator.d.ts +31 -0
  235. package/dist/server/utils/validator.js +133 -158
  236. package/dist/vite/inject-cache-key-plugin.js +3 -27
  237. package/dist/vite/moduleAliases.js +36 -0
  238. package/dist/vite/node-loader.mjs +2 -29
  239. package/dist/vite/resolveDevServerConfig.js +4 -1
  240. package/dist/vite/vite.config.js +2 -29
  241. package/dist/web-router/components/MetaTag.d.ts +4 -0
  242. package/dist/web-router/components/MetaTag.jsx +26 -28
  243. package/dist/web-router/components/MetaTag.test.jsx +100 -0
  244. package/dist/web-router/components/RouterDataProvider.d.ts +96 -0
  245. package/dist/web-router/components/RouterDataProvider.jsx +260 -259
  246. package/dist/web-router/components/RouterDataProvider.server.test.jsx +98 -0
  247. package/dist/web-router/components/RouterDataProvider.test.jsx +310 -0
  248. package/dist/web-router/components/Split.d.ts +74 -0
  249. package/dist/web-router/components/Split.jsx +147 -156
  250. package/dist/web-router/components/Split.server.test.jsx +52 -0
  251. package/dist/web-router/components/Split.test.jsx +167 -0
  252. package/dist/web-router/components/SplitInview.d.ts +22 -0
  253. package/dist/web-router/components/SplitInview.jsx +78 -89
  254. package/dist/web-router/components/SplitInview.test.jsx +112 -0
  255. package/dist/web-router/components/SsrRequestContext.d.ts +20 -0
  256. package/dist/web-router/components/SsrRequestContext.jsx +17 -7
  257. package/dist/web-router/components/SsrRequestContext.test.jsx +53 -0
  258. package/dist/web-router/context.d.ts +15 -0
  259. package/dist/web-router/context.jsx +5 -5
  260. package/dist/web-router/context.test.jsx +10 -0
  261. package/dist/web-router/utils/metaDataUtils.d.ts +24 -0
  262. package/dist/web-router/utils/metaDataUtils.jsx +86 -86
  263. package/dist/web-router/utils/metaDataUtils.test.jsx +188 -0
  264. package/dist/web-router/vitest.setup.js +12 -0
  265. package/mcp_v2/knowledge-base.json +115 -17
  266. package/mcp_v2/mcp.js +141 -3
  267. package/mcp_v2/package-lock.json +2031 -0
  268. package/mcp_v2/package.json +13 -2
  269. package/mcp_v2/scripts/copyErrorsIndex.cjs +34 -0
  270. package/mcp_v2/test/errors.test.ts +123 -0
  271. package/mcp_v2/test/github.test.ts +177 -0
  272. package/mcp_v2/tools/errors.d.ts +20 -0
  273. package/mcp_v2/tools/errors.js +63 -0
  274. package/mcp_v2/tools/github.d.ts +41 -0
  275. package/mcp_v2/tools/github.js +231 -0
  276. package/mcp_v2/vitest.config.mjs +22 -0
  277. package/package.json +60 -11
  278. package/test/errors/docsDrift.test.ts +113 -0
  279. package/test/errors/index.test.ts +200 -0
  280. package/test/errors/registry.test.ts +108 -0
  281. package/test/scripts/preflight.test.ts +118 -0
  282. package/test/server/ChunkExtractor.test.ts +97 -0
  283. package/test/server/extract.test.ts +128 -0
  284. package/test/server/fixtures/template/server/document.jsx +19 -0
  285. package/test/server/fixtures/template/server/index.js +10 -0
  286. package/test/server/fixtures/template/src/js/containers/App/index.jsx +33 -0
  287. package/test/server/fixtures/template/src/js/routes/index.jsx +72 -0
  288. package/test/server/fixtures/template/src/js/routes/utils.jsx +39 -0
  289. package/test/server/fixtures/template/src/js/store/index.js +19 -0
  290. package/test/server/handler.assets.test.ts +330 -0
  291. package/test/server/handler.test.ts +208 -0
  292. package/test/server/manifestCache.test.ts +71 -0
  293. package/test/server/render.test.ts +71 -0
  294. package/test/server/scriptUtils.test.ts +103 -0
  295. package/test/server/userAgentUtil.test.ts +48 -0
  296. package/test/server/validator.test.ts +225 -0
  297. package/test/vite/resolveDevServerConfig.test.js +4 -1
  298. package/tsconfig.test.json +23 -0
  299. package/vitest.config.ts +93 -0
  300. package/dist/caching.js +0 -1
  301. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/DeviceInfoUtilsTest.kt +0 -187
  302. package/dist/native/bridge/standardizedHooks.js +0 -611
  303. package/dist/router.js +0 -1
  304. package/dist/scripts/validator.js +0 -149
  305. package/dist/web-router/hooks.jsx +0 -72
  306. package/dist/web-router/types/components/MetaTag.d.ts +0 -2
  307. package/dist/web-router/types/components/RouterDataProvider.d.ts +0 -95
  308. package/dist/web-router/types/context.d.ts +0 -14
  309. package/dist/web-router/types/hooks.d.ts +0 -1
  310. package/dist/web-router/types/index.d.ts +0 -3
  311. package/dist/web-router/types/utils/metaDataUtils.d.ts +0 -40
  312. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/CatalystConstants.swift +0 -0
  313. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/ConfigConstants.swift +0 -0
  314. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Notifications/NotificationModels.swift +0 -0
  315. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Protocols/NotificationHandlerProtocol.swift +0 -0
  316. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NullNotificationHandler.swift +0 -0
@@ -1,328 +1,333 @@
1
- import React from "react"
2
- import { renderStart, renderEnd } from "./render.js"
3
- import { Provider } from "react-redux"
4
- import { Body } from "./document/Body.jsx"
5
- import { Head } from "./document/Head.jsx"
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, matchRoutes as NestedMatchRoutes, getMetaData } from "../../index.jsx"
12
- import { validateConfigureStore, validateGetRoutes, safeCall } from "../utils/validator.js"
13
- import { ChunkExtractor } from "./ChunkExtractor.js"
14
- import {
15
- readCssFromDisk,
16
- generateScriptElements,
17
- generateCssLinkStrings,
18
- generateScriptStrings,
19
- getDeferredRouteKey,
20
- getCachedDeferredCssPathsForRoute,
21
- registerDeferredAssetsForRoute,
22
- getDeferredPreloadScriptUrls,
23
- generateModulePreloadLinkElements,
24
- } from "./extract.js"
25
- import path from "path"
26
- import { Transform } from "node:stream"
27
-
28
- import CustomDocument from "@catalyst/template/server/document"
29
-
30
- import App from "@catalyst/template/src/js/containers/App/index"
31
- import { getRoutes } from "@catalyst/template/src/js/routes/utils"
32
- import createStore from "@catalyst/template/src/js/store/index.js"
33
- import { SsrRequestProvider } from "../../web-router/components/SsrRequestContext.jsx"
34
- import { getManifest, getAssetManifest } from "../manifestCache.js"
35
-
36
- const DEFAULT_SAFE_AREA_INSETS = { top: 0, right: 0, bottom: 0, left: 0 }
37
-
38
- const parseSafeAreaFromHeaders = (req) => {
39
- const readEdge = (header) => {
40
- const raw = req.get(header) ?? req.headers[header.toLowerCase()]
41
- const value = Number(raw)
42
- return Number.isFinite(value) && value >= 0 ? value : null
43
- }
44
-
45
- const top = readEdge("X-Safe-Area-Top")
46
- const right = readEdge("X-Safe-Area-Right")
47
- const bottom = readEdge("X-Safe-Area-Bottom")
48
- const left = readEdge("X-Safe-Area-Left")
49
-
50
- if (![top, right, bottom, left].some((value) => value !== null)) return null
51
-
52
- return {
53
- top: top ?? 0,
54
- right: right ?? 0,
55
- bottom: bottom ?? 0,
56
- left: left ?? 0,
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
- if (_cachedRoutes === undefined) {
65
- _cachedRoutes = validateGetRoutes(getRoutes) ? getRoutes() : null
66
- }
67
- return _cachedRoutes
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
- const hooks = await import("@catalyst/template/server/index.js")
81
- _onRouteMatch = hooks.onRouteMatch
82
- _onFetcherSuccess = hooks.onFetcherSuccess
83
- _onFetcherError = hooks.onFetcherError
84
- _onAppServerSideSuccess = hooks.onAppServerSideSuccess
85
- _onAppServerSideError = hooks.onAppServerSideError
86
- _onRenderError = hooks.onRenderError
87
- _onRequestError = hooks.onRequestError
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
- // No hooks file — all hooks remain undefined, safeCall will skip them
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
- try {
98
- const otel = await import("../../otel.js")
99
- withObservability = otel.withObservability
100
- withSyncObservability = otel.withSyncObservability
101
- } catch {
102
- // otel packages not installed — continue without tracing
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 SSR_SERVICE = process.env.SERVICE_NAME || `pwa-${process.env.APPLICATION}-node-server`
107
-
108
- const traceHook = (fn, spanName) =>
109
- typeof fn === "function" ? withSyncObservability(SSR_SERVICE, fn, spanName) : fn
110
-
111
- const onRouteMatch = traceHook(_onRouteMatch, "onRouteMatch")
112
- const onFetcherSuccess = traceHook(_onFetcherSuccess, "onFetcherSuccess")
113
- const onFetcherError = traceHook(_onFetcherError, "onFetcherError")
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
- const chunkExtractor = new ChunkExtractor({
122
- manifest: getManifest() || {},
123
- assetManifest: getAssetManifest() || {},
124
- })
130
+ const chunkExtractor = new ChunkExtractor({
131
+ manifest: getManifest() || {},
132
+ assetManifest: getAssetManifest() || {}
133
+ });
125
134
 
126
- // Add route-matched CSS/JS to critical bucket (loaded in <head>)
127
- chunkExtractor.preloadRouteCss(allMatches)
128
-
129
- return chunkExtractor
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
- const getComponent = (store, context, req, fetcherData, isBot) => (
136
- <div id="app">
137
- <SsrRequestProvider value={{ isBot }}>
138
- <Provider store={store}>
139
- <StaticRouter context={context} location={req.originalUrl}>
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
- </StaticRouter>
142
- </Provider>
143
- </SsrRequestProvider>
144
- </div>
145
- )
163
+ </StaticRouterAny>
164
+ </ProviderAny>
165
+ </SsrRequestProviderAny>
166
+ </div>;
146
167
 
147
168
  // ── Render and stream ──────────────────────────────────────────────────
148
- const _renderMarkUp = async (
149
- errorCode,
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
- metaTags,
153
- fetcherData,
154
- store,
155
- allMatches,
156
- context,
157
- chunkExtractor
158
- ) => {
159
- const deviceDetails = getUserAgentDetails(req.headers["user-agent"] || "")
160
- // Match mweb's wider definition: synthetic monitors (StatusCake) and AI crawlers
161
- // also need the bot code path — no JS, and split(... ssr: false ...) widgets that
162
- // previously read state.shellReducer.isBot now read this via SsrRequestContext.
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
- // Critical assets → <head>
177
- const criticalAssets = chunkExtractor ? chunkExtractor.getCriticalAssets() : { js: [], css: [] }
178
-
179
- // Inline critical CSS from disk (small thanks to natural code-splitting)
180
- const buildDir = path.join(process.env.src_path, process.env.BUILD_OUTPUT_PATH || "build")
181
- const inlineCss = readCssFromDisk(criticalAssets.css, buildDir)
182
-
183
- const deferredRouteKey = getDeferredRouteKey(req, allMatches)
184
- const deferredRouteInlineCss = readCssFromDisk(
185
- getCachedDeferredCssPathsForRoute(deferredRouteKey),
186
- buildDir
187
- )
188
-
189
- const jsScripts = generateScriptElements(criticalAssets.js)
190
- const criticalPreloadLinks = generateModulePreloadLinkElements(criticalAssets.js, "critical-js")
191
- const deferredPreloadUrls = getDeferredPreloadScriptUrls(deferredRouteKey, criticalAssets.js)
192
- const deferredPreloadLinks = generateModulePreloadLinkElements(deferredPreloadUrls, "deferred-js")
193
-
194
- // Build Head props
195
- const shellStart = renderStart({
196
- inlineCss,
197
- deferredRouteInlineCss,
198
- jsScripts,
199
- criticalPreloadLinks,
200
- deferredPreloadLinks,
201
- metaTags,
202
- isBot,
203
- fetcherData,
204
- })
205
-
206
- const state = store.getState()
207
- const jsx = getComponent(store, context, req, fetcherData, isBot)
208
- const shellEnd = renderEnd(state, res, jsx, errorCode, fetcherData)
209
-
210
- const finalProps = { ...shellStart, ...shellEnd, jsx, req, res, safeArea }
211
-
212
- const CompleteDocument = () => {
213
- if (CustomDocument) {
214
- return CustomDocument(finalProps)
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
- return (
217
- <html lang={finalProps.lang}>
218
- <Head
219
- isBot={finalProps.isBot}
220
- inlineCss={finalProps.inlineCss}
221
- deferredRouteInlineCss={finalProps.deferredRouteInlineCss}
222
- jsScripts={finalProps.jsScripts}
223
- criticalPreloadLinks={finalProps.criticalPreloadLinks}
224
- deferredPreloadLinks={finalProps.deferredPreloadLinks}
225
- fetcherData={finalProps.fetcherData}
226
- metaTags={finalProps.metaTags}
227
- publicAssetPath={finalProps.publicAssetPath}
228
- />
229
- <Body
230
- initialState={finalProps.initialState}
231
- jsx={finalProps.jsx}
232
- statusCode={finalProps.statusCode}
233
- fetcherData={finalProps.fetcherData}
234
- safeArea={finalProps.safeArea}
235
- />
236
- </html>
237
- )
238
- }
239
-
240
- try {
241
- const status = errorCode || (allMatches.length && allMatches[0]?.route?.path === "*" ? 404 : 200)
242
- res.set({ "content-type": "text/html; charset=utf-8" })
243
- res.status(status)
244
-
245
- return new Promise((resolve, reject) => {
246
- // Single completion path: React's pipe() auto-ends `tail`, and
247
- // `flush()` appends the deferred asset tags before that end
248
- // signal propagates to `res` via the plain pipe below. This
249
- // avoids the onShellReady/onAllReady race where React's own
250
- // stream-end and a manual res.end() compete to close `res`
251
- // (see issue #320).
252
- const tail = new Transform({
253
- transform(chunk, _enc, cb) {
254
- cb(null, chunk)
255
- },
256
- flush(cb) {
257
- // Deferred assets — injected after body (non-blocking)
258
- const deferredAssets = chunkExtractor
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
- let context = {}
342
- let fetcherData = {}
343
- const store = validateConfigureStore(createStore) ? await createStore({}, req, res) : null
344
-
345
- const cachedRoutes = getCachedRoutes()
346
- const allMatches = cachedRoutes ? NestedMatchRoutes(cachedRoutes, req.originalUrl) || [] : []
347
- let allTags = []
348
-
349
- safeCall(onRouteMatch, { req, res, matches: allMatches, store })
350
-
351
- if (res.headersSent) return
352
-
353
- try {
354
- await tracedAppServerSideFunction({ store, req, res })
355
- safeCall(onAppServerSideSuccess, { req, res, store })
356
-
357
- if (res.headersSent) return
358
-
359
- try {
360
- fetcherData = await tracedServerDataFetcher(
361
- { routes: cachedRoutes, req, res, url: req.originalUrl },
362
- { store }
363
- )
364
-
365
- if (res.headersSent) return
366
-
367
- const err = fetcherData?.[req.originalUrl]?.error
368
- allTags = tracedGetMetaData(allMatches, fetcherData)
369
- const chunkExtractor = collectAssets(req, allMatches)
370
-
371
- if (err) {
372
- safeCall(onFetcherError, { req, res, store, error: err })
373
-
374
- if (res.headersSent) return
375
-
376
- const statusCode = err.status_code || 404
377
- await tracedRenderMarkUp(
378
- statusCode,
379
- req,
380
- res,
381
- allTags,
382
- fetcherData,
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
- console.error("Error in handling document request:", error)
445
- safeCall(onRequestError, { req, res, error })
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
- const handler = withObservability(SSR_SERVICE, _handler, "handler")
450
-
451
- export default handler
447
+ const handler = withObservability(SSR_SERVICE, _handler, "handler");
448
+ export default handler;