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,192 @@
1
+ import XCTest
2
+ @testable import CatalystCoreLogic
3
+
4
+ /**
5
+ * Unit tests for CatalystConstants
6
+ *
7
+ * Most of this file is static constant declarations, exercised here mainly
8
+ * as sanity/regression checks. The two pieces of real logic —
9
+ * `Bridge.isTraceExportEnabled` and `Bridge.validCommands` — get more
10
+ * thorough coverage.
11
+ *
12
+ * Known gap (documented, not silently accepted): `validCommands` also reads
13
+ * module-level `ConfigConstants.Profiler.enabled` /
14
+ * `ConfigConstants.Notifications.enabled`, which are `public static let`
15
+ * compile-time constants from the checked-in stub (both `false`). There is
16
+ * no test seam (unlike URLWhitelistManager's `testInitialize`) to force
17
+ * those to `true`, so the two `if` branches that add
18
+ * "exportCatalystTrace"/notification commands to `validCommands` cannot be
19
+ * exercised from this file alone. `isTraceExportEnabled` itself IS fully
20
+ * covered below since its parameters can be passed explicitly, bypassing
21
+ * the defaults.
22
+ */
23
+ final class CatalystConstantsTests: XCTestCase {
24
+
25
+ // MARK: - FileTransport
26
+
27
+ func testFileTransport_Base64SizeLimit() {
28
+ XCTAssertEqual(CatalystConstants.FileTransport.base64SizeLimit, 2 * 1024 * 1024)
29
+ }
30
+
31
+ func testFileTransport_FrameworkServerSizeLimit() {
32
+ XCTAssertEqual(CatalystConstants.FileTransport.frameworkServerSizeLimit, 100 * 1024 * 1024)
33
+ }
34
+
35
+ // MARK: - ImageProcessing
36
+
37
+ func testImageProcessing_QualityLevels() {
38
+ XCTAssertEqual(CatalystConstants.ImageProcessing.Quality.high, 0.9)
39
+ XCTAssertEqual(CatalystConstants.ImageProcessing.Quality.medium, 0.7)
40
+ XCTAssertEqual(CatalystConstants.ImageProcessing.Quality.low, 0.5)
41
+ }
42
+
43
+ func testImageProcessing_DefaultQualityMatchesMedium() {
44
+ XCTAssertEqual(
45
+ CatalystConstants.ImageProcessing.defaultQuality,
46
+ CatalystConstants.ImageProcessing.Quality.medium
47
+ )
48
+ }
49
+
50
+ // MARK: - NetworkServer
51
+
52
+ func testNetworkServer_PortRangeIsValidAndNonEmpty() {
53
+ let start = CatalystConstants.NetworkServer.portRangeStart
54
+ let end = CatalystConstants.NetworkServer.portRangeEnd
55
+ XCTAssertLessThan(start, end)
56
+ }
57
+
58
+ func testNetworkServer_TimeoutsArePositive() {
59
+ XCTAssertGreaterThan(CatalystConstants.NetworkServer.sessionTimeout, 0)
60
+ XCTAssertGreaterThan(CatalystConstants.NetworkServer.cleanupInterval, 0)
61
+ XCTAssertGreaterThan(CatalystConstants.NetworkServer.connectionTimeout, 0)
62
+ }
63
+
64
+ func testNetworkServer_MaxConnectionsIsPositive() {
65
+ XCTAssertGreaterThan(CatalystConstants.NetworkServer.maxConnections, 0)
66
+ }
67
+
68
+ // MARK: - ErrorCodes
69
+
70
+ func testErrorCodes_MatchStandardHTTPStatusCodes() {
71
+ XCTAssertEqual(CatalystConstants.ErrorCodes.badRequest, 400)
72
+ XCTAssertEqual(CatalystConstants.ErrorCodes.fileNotFound, 404)
73
+ XCTAssertEqual(CatalystConstants.ErrorCodes.internalServerError, 500)
74
+ }
75
+
76
+ // MARK: - Cache
77
+
78
+ func testCache_FreshWindowIsShorterThanStaleWindow() {
79
+ XCTAssertLessThan(
80
+ CatalystConstants.Cache.freshWindow,
81
+ CatalystConstants.Cache.staleWindow
82
+ )
83
+ }
84
+
85
+ func testCache_CapacitiesAreDiskLargerThanMemory() {
86
+ XCTAssertLessThan(
87
+ CatalystConstants.Cache.memoryCapacity,
88
+ CatalystConstants.Cache.diskCapacity
89
+ )
90
+ }
91
+
92
+ // MARK: - Logging.Categories
93
+
94
+ func testLogging_CategoriesAreAllNonEmptyAndDistinct() {
95
+ let categories = [
96
+ CatalystConstants.Logging.Categories.nativeBridge,
97
+ CatalystConstants.Logging.Categories.javascriptInterface,
98
+ CatalystConstants.Logging.Categories.messageValidator,
99
+ CatalystConstants.Logging.Categories.commandHandler,
100
+ CatalystConstants.Logging.Categories.fileHandler,
101
+ CatalystConstants.Logging.Categories.delegateHandler,
102
+ ]
103
+ XCTAssertTrue(categories.allSatisfy { !$0.isEmpty })
104
+ XCTAssertEqual(Set(categories).count, categories.count, "Logging categories should be distinct")
105
+ }
106
+
107
+ // MARK: - Bridge.isDebugBuild
108
+
109
+ func testBridge_IsDebugBuildMatchesBuildConfiguration() {
110
+ #if DEBUG
111
+ XCTAssertTrue(CatalystConstants.Bridge.isDebugBuild)
112
+ #else
113
+ XCTAssertFalse(CatalystConstants.Bridge.isDebugBuild)
114
+ #endif
115
+ }
116
+
117
+ // MARK: - Bridge.isTraceExportEnabled — all 4 explicit combinations
118
+
119
+ func testIsTraceExportEnabled_DebugTrueProfilerTrue_ReturnsTrue() {
120
+ XCTAssertTrue(
121
+ CatalystConstants.Bridge.isTraceExportEnabled(debugBuild: true, profilerEnabled: true)
122
+ )
123
+ }
124
+
125
+ func testIsTraceExportEnabled_DebugTrueProfilerFalse_ReturnsFalse() {
126
+ XCTAssertFalse(
127
+ CatalystConstants.Bridge.isTraceExportEnabled(debugBuild: true, profilerEnabled: false)
128
+ )
129
+ }
130
+
131
+ func testIsTraceExportEnabled_DebugFalseProfilerTrue_ReturnsFalse() {
132
+ XCTAssertFalse(
133
+ CatalystConstants.Bridge.isTraceExportEnabled(debugBuild: false, profilerEnabled: true)
134
+ )
135
+ }
136
+
137
+ func testIsTraceExportEnabled_DebugFalseProfilerFalse_ReturnsFalse() {
138
+ XCTAssertFalse(
139
+ CatalystConstants.Bridge.isTraceExportEnabled(debugBuild: false, profilerEnabled: false)
140
+ )
141
+ }
142
+
143
+ // MARK: - Bridge.validCommands
144
+
145
+ func testValidCommands_ContainsAlwaysPresentBaseCommands() {
146
+ let commands = CatalystConstants.Bridge.validCommands
147
+
148
+ let expectedBaseCommands = [
149
+ "openCamera", "requestCameraPermission", "getDeviceInfo", "getNetworkStatus",
150
+ "logger", "pickFile", "openFileWithIntent", "requestHapticFeedback",
151
+ "googleSignIn", "getSafeArea", "setScreenSecure", "getScreenSecure",
152
+ "clearWebData", "startVideoStream", "stopVideoStream", "flipVideoStream",
153
+ "sendVideoStreamCommand", "setVideoStreamZoom", "setVideoStreamTorch",
154
+ "setVideoStreamFps", "startTransition", "commitTransition", "cancelTransition",
155
+ ]
156
+
157
+ for command in expectedBaseCommands {
158
+ XCTAssertTrue(commands.contains(command), "Expected base command missing: \(command)")
159
+ }
160
+ }
161
+
162
+ /// Known gap: with the checked-in ConfigConstants stub (Profiler.enabled
163
+ /// and Notifications.enabled both false), validCommands cannot be forced
164
+ /// down its `true`-branches from this test file — there's no equivalent
165
+ /// of URLWhitelistManager's testInitialize() seam for ConfigConstants.
166
+ /// This test documents and asserts the actual (disabled-config) behavior
167
+ /// rather than silently skipping the file.
168
+ func testValidCommands_ExportTraceAndNotificationCommandsAbsentUnderDisabledConfig() {
169
+ let commands = CatalystConstants.Bridge.validCommands
170
+
171
+ if !CatalystConstants.Bridge.isTraceExportEnabled() {
172
+ XCTAssertFalse(commands.contains("exportCatalystTrace"))
173
+ }
174
+
175
+ if !ConfigConstants.Notifications.enabled {
176
+ let notificationCommands = [
177
+ "requestNotificationPermission", "scheduleLocalNotification",
178
+ "cancelLocalNotification", "registerForPushNotifications",
179
+ "subscribeToTopic", "unsubscribeFromTopic", "getSubscribedTopics",
180
+ ]
181
+ for command in notificationCommands {
182
+ XCTAssertFalse(commands.contains(command), "Unexpected notification command present: \(command)")
183
+ }
184
+ }
185
+ }
186
+
187
+ func testValidCommands_IsStableAcrossRepeatedCalls() {
188
+ let first = CatalystConstants.Bridge.validCommands
189
+ let second = CatalystConstants.Bridge.validCommands
190
+ XCTAssertEqual(first, second)
191
+ }
192
+ }
@@ -1,5 +1,5 @@
1
1
  import XCTest
2
- @testable import CatalystCore
2
+ @testable import CatalystCoreLogic
3
3
 
4
4
  /**
5
5
  * Unit tests for Config Mapping
@@ -0,0 +1,106 @@
1
+ import XCTest
2
+ import Foundation
3
+ @testable import CatalystCoreLogic
4
+
5
+ /**
6
+ * Targeted unit tests for FrameworkServerUtils' internal error/edge branches
7
+ * that don't need a running server or a real HTTP round-trip — the pure
8
+ * validation logic (validateFilePath, updateAllowedOrigin,
9
+ * addFileToServe's not-running guard) and FrameworkServerError's own
10
+ * computed property.
11
+ *
12
+ * Complements FrameworkServerUtilsTests.swift (lifecycle/config) and
13
+ * FrameworkServerUtilsLoopbackTests.swift (real request/response
14
+ * round-trips) — this file is specifically the "call the internal method
15
+ * directly with an input designed to hit one branch" layer.
16
+ */
17
+ final class FrameworkServerUtilsErrorPathTests: XCTestCase {
18
+
19
+ var frameworkServer: FrameworkServerUtils!
20
+
21
+ override func setUp() {
22
+ super.setUp()
23
+ frameworkServer = FrameworkServerUtils.shared
24
+ }
25
+
26
+ override func tearDown() {
27
+ if frameworkServer.isRunning() {
28
+ frameworkServer.stopServer()
29
+ }
30
+ frameworkServer = nil
31
+ super.tearDown()
32
+ }
33
+
34
+ // MARK: - FrameworkServerError.localizedDescription
35
+
36
+ func testFrameworkServerError_CacheInitializationFailed_Description() {
37
+ let error = FrameworkServerError.cacheInitializationFailed("disk full")
38
+ XCTAssertEqual(error.localizedDescription, "Cache initialization failed: disk full")
39
+ }
40
+
41
+ func testFrameworkServerError_DataConversionFailed_Description() {
42
+ let error = FrameworkServerError.dataConversionFailed("bad encoding")
43
+ XCTAssertEqual(error.localizedDescription, "Data conversion failed: bad encoding")
44
+ }
45
+
46
+ func testFrameworkServerError_ConnectionHandlingFailed_Description() {
47
+ let error = FrameworkServerError.connectionHandlingFailed("socket closed")
48
+ XCTAssertEqual(error.localizedDescription, "Connection handling failed: socket closed")
49
+ }
50
+
51
+ // MARK: - addFileToServe: not-running guard
52
+
53
+ func testAddFileToServe_ServerNotRunning_ReturnsNil() {
54
+ // Server is deliberately not started in this test.
55
+ XCTAssertFalse(frameworkServer.isRunning())
56
+
57
+ let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("not-served.txt")
58
+ try? Data("x".utf8).write(to: tempURL)
59
+ defer { try? FileManager.default.removeItem(at: tempURL) }
60
+
61
+ let result = frameworkServer.addFileToServe(file: tempURL, fileName: "not-served.txt", mimeType: "text/plain")
62
+
63
+ XCTAssertNil(result)
64
+ }
65
+
66
+ // MARK: - copyAndServeFile: not-running / no cache directory guard
67
+
68
+ func testCopyAndServeFile_ServerNotRunning_ReturnsNil() {
69
+ XCTAssertFalse(frameworkServer.isRunning())
70
+
71
+ let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("not-copied.txt")
72
+ try? Data("x".utf8).write(to: tempURL)
73
+ defer { try? FileManager.default.removeItem(at: tempURL) }
74
+
75
+ let result = frameworkServer.copyAndServeFile(originalFile: tempURL, fileName: "not-copied.txt", mimeType: "text/plain")
76
+
77
+ XCTAssertNil(result)
78
+ }
79
+
80
+ // MARK: - updateAllowedOrigin
81
+
82
+ func testUpdateAllowedOrigin_EmptyURL_FallsBackToWildcard() {
83
+ // No direct getter for allowedOrigin, but this exercises the
84
+ // internal branch without crashing and documents the contract.
85
+ frameworkServer.updateAllowedOrigin(from: "")
86
+ // No assertion on internal state (private) — the value is exercised
87
+ // for real via the CORS header in the loopback tests' status
88
+ // endpoint response headers in a follow-up if a getter is ever added.
89
+ }
90
+
91
+ func testUpdateAllowedOrigin_ValidURL_ExtractsSchemeHostPort() {
92
+ frameworkServer.updateAllowedOrigin(from: "http://192.168.0.104:3005/some/path")
93
+ // Same note as above — exercises the parse-success branch.
94
+ }
95
+
96
+ func testUpdateAllowedOrigin_UnparsableURL_FallsBackToWildcard() {
97
+ // A string with characters URLComponents can't parse as a URL at all.
98
+ frameworkServer.updateAllowedOrigin(from: "http://[invalid")
99
+ }
100
+
101
+ func testUpdateAllowedOrigin_URLWithNoHostOrScheme_FallsBackToWildcard() {
102
+ // Parses as URLComponents but yields no scheme/host/port to build
103
+ // an origin from.
104
+ frameworkServer.updateAllowedOrigin(from: "/relative/path/only")
105
+ }
106
+ }
@@ -0,0 +1,243 @@
1
+ import XCTest
2
+ import Foundation
3
+ @testable import CatalystCoreLogic
4
+
5
+ /**
6
+ * In-process loopback HTTP tests for FrameworkServerUtils.
7
+ *
8
+ * The existing FrameworkServerUtilsTests.swift covers server lifecycle,
9
+ * config, and port/session-id logic, but skips actual request handling
10
+ * (receiveHTTPRequest / processHTTPRequest / serveFile / sendHTTPHeaders /
11
+ * streamFileContent) — that's most of the file's uncovered ~65%. This file
12
+ * makes real HTTP requests over URLSession against http://localhost:<port>
13
+ * to exercise that path directly.
14
+ *
15
+ * Not Tier 2 (#416, scaffolded-app simulator tests) — this stays entirely
16
+ * in-process, no simulator, same package/target as every other CoreLogic
17
+ * test. It's the loopback-client middle ground flagged when the coverage
18
+ * math showed 95% package-wide wasn't reachable while FrameworkServerUtils
19
+ * stayed at ~35%.
20
+ *
21
+ * Same "server may not start in this sandbox" guard as the existing test
22
+ * file — network restrictions in some CI/sandbox environments can prevent
23
+ * NWListener from binding. Tests skip (not fail) in that case, consistent
24
+ * with the established pattern.
25
+ */
26
+ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
27
+
28
+ var frameworkServer: FrameworkServerUtils!
29
+ private var tempFileURL: URL?
30
+
31
+ override func setUp() {
32
+ super.setUp()
33
+ frameworkServer = FrameworkServerUtils.shared
34
+ }
35
+
36
+ override func tearDown() {
37
+ if let tempFileURL {
38
+ try? FileManager.default.removeItem(at: tempFileURL)
39
+ }
40
+ tempFileURL = nil
41
+
42
+ if frameworkServer.isRunning() {
43
+ frameworkServer.stopServer()
44
+ }
45
+ frameworkServer = nil
46
+ super.tearDown()
47
+ }
48
+
49
+ /// Starts the server and waits for NWListener's async stateUpdateHandler
50
+ /// to settle, mirroring the pattern already established in
51
+ /// FrameworkServerUtilsTests.swift. Returns false (and the caller should
52
+ /// skip) if the server isn't actually running afterward.
53
+ private func startServerAndWaitReady() -> Bool {
54
+ let started = frameworkServer.startServer()
55
+ guard started else { return false }
56
+ Thread.sleep(forTimeInterval: 0.3)
57
+ return frameworkServer.isRunning()
58
+ }
59
+
60
+ private func serveTemporaryFile(content: String, fileName: String, mimeType: String) throws -> String? {
61
+ let data = Data(content.utf8)
62
+ let url = FileManager.default.temporaryDirectory.appendingPathComponent(fileName)
63
+ try data.write(to: url)
64
+ tempFileURL = url
65
+
66
+ return frameworkServer.copyAndServeFile(originalFile: url, fileName: fileName, mimeType: mimeType)
67
+ }
68
+
69
+ private func makeRequest(
70
+ url: URL,
71
+ method: String = "GET"
72
+ ) async throws -> (HTTPURLResponse, Data) {
73
+ var request = URLRequest(url: url)
74
+ request.httpMethod = method
75
+ request.timeoutInterval = 5
76
+
77
+ let (data, response) = try await URLSession.shared.data(for: request)
78
+ let httpResponse = try XCTUnwrap(response as? HTTPURLResponse)
79
+ return (httpResponse, data)
80
+ }
81
+
82
+ // MARK: - Status endpoint
83
+
84
+ func testStatusEndpoint_ReturnsRunningJSON() async throws {
85
+ guard startServerAndWaitReady() else {
86
+ throw XCTSkip("Server failed to start in this environment — skipping loopback test")
87
+ }
88
+
89
+ let port = frameworkServer.getServerPort()
90
+ let sessionId = frameworkServer.getSessionId()
91
+ let statusURL = try XCTUnwrap(URL(string: "http://localhost:\(port)/framework-\(sessionId)/status"))
92
+
93
+ let (response, data) = try await makeRequest(url: statusURL)
94
+
95
+ XCTAssertEqual(response.statusCode, 200)
96
+ XCTAssertEqual(response.value(forHTTPHeaderField: "Content-Type"), "application/json")
97
+
98
+ let json = try XCTUnwrap(try JSONSerialization.jsonObject(with: data) as? [String: Any])
99
+ XCTAssertEqual(json["status"] as? String, "running")
100
+ XCTAssertEqual(json["sessionId"] as? String, sessionId)
101
+ XCTAssertEqual(json["port"] as? Int, Int(port))
102
+ }
103
+
104
+ // MARK: - File serving: success path
105
+
106
+ func testFileRequest_ServesRealFileContent() async throws {
107
+ guard startServerAndWaitReady() else {
108
+ throw XCTSkip("Server failed to start in this environment — skipping loopback test")
109
+ }
110
+
111
+ let content = "loopback test file content \(UUID().uuidString)"
112
+ let servedURLString = try XCTUnwrap(
113
+ try serveTemporaryFile(content: content, fileName: "loopback-test.txt", mimeType: "text/plain")
114
+ )
115
+ let servedURL = try XCTUnwrap(URL(string: servedURLString))
116
+
117
+ let (response, data) = try await makeRequest(url: servedURL)
118
+
119
+ XCTAssertEqual(response.statusCode, 200)
120
+ XCTAssertEqual(response.value(forHTTPHeaderField: "Content-Type"), "text/plain")
121
+ XCTAssertEqual(String(data: data, encoding: .utf8), content)
122
+ }
123
+
124
+ func testFileRequest_ContentLengthHeaderMatchesActualBytes() async throws {
125
+ guard startServerAndWaitReady() else {
126
+ throw XCTSkip("Server failed to start in this environment — skipping loopback test")
127
+ }
128
+
129
+ let content = String(repeating: "x", count: 5000)
130
+ let servedURLString = try XCTUnwrap(
131
+ try serveTemporaryFile(content: content, fileName: "loopback-large.txt", mimeType: "text/plain")
132
+ )
133
+ let servedURL = try XCTUnwrap(URL(string: servedURLString))
134
+
135
+ let (response, data) = try await makeRequest(url: servedURL)
136
+
137
+ XCTAssertEqual(response.statusCode, 200)
138
+ XCTAssertEqual(data.count, 5000)
139
+ let contentLengthHeader = response.value(forHTTPHeaderField: "Content-Length")
140
+ XCTAssertEqual(contentLengthHeader, "5000")
141
+ }
142
+
143
+ // MARK: - File serving: error paths
144
+
145
+ func testFileRequest_UnknownFileId_Returns404() async throws {
146
+ guard startServerAndWaitReady() else {
147
+ throw XCTSkip("Server failed to start in this environment — skipping loopback test")
148
+ }
149
+
150
+ let port = frameworkServer.getServerPort()
151
+ let sessionId = frameworkServer.getSessionId()
152
+ let url = try XCTUnwrap(URL(string: "http://localhost:\(port)/framework-\(sessionId)/file-does-not-exist"))
153
+
154
+ let (response, _) = try await makeRequest(url: url)
155
+
156
+ XCTAssertEqual(response.statusCode, 404)
157
+ }
158
+
159
+ func testInvalidRoute_Returns404() async throws {
160
+ guard startServerAndWaitReady() else {
161
+ throw XCTSkip("Server failed to start in this environment — skipping loopback test")
162
+ }
163
+
164
+ let port = frameworkServer.getServerPort()
165
+ let url = try XCTUnwrap(URL(string: "http://localhost:\(port)/not-a-real-route"))
166
+
167
+ let (response, _) = try await makeRequest(url: url)
168
+
169
+ XCTAssertEqual(response.statusCode, 404)
170
+ }
171
+
172
+ func testNonGETMethod_Returns405() async throws {
173
+ guard startServerAndWaitReady() else {
174
+ throw XCTSkip("Server failed to start in this environment — skipping loopback test")
175
+ }
176
+
177
+ let port = frameworkServer.getServerPort()
178
+ let sessionId = frameworkServer.getSessionId()
179
+ let url = try XCTUnwrap(URL(string: "http://localhost:\(port)/framework-\(sessionId)/status"))
180
+
181
+ let (response, _) = try await makeRequest(url: url, method: "POST")
182
+
183
+ XCTAssertEqual(response.statusCode, 405)
184
+ }
185
+
186
+ // MARK: - Physical file missing after being registered (not just unknown id)
187
+
188
+ func testFileRequest_PhysicalFileDeletedAfterRegistration_Returns404() async throws {
189
+ guard startServerAndWaitReady() else {
190
+ throw XCTSkip("Server failed to start in this environment — skipping loopback test")
191
+ }
192
+
193
+ let servedURLString = try XCTUnwrap(
194
+ try serveTemporaryFile(content: "will be deleted from cache", fileName: "loopback-vanish.txt", mimeType: "text/plain")
195
+ )
196
+ let servedURL = try XCTUnwrap(URL(string: servedURLString))
197
+
198
+ // copyAndServeFile copies into a documents-directory cache path
199
+ // deterministically named "<timestamp>_<fileName>" — reconstruct
200
+ // and delete it directly (cacheDirectory itself is private) to
201
+ // simulate the file having vanished between being registered and
202
+ // being requested (servedFile entry still present, physical file
203
+ // gone), exercising serveFile's "Physical file not found" branch
204
+ // rather than addFileToServe's earlier existence checks.
205
+ let documentsDirectory = try XCTUnwrap(
206
+ FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
207
+ )
208
+ let cacheDirectory = documentsDirectory.appendingPathComponent("framework_server_files", isDirectory: true)
209
+ let cachedFiles = try FileManager.default.contentsOfDirectory(at: cacheDirectory, includingPropertiesForKeys: nil)
210
+ let vanishedCacheFile = try XCTUnwrap(cachedFiles.first { $0.lastPathComponent.hasSuffix("loopback-vanish.txt") })
211
+ try FileManager.default.removeItem(at: vanishedCacheFile)
212
+
213
+ let (response, _) = try await makeRequest(url: servedURL)
214
+
215
+ XCTAssertEqual(response.statusCode, 404)
216
+ }
217
+
218
+ // MARK: - removeServedFile
219
+
220
+ func testRemoveServedFile_SubsequentRequestReturns404() async throws {
221
+ guard startServerAndWaitReady() else {
222
+ throw XCTSkip("Server failed to start in this environment — skipping loopback test")
223
+ }
224
+
225
+ let servedURLString = try XCTUnwrap(
226
+ try serveTemporaryFile(content: "to be removed", fileName: "loopback-remove.txt", mimeType: "text/plain")
227
+ )
228
+ let servedURL = try XCTUnwrap(URL(string: servedURLString))
229
+
230
+ // Sanity: file is servable before removal.
231
+ let (beforeResponse, _) = try await makeRequest(url: servedURL)
232
+ XCTAssertEqual(beforeResponse.statusCode, 200)
233
+
234
+ let fileId = String(servedURL.lastPathComponent.dropFirst("file-".count))
235
+ frameworkServer.removeServedFile(fileId: fileId)
236
+
237
+ // Give the barrier-queued removal a moment to land before re-requesting.
238
+ try await Task.sleep(nanoseconds: 100_000_000)
239
+
240
+ let (afterResponse, _) = try await makeRequest(url: servedURL)
241
+ XCTAssertEqual(afterResponse.statusCode, 404)
242
+ }
243
+ }
@@ -1,6 +1,6 @@
1
1
  import XCTest
2
2
  import Foundation
3
- @testable import CatalystCore
3
+ @testable import CatalystCoreLogic
4
4
 
5
5
  /**
6
6
  * Unit tests for FrameworkServerUtils
@@ -0,0 +1,119 @@
1
+ import XCTest
2
+ import Network
3
+ @testable import CatalystCoreLogic
4
+
5
+ /**
6
+ * Unit tests for NetworkMonitor
7
+ *
8
+ * NetworkMonitor is a `private init()` singleton wrapping a real
9
+ * NWPathMonitor, with no dependency-injection seam (unlike
10
+ * URLWhitelistManager's testInitialize()). Adding real DI is a production
11
+ * code change out of scope for a mechanical coverage pass — flagged as a
12
+ * follow-up (see issue #432 plan notes), not attempted here.
13
+ *
14
+ * What IS covered: `mapPathToStatus`, the one pure/static piece of logic in
15
+ * this file, made `internal` (from `private`) specifically so @testable
16
+ * import can reach it directly — no singleton, no async callback needed.
17
+ *
18
+ * `NWPath` has no public initializer for arbitrary synthetic states, so the
19
+ * "happy path" tests below obtain a REAL NWPath from a short-lived
20
+ * NWPathMonitor rather than hand-built fixtures. This means only whatever
21
+ * interface type is actually active on the test-running machine gets
22
+ * exercised (typically wifi or ethernet in this environment) — the
23
+ * cellular/loopback/"other"/none branches of the interface-type mapping
24
+ * are NOT exercised by this file. That is the honest, stated coverage gap
25
+ * for this file, not a hidden one.
26
+ *
27
+ * NetworkStatus's own struct/field access and the singleton's
28
+ * currentStatus/addListener/removeListener lifecycle (async, dependent on
29
+ * a live path callback firing) are left untested for the same DI reason.
30
+ */
31
+ final class NetworkMonitorTests: XCTestCase {
32
+
33
+ /// Waits for a real NWPath from a fresh, short-lived monitor. Not the
34
+ /// shared NetworkMonitor singleton — an independent NWPathMonitor so
35
+ /// this test doesn't depend on NetworkMonitor's internal state/timing.
36
+ private func waitForRealPath(timeout: TimeInterval = 5) -> NWPath? {
37
+ let monitor = NWPathMonitor()
38
+ let expectation = self.expectation(description: "NWPathMonitor delivered a path")
39
+ var capturedPath: NWPath?
40
+
41
+ monitor.pathUpdateHandler = { path in
42
+ capturedPath = path
43
+ expectation.fulfill()
44
+ }
45
+ monitor.start(queue: DispatchQueue.global())
46
+
47
+ wait(for: [expectation], timeout: timeout)
48
+ monitor.cancel()
49
+ return capturedPath
50
+ }
51
+
52
+ func testMapPathToStatus_RealPathProducesConsistentIsOnline() throws {
53
+ guard let path = waitForRealPath() else {
54
+ throw XCTSkip("No NWPath delivered within timeout — cannot exercise mapPathToStatus without a live path")
55
+ }
56
+
57
+ let status = NetworkMonitor.mapPathToStatus(path)
58
+
59
+ // isOnline should exactly mirror path.status == .satisfied
60
+ XCTAssertEqual(status.isOnline, path.status == .satisfied)
61
+ }
62
+
63
+ func testMapPathToStatus_RealPathTypeMatchesActualInterface() throws {
64
+ guard let path = waitForRealPath() else {
65
+ throw XCTSkip("No NWPath delivered within timeout — cannot exercise mapPathToStatus without a live path")
66
+ }
67
+
68
+ let status = NetworkMonitor.mapPathToStatus(path)
69
+
70
+ // Whichever interface is actually active should be reflected;
71
+ // this is inherently machine-dependent, so assert consistency
72
+ // with the source path rather than a specific expected value.
73
+ if path.usesInterfaceType(.wifi) {
74
+ XCTAssertEqual(status.type, "wifi")
75
+ } else if path.usesInterfaceType(.cellular) {
76
+ XCTAssertEqual(status.type, "cellular")
77
+ } else if path.usesInterfaceType(.wiredEthernet) {
78
+ XCTAssertEqual(status.type, "ethernet")
79
+ } else if path.usesInterfaceType(.other) {
80
+ XCTAssertEqual(status.type, "other")
81
+ } else if path.usesInterfaceType(.loopback) {
82
+ XCTAssertEqual(status.type, "loopback")
83
+ } else {
84
+ XCTAssertNil(status.type)
85
+ }
86
+ }
87
+
88
+ func testMapPathToStatus_IsPureAndDeterministicForSamePath() throws {
89
+ guard let path = waitForRealPath() else {
90
+ throw XCTSkip("No NWPath delivered within timeout — cannot exercise mapPathToStatus without a live path")
91
+ }
92
+
93
+ let first = NetworkMonitor.mapPathToStatus(path)
94
+ let second = NetworkMonitor.mapPathToStatus(path)
95
+
96
+ XCTAssertEqual(first.isOnline, second.isOnline)
97
+ XCTAssertEqual(first.type, second.type)
98
+ }
99
+
100
+ // MARK: - NetworkStatus struct itself
101
+
102
+ func testNetworkStatus_StoresProvidedValues() {
103
+ let online = NetworkStatus(isOnline: true, type: "wifi")
104
+ XCTAssertTrue(online.isOnline)
105
+ XCTAssertEqual(online.type, "wifi")
106
+
107
+ let offline = NetworkStatus(isOnline: false, type: nil)
108
+ XCTAssertFalse(offline.isOnline)
109
+ XCTAssertNil(offline.type)
110
+ }
111
+
112
+ // MARK: - Singleton smoke test (no assertions on async behavior)
113
+
114
+ func testShared_ReturnsSameInstanceAcrossAccesses() {
115
+ // Confirms the singleton wiring itself, without depending on any
116
+ // async NWPathMonitor callback having fired yet.
117
+ XCTAssertTrue(NetworkMonitor.shared === NetworkMonitor.shared)
118
+ }
119
+ }