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
@@ -0,0 +1,471 @@
1
+ package io.yourname.androidproject
2
+
3
+ import android.app.Activity
4
+ import android.content.ContentResolver
5
+ import android.net.Uri
6
+ import android.util.Base64
7
+ import android.webkit.MimeTypeMap
8
+ import android.webkit.WebView
9
+ import io.yourname.androidproject.utils.FileSizeRouterUtils
10
+ import io.yourname.androidproject.utils.FrameworkServerUtils
11
+ import org.junit.After
12
+ import org.junit.Assert.*
13
+ import org.junit.Before
14
+ import org.junit.Test
15
+ import org.mockito.MockedStatic
16
+ import org.mockito.Mockito.mockStatic
17
+ import org.mockito.kotlin.any
18
+ import org.mockito.kotlin.doReturn
19
+ import org.mockito.kotlin.doThrow
20
+ import org.mockito.kotlin.mock
21
+ import kotlin.io.path.createTempDirectory
22
+
23
+ /**
24
+ * Unit tests for FileSizeRouterUtils, previously entirely untested (0%).
25
+ *
26
+ * determineTransport/getMimeType/formatFileSize operate on plain
27
+ * java.io.File and don't need a Context, so they're exercised directly
28
+ * against real temp files.
29
+ *
30
+ * processFile's three transport branches (added in a later pass):
31
+ *
32
+ * IMPORTANT, confirmed empirically: Mockito.mockStatic only intercepts
33
+ * genuine JVM static methods (Java classes like android.jar's Uri/
34
+ * Base64/MimeTypeMap). FileUtils/FrameworkServerUtils/IntentUtils are
35
+ * Kotlin `object`s — their "static-looking" members compile to instance
36
+ * methods on a singleton INSTANCE field, which mockStatic cannot
37
+ * intercept (attempting it throws MissingMethodInvocationException,
38
+ * since the real method runs instead of a mock). The PluginBridge
39
+ * coverage pass hit the identical wall with GeneratedPluginIndex. Real
40
+ * objects are used below instead, matching FrameworkServerUtilsTest's
41
+ * own established pattern (a real embedded server, not a mock):
42
+ * - processViaBridge: real FileUtils.convertUriToBase64 with a mocked
43
+ * Context/ContentResolver — Uri.fromFile and Base64.encodeToString are
44
+ * genuine android.jar statics, mockStatic'd normally.
45
+ * - processViaFrameworkServer: the real FrameworkServerUtils object,
46
+ * started/stopped exactly as FrameworkServerUtilsTest.kt does (shared
47
+ * JVM-wide singleton state — defensive stopServer() in both setUp and
48
+ * tearDown so this class can't leak state into/from that one
49
+ * depending on JVM test-class execution order).
50
+ * - processViaContentProvider: IntentUtils.createFileProviderUri needs
51
+ * real FileProvider manifest metadata to succeed, which isn't
52
+ * available in a JVM unit test — only its failure/exception paths
53
+ * (non-Activity context, and a real call that throws) are covered;
54
+ * the success path is Robolectric/instrumented-test territory.
55
+ */
56
+ class FileSizeRouterUtilsTest {
57
+
58
+ private lateinit var tempDir: java.io.File
59
+ private lateinit var mimeTypeMapMock: MockedStatic<MimeTypeMap>
60
+
61
+ @Before
62
+ fun setUp() {
63
+ tempDir = createTempDirectory(prefix = "catalyst-router-test").toFile()
64
+
65
+ val mimeTypeMap: MimeTypeMap = mock {
66
+ // Mockito's stub-matching is most-recent-wins for overlapping
67
+ // matchers — the any() catch-all must be stubbed first so the
68
+ // specific "txt"/"jpg" stubs below take precedence for those
69
+ // exact arguments.
70
+ on { getMimeTypeFromExtension(org.mockito.kotlin.any()) } doReturn null
71
+ on { getMimeTypeFromExtension("txt") } doReturn "text/plain"
72
+ on { getMimeTypeFromExtension("jpg") } doReturn "image/jpeg"
73
+ }
74
+ mimeTypeMapMock = mockStatic(MimeTypeMap::class.java)
75
+ mimeTypeMapMock.`when`<MimeTypeMap> { MimeTypeMap.getSingleton() } doReturn mimeTypeMap
76
+
77
+ // FrameworkServerUtils is a JVM-wide singleton object; stop it
78
+ // defensively before each test so this class can't inherit
79
+ // running-server state left by FrameworkServerUtilsTest (or any
80
+ // other test class) depending on JVM test-class execution order.
81
+ FrameworkServerUtils.stopServer()
82
+ }
83
+
84
+ @After
85
+ fun tearDown() {
86
+ mimeTypeMapMock.close()
87
+ FrameworkServerUtils.stopServer()
88
+ tempDir.deleteRecursively()
89
+ }
90
+
91
+ private fun fileOfSize(name: String, bytes: Int): java.io.File {
92
+ val file = java.io.File(tempDir, name)
93
+ file.writeBytes(ByteArray(bytes))
94
+ return file
95
+ }
96
+
97
+ // ============================================================
98
+ // determineTransport
99
+ // ============================================================
100
+
101
+ @Test
102
+ fun `determineTransport reports UNSUPPORTED and cannot proceed for a missing file`() {
103
+ val missing = java.io.File(tempDir, "does-not-exist.txt")
104
+
105
+ val decision = FileSizeRouterUtils.determineTransport(missing)
106
+
107
+ assertEquals(FileSizeRouterUtils.TransportType.UNSUPPORTED, decision.transportType)
108
+ assertFalse(decision.canProceed)
109
+ assertEquals("Selected file no longer exists", decision.errorMessage)
110
+ }
111
+
112
+ @Test
113
+ fun `determineTransport routes a small file to BRIDGE_BASE64`() {
114
+ val file = fileOfSize("small.txt", 1024) // 1 KB, well under 2MB
115
+
116
+ val decision = FileSizeRouterUtils.determineTransport(file)
117
+
118
+ assertEquals(FileSizeRouterUtils.TransportType.BRIDGE_BASE64, decision.transportType)
119
+ assertTrue(decision.canProceed)
120
+ assertEquals("text/plain", decision.mimeType)
121
+ assertEquals(1024L, decision.fileSize)
122
+ }
123
+
124
+ @Test
125
+ fun `determineTransport routes a file just over the bridge threshold to CONTENT_PROVIDER when server is not running`() {
126
+ val file = fileOfSize("medium.jpg", FileSizeRouterUtils.MAX_BRIDGE_SIZE + 1)
127
+
128
+ val decision = FileSizeRouterUtils.determineTransport(file)
129
+
130
+ // FrameworkServerUtils.isRunning() is false by default (no server
131
+ // started anywhere in this test), so the >2MB branch falls back to
132
+ // CONTENT_PROVIDER rather than FRAMEWORK_SERVER.
133
+ assertEquals(FileSizeRouterUtils.TransportType.CONTENT_PROVIDER, decision.transportType)
134
+ assertTrue(decision.canProceed)
135
+ }
136
+
137
+ @Test
138
+ fun `MAX_SERVER_SIZE boundary exceeds MAX_BRIDGE_SIZE and a small file still routes to BRIDGE_BASE64`() {
139
+ // Not a real >100MB UNSUPPORTED case (impractical to allocate that
140
+ // much on disk in a unit test) -- this only asserts the threshold
141
+ // ordering the UNSUPPORTED branch depends on, plus that a small
142
+ // file's routing stays correct/unaffected by that boundary's
143
+ // existence. Renamed from a name that claimed to test the
144
+ // >100MB UNSUPPORTED path, which it never actually exercised.
145
+ val file = fileOfSize("huge.bin", 100) // real size doesn't matter, but must exist
146
+ assertTrue(FileSizeRouterUtils.MAX_SERVER_SIZE > FileSizeRouterUtils.MAX_BRIDGE_SIZE)
147
+
148
+ val decision = FileSizeRouterUtils.determineTransport(file)
149
+ assertEquals(FileSizeRouterUtils.TransportType.BRIDGE_BASE64, decision.transportType)
150
+ }
151
+
152
+ // ============================================================
153
+ // getMimeType
154
+ // ============================================================
155
+
156
+ @Test
157
+ fun `getMimeType resolves via MimeTypeMap when the extension is known`() {
158
+ val file = fileOfSize("photo.jpg", 10)
159
+ assertEquals("image/jpeg", FileSizeRouterUtils.getMimeType(file))
160
+ }
161
+
162
+ @Test
163
+ fun `getMimeType falls back to the office-document table for docx`() {
164
+ val file = fileOfSize("report.docx", 10)
165
+ assertEquals(
166
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
167
+ FileSizeRouterUtils.getMimeType(file)
168
+ )
169
+ }
170
+
171
+ @Test
172
+ fun `getMimeType falls back to octet-stream for a fully unknown extension`() {
173
+ val file = fileOfSize("mystery.xyz123", 10)
174
+ assertEquals("application/octet-stream", FileSizeRouterUtils.getMimeType(file))
175
+ }
176
+
177
+ // ============================================================
178
+ // formatFileSize
179
+ // ============================================================
180
+
181
+ @Test
182
+ fun `formatFileSize formats bytes without a decimal point`() {
183
+ assertEquals("512 B", FileSizeRouterUtils.formatFileSize(512))
184
+ }
185
+
186
+ @Test
187
+ fun `formatFileSize formats kilobytes with one decimal place`() {
188
+ assertEquals("2.0 KB", FileSizeRouterUtils.formatFileSize(2048))
189
+ }
190
+
191
+ @Test
192
+ fun `formatFileSize formats megabytes and gigabytes correctly`() {
193
+ assertEquals("5.0 MB", FileSizeRouterUtils.formatFileSize(5L * 1024 * 1024))
194
+ assertEquals("1.0 GB", FileSizeRouterUtils.formatFileSize(1024L * 1024 * 1024))
195
+ }
196
+
197
+ // ============================================================
198
+ // processFile — early return when the decision cannot proceed
199
+ // ============================================================
200
+
201
+ @Test
202
+ fun `processFile returns a failure result immediately when the decision cannot proceed`() {
203
+ val decision = FileSizeRouterUtils.determineTransport(java.io.File(tempDir, "does-not-exist.txt"))
204
+ val context: android.content.Context = mock()
205
+ val webView: WebView = mock()
206
+
207
+ val result = FileSizeRouterUtils.processFile(context, webView, decision)
208
+
209
+ assertFalse(result.success)
210
+ assertEquals(decision.errorMessage, result.error)
211
+ assertNull(result.fileSrc)
212
+ }
213
+
214
+ // ============================================================
215
+ // processFile — BRIDGE_BASE64 branch (processViaBridge)
216
+ //
217
+ // FileUtils is a real object (Kotlin objects can't be mockStatic'd,
218
+ // see class header) -- convertUriToBase64 is driven for real via a
219
+ // mocked Context.contentResolver. Uri.fromFile and Base64.encodeToString
220
+ // are genuine android.jar statics, mockStatic'd normally.
221
+ // ============================================================
222
+
223
+ @Test
224
+ fun `processFile via bridge succeeds when the content resolver returns real bytes`() {
225
+ val file = fileOfSize("small.txt", 100)
226
+ val decision = FileSizeRouterUtils.determineTransport(file)
227
+ assertEquals(FileSizeRouterUtils.TransportType.BRIDGE_BASE64, decision.transportType)
228
+
229
+ val fakeUri: Uri = mock()
230
+ val resolver: ContentResolver = mock {
231
+ on { openInputStream(fakeUri) } doReturn java.io.ByteArrayInputStream("hello".toByteArray())
232
+ }
233
+ val context: android.content.Context = mock {
234
+ on { getContentResolver() } doReturn resolver
235
+ }
236
+ val webView: WebView = mock()
237
+
238
+ val uriMock = mockStatic(Uri::class.java)
239
+ val base64Mock = mockStatic(Base64::class.java)
240
+ try {
241
+ uriMock.`when`<Uri> { Uri.fromFile(file) }.thenReturn(fakeUri)
242
+ base64Mock.`when`<String> { Base64.encodeToString(any(), any()) }.thenReturn("aGVsbG8=")
243
+
244
+ val result = FileSizeRouterUtils.processFile(context, webView, decision)
245
+
246
+ assertTrue(result.success)
247
+ assertEquals(FileSizeRouterUtils.TransportType.BRIDGE_BASE64, result.transportUsed)
248
+ assertEquals("data:${decision.mimeType};base64,aGVsbG8=", result.fileSrc)
249
+ } finally {
250
+ uriMock.close()
251
+ base64Mock.close()
252
+ }
253
+ }
254
+
255
+ @Test
256
+ fun `processFile via bridge fails when the content resolver cannot open the uri`() {
257
+ val file = fileOfSize("small.txt", 100)
258
+ val decision = FileSizeRouterUtils.determineTransport(file)
259
+
260
+ val fakeUri: Uri = mock()
261
+ val resolver: ContentResolver = mock {
262
+ on { openInputStream(fakeUri) } doReturn null
263
+ }
264
+ val context: android.content.Context = mock {
265
+ on { getContentResolver() } doReturn resolver
266
+ }
267
+ val webView: WebView = mock()
268
+
269
+ val uriMock = mockStatic(Uri::class.java)
270
+ try {
271
+ uriMock.`when`<Uri> { Uri.fromFile(file) }.thenReturn(fakeUri)
272
+
273
+ val result = FileSizeRouterUtils.processFile(context, webView, decision)
274
+
275
+ assertFalse(result.success)
276
+ assertEquals(FileSizeRouterUtils.TransportType.BRIDGE_BASE64, result.transportUsed)
277
+ assertNull(result.fileSrc)
278
+ assertNotNull(result.error)
279
+ } finally {
280
+ uriMock.close()
281
+ }
282
+ }
283
+
284
+ @Test
285
+ fun `processFile via bridge fails gracefully when the content resolver throws`() {
286
+ val file = fileOfSize("small.txt", 100)
287
+ val decision = FileSizeRouterUtils.determineTransport(file)
288
+
289
+ val fakeUri: Uri = mock()
290
+ val resolver: ContentResolver = mock {
291
+ on { openInputStream(fakeUri) } doThrow RuntimeException("permission denied")
292
+ }
293
+ val context: android.content.Context = mock {
294
+ on { getContentResolver() } doReturn resolver
295
+ }
296
+ val webView: WebView = mock()
297
+
298
+ val uriMock = mockStatic(Uri::class.java)
299
+ try {
300
+ uriMock.`when`<Uri> { Uri.fromFile(file) }.thenReturn(fakeUri)
301
+
302
+ val result = FileSizeRouterUtils.processFile(context, webView, decision)
303
+
304
+ assertFalse(result.success)
305
+ assertNotNull(result.error)
306
+ } finally {
307
+ uriMock.close()
308
+ }
309
+ }
310
+
311
+ // ============================================================
312
+ // processFile — FRAMEWORK_SERVER branch (processViaFrameworkServer)
313
+ //
314
+ // FrameworkServerUtils is a real, JVM-wide singleton object (can't be
315
+ // mockStatic'd, see class header) -- started/stopped exactly as
316
+ // FrameworkServerUtilsTest.kt does. Defensive stop in both setUp and
317
+ // tearDown so shared server state can't leak across test classes.
318
+ // ============================================================
319
+
320
+ @Test
321
+ fun `processFile via framework server succeeds against a real running server`() {
322
+ FrameworkServerUtils.stopServer()
323
+ try {
324
+ val file = fileOfSize("bigfile.bin", 3 * 1024 * 1024) // 3MB, over bridge threshold
325
+ val serverContext: android.content.Context = mock { on { getCacheDir() } doReturn tempDir }
326
+ val serverWebView: WebView = mock { on { getUrl() } doReturn "http://localhost:8080/index.html" }
327
+ // If the real bind fails in this environment (port contention
328
+ // on a shared CI runner), skip rather than fail confusingly on
329
+ // a downstream assertion -- matches FrameworkServerUtilsTest's
330
+ // own Assume-based tolerance for the same startServer() call.
331
+ org.junit.Assume.assumeTrue(
332
+ "Framework server failed to start in this environment — skipping",
333
+ FrameworkServerUtils.startServer(serverContext, serverWebView)
334
+ )
335
+
336
+ val decision = FileSizeRouterUtils.determineTransport(file)
337
+ assertEquals(FileSizeRouterUtils.TransportType.FRAMEWORK_SERVER, decision.transportType)
338
+
339
+ val context: android.content.Context = mock()
340
+ val webView: WebView = mock()
341
+ val result = FileSizeRouterUtils.processFile(context, webView, decision)
342
+
343
+ assertTrue(result.success)
344
+ assertEquals(FileSizeRouterUtils.TransportType.FRAMEWORK_SERVER, result.transportUsed)
345
+ assertNotNull(result.fileSrc)
346
+ assertTrue(result.fileSrc!!.startsWith("http://"))
347
+ } finally {
348
+ FrameworkServerUtils.stopServer()
349
+ }
350
+ }
351
+
352
+ @Test
353
+ fun `processFile via framework server fails when copyAndServeFile is called while stopped`() {
354
+ // Construct a FRAMEWORK_SERVER decision directly (determineTransport
355
+ // itself would route to CONTENT_PROVIDER once the server is
356
+ // confirmed stopped) to exercise processViaFrameworkServer's own
357
+ // failure branch: copyAndServeFile returns null when
358
+ // !isServerRunning, independent of how the decision was reached.
359
+ FrameworkServerUtils.stopServer()
360
+ try {
361
+ assertFalse(FrameworkServerUtils.isRunning())
362
+ val file = fileOfSize("bigfile.bin", 3 * 1024 * 1024)
363
+ val decision = FileSizeRouterUtils.FileRoutingDecision(
364
+ transportType = FileSizeRouterUtils.TransportType.FRAMEWORK_SERVER,
365
+ file = file,
366
+ fileName = file.name,
367
+ mimeType = "application/octet-stream",
368
+ fileSize = file.length(),
369
+ reason = "forced for test"
370
+ )
371
+
372
+ val context: android.content.Context = mock()
373
+ val webView: WebView = mock()
374
+ val result = FileSizeRouterUtils.processFile(context, webView, decision)
375
+
376
+ assertFalse(result.success)
377
+ assertNull(result.fileSrc)
378
+ assertNotNull(result.error)
379
+ } finally {
380
+ FrameworkServerUtils.stopServer()
381
+ }
382
+ }
383
+
384
+ // ============================================================
385
+ // processFile — CONTENT_PROVIDER branch (processViaContentProvider)
386
+ //
387
+ // IntentUtils.createFileProviderUri needs real FileProvider manifest
388
+ // metadata to succeed -- not available in a JVM unit test, so only
389
+ // its failure paths are covered here (non-Activity context, and a
390
+ // real call against a mocked Activity that throws for lack of
391
+ // provider registration). The success path is Robolectric/
392
+ // instrumented-test territory.
393
+ // ============================================================
394
+
395
+ @Test
396
+ fun `processFile via content provider fails when context is not an Activity`() {
397
+ FrameworkServerUtils.stopServer()
398
+ try {
399
+ val file = fileOfSize("bigfile.bin", 3 * 1024 * 1024)
400
+ val decision = FileSizeRouterUtils.determineTransport(file)
401
+ assertEquals(FileSizeRouterUtils.TransportType.CONTENT_PROVIDER, decision.transportType)
402
+
403
+ val nonActivityContext: android.content.Context = mock()
404
+ val webView: WebView = mock()
405
+
406
+ val result = FileSizeRouterUtils.processFile(nonActivityContext, webView, decision)
407
+
408
+ assertFalse(result.success)
409
+ assertTrue(result.error?.contains("Activity context") == true)
410
+ } finally {
411
+ FrameworkServerUtils.stopServer()
412
+ }
413
+ }
414
+
415
+ @Test
416
+ fun `processFile via content provider fails gracefully when the real call throws`() {
417
+ FrameworkServerUtils.stopServer()
418
+ try {
419
+ val file = fileOfSize("bigfile.bin", 3 * 1024 * 1024)
420
+ val decision = FileSizeRouterUtils.determineTransport(file)
421
+ assertEquals(FileSizeRouterUtils.TransportType.CONTENT_PROVIDER, decision.transportType)
422
+
423
+ // A mocked Activity has no real FileProvider manifest entry --
424
+ // IntentUtils.createFileProviderUri's real call against it is
425
+ // expected to throw, exercising the catch branch.
426
+ val activity: Activity = mock()
427
+ val webView: WebView = mock()
428
+
429
+ val result = FileSizeRouterUtils.processFile(activity, webView, decision)
430
+
431
+ assertFalse(result.success)
432
+ assertEquals(FileSizeRouterUtils.TransportType.CONTENT_PROVIDER, result.transportUsed)
433
+ assertNull(result.fileSrc)
434
+ assertNotNull(result.error)
435
+ } finally {
436
+ FrameworkServerUtils.stopServer()
437
+ }
438
+ }
439
+
440
+ // ============================================================
441
+ // processFile — UNSUPPORTED branch (the `else` in the when block)
442
+ // ============================================================
443
+
444
+ @Test
445
+ fun `processFile returns not-supported for an UNSUPPORTED decision that still has canProceed true`() {
446
+ // determineTransport never actually produces canProceed=true with
447
+ // UNSUPPORTED (the >100MB case sets canProceed=false, hitting the
448
+ // early-return branch tested above) -- this constructs that
449
+ // otherwise-unreachable combination directly to exercise
450
+ // processFile's own `when` branch for TransportType.UNSUPPORTED,
451
+ // which is defensive dead code from determineTransport's callers
452
+ // but still real code in processFile itself.
453
+ val file = fileOfSize("weird.bin", 10)
454
+ val decision = FileSizeRouterUtils.FileRoutingDecision(
455
+ transportType = FileSizeRouterUtils.TransportType.UNSUPPORTED,
456
+ file = file,
457
+ fileName = file.name,
458
+ mimeType = "application/octet-stream",
459
+ fileSize = 10,
460
+ reason = "forced for test",
461
+ canProceed = true
462
+ )
463
+
464
+ val context: android.content.Context = mock()
465
+ val webView: WebView = mock()
466
+ val result = FileSizeRouterUtils.processFile(context, webView, decision)
467
+
468
+ assertFalse(result.success)
469
+ assertEquals("Transport not supported", result.error)
470
+ }
471
+ }