catalyst-core 0.2.0-beta.2 → 0.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/babel.native.config.cjs +8 -0
  2. package/bin/catalyst.js +49 -105
  3. package/changelog.md +8 -0
  4. package/dist/caching.js +1 -1
  5. package/dist/index.jsx +8 -0
  6. package/dist/logger.js +74 -3
  7. package/dist/native/androidProject/app/build.gradle.kts +1 -0
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +143 -4
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +26 -6
  10. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt +143 -17
  11. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +6 -0
  12. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +26 -0
  13. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +56 -1
  14. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +48 -8
  15. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfDeliveryState.kt +36 -0
  16. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfEventBuffer.kt +289 -0
  17. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/ProfilerNavigationState.kt +13 -0
  18. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ProfilerLifecycleTest.kt +51 -0
  19. package/dist/native/androidSetup.js +300 -8
  20. package/dist/native/bridge/WebBridge.js +481 -59
  21. package/dist/native/bridge/constants/NativeInterfaces.js +290 -44
  22. package/dist/native/bridge/errors.js +250 -26
  23. package/dist/native/bridge/hooks/index.js +125 -1
  24. package/dist/native/bridge/hooks/useCamera.js +347 -9
  25. package/dist/native/bridge/hooks/useDataProtection.js +200 -2
  26. package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
  27. package/dist/native/bridge/hooks/useFilePicker.js +429 -2
  28. package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
  29. package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
  30. package/dist/native/bridge/hooks/useIntent.js +95 -1
  31. package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
  32. package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
  33. package/dist/native/bridge/hooks/useNotification.js +318 -2
  34. package/dist/native/bridge/hooks/useSafeArea.js +84 -2
  35. package/dist/native/bridge/hooks/useVideoStream.js +434 -6
  36. package/dist/native/bridge/hooks/utils.js +27 -1
  37. package/dist/native/bridge/hooks.js +16 -1
  38. package/dist/native/bridge/perf/collectors/ApiCollector.js +85 -0
  39. package/dist/native/bridge/perf/collectors/CacheCollector.js +215 -0
  40. package/dist/native/bridge/perf/collectors/HookCollector.js +165 -0
  41. package/dist/native/bridge/perf/collectors/InsightsCollector.js +505 -0
  42. package/dist/native/bridge/perf/collectors/InteractionCollector.js +273 -0
  43. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +124 -0
  44. package/dist/native/bridge/perf/collectors/NavigationCollector.js +294 -0
  45. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +183 -0
  46. package/dist/native/bridge/perf/collectors/RenderCollector.js +284 -0
  47. package/dist/native/bridge/perf/collectors/ScrollCollector.js +111 -0
  48. package/dist/native/bridge/perf/collectors/ThreadCollector.js +111 -0
  49. package/dist/native/bridge/perf/core/constants.js +126 -0
  50. package/dist/native/bridge/perf/core/measure.js +189 -0
  51. package/dist/native/bridge/perf/core/session.js +100 -0
  52. package/dist/native/bridge/perf/core/store.js +554 -0
  53. package/dist/native/bridge/perf/core/summary.js +174 -0
  54. package/dist/native/bridge/perf/core/traceExport.js +240 -0
  55. package/dist/native/bridge/perf/core/waterfalls.js +146 -0
  56. package/dist/native/bridge/perf/index.js +402 -0
  57. package/dist/native/bridge/safeArea.js +53 -5
  58. package/dist/native/bridge/standardizedHooks.js +595 -82
  59. package/dist/native/bridge/useBaseHook.js +274 -38
  60. package/dist/native/bridge/utils/CameraUtils.js +201 -56
  61. package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
  62. package/dist/native/bridge/utils/NativeBridge.js +425 -141
  63. package/dist/native/buildAndroid/assets.js +510 -13
  64. package/dist/native/buildAndroid/build.js +296 -19
  65. package/dist/native/buildAndroid/config.js +87 -1
  66. package/dist/native/buildAndroid/index.js +374 -1
  67. package/dist/native/buildAppAndroid.js +34 -2
  68. package/dist/native/buildAppIos.js +62 -2
  69. package/dist/native/buildIos/assets.js +609 -8
  70. package/dist/native/buildIos/build.js +639 -20
  71. package/dist/native/buildIos/config.js +294 -12
  72. package/dist/native/buildIos/index.js +343 -6
  73. package/dist/native/buildIos/plugins.js +343 -16
  74. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  75. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  76. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  77. package/dist/native/internalPluginUtils.js +232 -7
  78. package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +19 -0
  79. package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +4 -0
  80. package/dist/native/iosnativeWebView/Sources/Core/Utils/BootTimingUtility.swift +5 -1
  81. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +10 -0
  82. package/dist/native/iosnativeWebView/Sources/Core/Utils/DisplayLinkPerfMonitor.swift +89 -0
  83. package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +373 -0
  84. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +35 -1
  85. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +24 -11
  86. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +9 -0
  87. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +126 -0
  88. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +75 -1
  89. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +13 -0
  90. package/dist/native/package.json +3 -0
  91. package/dist/native/plugin-bridge/PluginBridge.js +227 -1
  92. package/dist/native/pluginComposerAndroid.js +407 -23
  93. package/dist/native/pluginComposerIos.js +435 -11
  94. package/dist/native/renameAndroidProject.js +1094 -88
  95. package/dist/native/setupEmulatorIos.js +289 -22
  96. package/dist/native/setupServer.js +160 -19
  97. package/dist/native/terminalProgress.js +199 -11
  98. package/dist/native/utils.js +193 -15
  99. package/dist/offline/catalyst-sw.js +145 -1
  100. package/dist/offline/registerServiceWorker.js +33 -1
  101. package/dist/otel.js +375 -46
  102. package/dist/router/ClientRouter.js +14 -2
  103. package/dist/router/ServerRouter.js +9 -1
  104. package/dist/router.js +1 -1
  105. package/dist/scripts/build.js +112 -3
  106. package/dist/scripts/checkVersion.js +3 -1
  107. package/dist/scripts/cleanCache.js +30 -1
  108. package/dist/scripts/generateOfflineManifest.js +116 -1
  109. package/dist/scripts/loadEnvironmentVariables.js +32 -6
  110. package/dist/scripts/plugins.js +309 -1
  111. package/dist/scripts/preServerInit.js +13 -0
  112. package/dist/scripts/scriptUtils.js +54 -3
  113. package/dist/scripts/serve.js +51 -4
  114. package/dist/scripts/start.js +47 -10
  115. package/dist/scripts/validator.js +149 -1
  116. package/dist/sentry.cjs +127 -0
  117. package/dist/sentry.js +125 -5
  118. package/dist/server/expressServer.js +250 -5
  119. package/dist/server/manifestCache.js +36 -0
  120. package/dist/server/renderer/ChunkExtractor.js +144 -0
  121. package/dist/server/renderer/document/Body.jsx +59 -0
  122. package/dist/server/renderer/document/FastRefresh.jsx +20 -0
  123. package/dist/server/renderer/document/Head.jsx +60 -0
  124. package/dist/server/renderer/document/index.jsx +2 -0
  125. package/dist/server/renderer/extract.js +149 -31
  126. package/dist/server/renderer/handler.jsx +433 -0
  127. package/dist/server/renderer/index.js +8 -1
  128. package/dist/server/renderer/render.js +47 -0
  129. package/dist/server/utils/botDetectionMiddleware.js +29 -0
  130. package/dist/server/utils/cjsRequire.js +10 -0
  131. package/dist/server/utils/userAgentUtil.js +87 -9
  132. package/dist/server/utils/validator.js +164 -1
  133. package/dist/vite/FastRefresh.jsx +20 -0
  134. package/dist/vite/inject-cache-key-plugin.js +276 -0
  135. package/dist/vite/loadCustomViteConfig.js +22 -0
  136. package/dist/vite/manifest-categorization-plugin.js +455 -0
  137. package/dist/vite/node-loader.mjs +458 -0
  138. package/dist/vite/scssParams.js +21 -0
  139. package/dist/vite/vite.config.client.js +130 -0
  140. package/dist/vite/vite.config.js +422 -0
  141. package/dist/vite/vite.config.server.js +84 -0
  142. package/dist/web-router/components/MetaTag.jsx +34 -0
  143. package/dist/web-router/components/RouterDataProvider.jsx +332 -0
  144. package/dist/web-router/components/Split.jsx +200 -0
  145. package/dist/web-router/components/SplitInview.jsx +106 -0
  146. package/dist/web-router/components/SsrRequestContext.jsx +14 -0
  147. package/dist/web-router/context.jsx +12 -0
  148. package/dist/web-router/hooks.jsx +72 -0
  149. package/dist/web-router/types/components/MetaTag.d.ts +2 -0
  150. package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
  151. package/dist/web-router/types/context.d.ts +14 -0
  152. package/dist/web-router/types/hooks.d.ts +1 -0
  153. package/dist/web-router/types/index.d.ts +3 -0
  154. package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
  155. package/dist/web-router/utils/metaDataUtils.jsx +119 -0
  156. package/mcp_v2/conversion-tasks.json +35 -17
  157. package/mcp_v2/knowledge-base.json +124 -13
  158. package/mcp_v2/lib/helpers.js +10 -0
  159. package/mcp_v2/mcp.js +4 -4
  160. package/mcp_v2/package.json +1 -0
  161. package/mcp_v2/tools/build.js +116 -20
  162. package/mcp_v2/tools/conversion.js +106 -5
  163. package/mcp_v2/tools/tasks.js +18 -14
  164. package/package.json +35 -68
  165. package/babel.config.js +0 -32
  166. package/dist/index.js +0 -1
  167. package/dist/scripts/devBuild.js +0 -3
  168. package/dist/scripts/devServe.js +0 -3
  169. package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
  170. package/dist/scripts/registerAliases.js +0 -6
  171. package/dist/server/renderer/document/Body.js +0 -11
  172. package/dist/server/renderer/document/Head.js +0 -9
  173. package/dist/server/renderer/document/index.js +0 -1
  174. package/dist/server/renderer/handler.js +0 -47
  175. package/dist/server/startServer.js +0 -57
  176. package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
  177. package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
  178. package/dist/webpack/babel.config.client.js +0 -1
  179. package/dist/webpack/babel.config.ssr.js +0 -1
  180. package/dist/webpack/base.babel.js +0 -7
  181. package/dist/webpack/development.client.babel.js +0 -10
  182. package/dist/webpack/production.client.babel.js +0 -4
  183. package/dist/webpack/production.ssr.babel.js +0 -8
  184. package/dist/webpack/scssParams.js +0 -9
@@ -1,6 +1,434 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useVideoStream=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const STREAM_STATE_DEFAULT={zoom:null,minZoom:null,maxZoom:null,torchOn:false,fpsMin:null,fpsMax:null};const useVideoStream=({onQRDetected,webFallback}={})=>{const base=(0,_useBaseHook.useBaseHook)("useVideoStream",{hasWebFallback:true,webFallback});// Native stream state
2
- const[isStreaming,setIsStreaming]=(0,_react.useState)(false);const[streamState,setStreamState]=(0,_react.useState)(STREAM_STATE_DEFAULT);const onQRDetectedRef=(0,_react.useRef)(onQRDetected);onQRDetectedRef.current=onQRDetected;const viewfinderRef=(0,_react.useRef)(null);// Web fallback stream state — always declared (Rules of Hooks)
3
- const[isStreamingWeb,setIsStreamingWeb]=(0,_react.useState)(false);const[streamStateWeb,setStreamStateWeb]=(0,_react.useState)(STREAM_STATE_DEFAULT);const mediaStreamRef=(0,_react.useRef)(null);const facingModeRef=(0,_react.useRef)("environment");// Web fallback: stop active stream when webFallbackActive toggles off
4
- (0,_react.useEffect)(()=>{if(!base.webFallbackActive&&mediaStreamRef.current){mediaStreamRef.current.getTracks().forEach(t=>t.stop());mediaStreamRef.current=null;setIsStreamingWeb(false);setStreamStateWeb(STREAM_STATE_DEFAULT);}},[base.webFallbackActive]);// Web fallback: cleanup on unmount
5
- (0,_react.useEffect)(()=>{return()=>{if(mediaStreamRef.current){mediaStreamRef.current.getTracks().forEach(t=>t.stop());mediaStreamRef.current=null;}};},[]);const applyTrackConstraints=(0,_react.useCallback)(async constraints=>{const track=mediaStreamRef.current?.getVideoTracks()[0];if(!track)return;try{await track.applyConstraints(constraints);}catch(e){base.setError({message:`applyConstraints failed: ${e.message}`});}},[base]);const webStart=(0,_react.useCallback)(async(options={})=>{if(!window.isSecureContext){base.setError({message:"getUserMedia requires a secure context (HTTPS or localhost)"});return;}if(!navigator.mediaDevices?.getUserMedia){base.setError({message:"getUserMedia is not available in this browser"});return;}try{if(mediaStreamRef.current)return;base.setLoading(true);const constraints={video:{facingMode:{ideal:facingModeRef.current},...(options.fps?{frameRate:{ideal:options.fps}}:{})},audio:false};const stream=await navigator.mediaDevices.getUserMedia(constraints);mediaStreamRef.current=stream;setIsStreamingWeb(true);base.setLoading(false);const track=stream.getVideoTracks()[0];const caps=track?.getCapabilities?.()||{};setStreamStateWeb(prev=>({...prev,minZoom:caps.zoom?.min??null,maxZoom:caps.zoom?.max??null,zoom:caps.zoom?.min??null}));}catch(e){base.setLoading(false);base.setError({message:e.message||"getUserMedia failed"});}},[base]);const webStop=(0,_react.useCallback)(()=>{if(!mediaStreamRef.current)return;mediaStreamRef.current.getTracks().forEach(t=>t.stop());mediaStreamRef.current=null;setIsStreamingWeb(false);setStreamStateWeb(STREAM_STATE_DEFAULT);},[]);const webFlip=(0,_react.useCallback)(async()=>{facingModeRef.current=facingModeRef.current==="environment"?"user":"environment";if(mediaStreamRef.current){webStop();await webStart();}},[webStart,webStop]);const webSendCommand=(0,_react.useCallback)(async(type,value)=>{if(!isStreamingWeb){base.setError({message:`sendCommand('${type}') called but stream is not active`});return;}switch(type){case"zoom":{if(typeof value!=="number"||value<1.0){base.setError({message:`sendCommand zoom: value must be a multiplier >= 1.0, got ${value}`});return;}await applyTrackConstraints({advanced:[{zoom:value}]});setStreamStateWeb(prev=>({...prev,zoom:value}));break;}case"torch":{if(typeof value!=="boolean"){base.setError({message:`sendCommand torch: value must be boolean, got ${value}`});return;}await applyTrackConstraints({advanced:[{torch:value}]});setStreamStateWeb(prev=>({...prev,torchOn:value}));break;}case"fps":{if(typeof value!=="object"||value===null){base.setError({message:`sendCommand fps: value must be { min, max }, got ${value}`});return;}const{min=null,max=null}=value;const frameRate={};if(min!==null)frameRate.min=min;if(max!==null)frameRate.max=max;await applyTrackConstraints({frameRate});setStreamStateWeb(prev=>({...prev,fpsMin:min,fpsMax:max}));break;}default:base.setError({message:`sendCommand: unknown type '${type}'`});}},[isStreamingWeb,applyTrackConstraints,base]);// Native event registration — skipped on web (WebBridge absent or native bridge unavailable)
6
- (0,_react.useEffect)(()=>{if(typeof window==="undefined"||!window.WebBridge||!_NativeBridge.default.isAvailable())return;window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_READY,()=>{console.log("📹 Video stream ready");setIsStreaming(true);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_STOPPED,()=>{console.log("📹 Video stream stopped");setIsStreaming(false);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_QR_DETECTED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;console.log("📹 QR detected:",result);onQRDetectedRef.current?.(result);}catch(e){base.handleNativeError("Failed to parse QR data");}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_TORCH_CHANGED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;const enabled=result?.enabled??false;console.log("📹 Torch changed:",enabled);setStreamState(prev=>({...prev,torchOn:enabled}));}catch(e){console.warn("[useVideoStream] Failed to parse ON_TORCH_CHANGED data:",e);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_ZOOM_CHANGED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;const zoom=result?.zoomLevel??null;const minZoom=result?.minZoom??null;const maxZoom=result?.maxZoom??null;console.log("📹 Zoom changed:",zoom,`(min=${minZoom} max=${maxZoom})`);setStreamState(prev=>({...prev,zoom,minZoom,maxZoom}));}catch(e){console.warn("[useVideoStream] Failed to parse ON_ZOOM_CHANGED data:",e);}});const handleBeforeUnload=()=>{_NativeBridge.default.videoStream.stop();};window.addEventListener("beforeunload",handleBeforeUnload);return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_READY);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_STOPPED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_QR_DETECTED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_TORCH_CHANGED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_ZOOM_CHANGED);window.removeEventListener("beforeunload",handleBeforeUnload);_NativeBridge.default.videoStream.stop();};},[base.handleNativeError]);const nativeStart=(0,_react.useCallback)((options={})=>{base.executeOperation(()=>{console.log("[useVideoStream] start() options:",JSON.stringify(options));_NativeBridge.default.videoStream.start(options);},"start video stream");},[base.executeOperation]);const nativeStop=(0,_react.useCallback)(()=>{console.log("[useVideoStream] stop()");_NativeBridge.default.videoStream.stop();setIsStreaming(false);},[]);const nativeFlip=(0,_react.useCallback)(()=>{console.log("[useVideoStream] flip()");_NativeBridge.default.videoStream.flip();},[]);const nativeSendCommand=(0,_react.useCallback)((type,value)=>{if(!isStreaming){base.setError({message:`sendCommand('${type}') called but stream is not active`});return;}console.log(`[useVideoStream] sendCommand(${type}, ${value})`);switch(type){case"zoom":{if(typeof value!=="number"||value<1.0){base.setError({message:`sendCommand zoom: value must be a multiplier >= 1.0 (e.g. 1.0=1x, 2.0=2x), got ${value}`});return;}_NativeBridge.default.videoStream.setZoom(value);break;}case"torch":{if(typeof value!=="boolean"){base.setError({message:`sendCommand torch: value must be boolean, got ${value}`});return;}_NativeBridge.default.videoStream.setTorch(value);break;}case"fps":{if(typeof value!=="object"||value===null){base.setError({message:`sendCommand fps: value must be { min, max }, got ${value}`});return;}const{min=null,max=null}=value;if(min!==null&&(typeof min!=="number"||min<1)){base.setError({message:`sendCommand fps: min must be a positive number, got ${min}`});return;}if(max!==null&&(typeof max!=="number"||max<1)){base.setError({message:`sendCommand fps: max must be a positive number, got ${max}`});return;}if(min!==null&&max!==null&&min>max){base.setError({message:`sendCommand fps: min (${min}) must be <= max (${max})`});return;}_NativeBridge.default.videoStream.setFps(min,max);setStreamState(prev=>({...prev,fpsMin:min,fpsMax:max}));break;}default:base.setError({message:`sendCommand: unknown type '${type}'`});}},[isStreaming,base.setError]);if(typeof window==="undefined"){return{isStreaming:false,streamState:STREAM_STATE_DEFAULT,error:null,isNative:false,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:()=>{},start:()=>{},stop:()=>{},sendCommand:()=>{},flip:()=>{},clearError:()=>{}};}if(base.webFallbackDisabled){return{isStreaming:false,streamState:STREAM_STATE_DEFAULT,error:null,isNative:false,webFallbackActive:false,webFallbackDisabled:true,setWebFallback:base.setWebFallback,start:()=>{},stop:()=>{},sendCommand:()=>{},flip:()=>{},clearError:()=>{}};}if(base.webFallbackActive){return{isStreaming:isStreamingWeb,streamState:streamStateWeb,error:base.error,isNative:false,webFallbackActive:true,webFallbackDisabled:false,setWebFallback:base.setWebFallback,mediaStream:mediaStreamRef.current,viewfinderRef,start:webStart,stop:webStop,sendCommand:webSendCommand,flip:webFlip,clearError:base.clearError};}return{isStreaming,streamState,error:base.error,isNative:base.isNative,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:base.setWebFallback,viewfinderRef,start:nativeStart,stop:nativeStop,sendCommand:nativeSendCommand,flip:nativeFlip,clearError:base.clearError};};exports.useVideoStream=useVideoStream;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useVideoStream = void 0;
7
+ var _react = require("react");
8
+ var _NativeBridge = _interopRequireDefault(require("../utils/NativeBridge.js"));
9
+ var _NativeInterfaces = require("../constants/NativeInterfaces.js");
10
+ var _useBaseHook = require("../useBaseHook.js");
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ /* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */
13
+
14
+ const STREAM_STATE_DEFAULT = {
15
+ zoom: null,
16
+ minZoom: null,
17
+ maxZoom: null,
18
+ torchOn: false,
19
+ fpsMin: null,
20
+ fpsMax: null
21
+ };
22
+ const useVideoStream = ({
23
+ onQRDetected,
24
+ webFallback
25
+ } = {}) => {
26
+ const base = (0, _useBaseHook.useBaseHook)("useVideoStream", {
27
+ hasWebFallback: true,
28
+ webFallback
29
+ });
30
+
31
+ // Native stream state
32
+ const [isStreaming, setIsStreaming] = (0, _react.useState)(false);
33
+ const [streamState, setStreamState] = (0, _react.useState)(STREAM_STATE_DEFAULT);
34
+ const onQRDetectedRef = (0, _react.useRef)(onQRDetected);
35
+ onQRDetectedRef.current = onQRDetected;
36
+ const viewfinderRef = (0, _react.useRef)(null);
37
+
38
+ // Web fallback stream state — always declared (Rules of Hooks)
39
+ const [isStreamingWeb, setIsStreamingWeb] = (0, _react.useState)(false);
40
+ const [streamStateWeb, setStreamStateWeb] = (0, _react.useState)(STREAM_STATE_DEFAULT);
41
+ const mediaStreamRef = (0, _react.useRef)(null);
42
+ const facingModeRef = (0, _react.useRef)("environment");
43
+
44
+ // Web fallback: stop active stream when webFallbackActive toggles off
45
+ (0, _react.useEffect)(() => {
46
+ if (!base.webFallbackActive && mediaStreamRef.current) {
47
+ mediaStreamRef.current.getTracks().forEach(t => t.stop());
48
+ mediaStreamRef.current = null;
49
+ setIsStreamingWeb(false);
50
+ setStreamStateWeb(STREAM_STATE_DEFAULT);
51
+ }
52
+ }, [base.webFallbackActive]);
53
+
54
+ // Web fallback: cleanup on unmount
55
+ (0, _react.useEffect)(() => {
56
+ return () => {
57
+ if (mediaStreamRef.current) {
58
+ mediaStreamRef.current.getTracks().forEach(t => t.stop());
59
+ mediaStreamRef.current = null;
60
+ }
61
+ };
62
+ }, []);
63
+ const applyTrackConstraints = (0, _react.useCallback)(async constraints => {
64
+ const track = mediaStreamRef.current?.getVideoTracks()[0];
65
+ if (!track) return;
66
+ try {
67
+ await track.applyConstraints(constraints);
68
+ } catch (e) {
69
+ base.setError({
70
+ message: `applyConstraints failed: ${e.message}`
71
+ });
72
+ }
73
+ }, [base]);
74
+ const webStart = (0, _react.useCallback)(async (options = {}) => {
75
+ if (!window.isSecureContext) {
76
+ base.setError({
77
+ message: "getUserMedia requires a secure context (HTTPS or localhost)"
78
+ });
79
+ return;
80
+ }
81
+ if (!navigator.mediaDevices?.getUserMedia) {
82
+ base.setError({
83
+ message: "getUserMedia is not available in this browser"
84
+ });
85
+ return;
86
+ }
87
+ try {
88
+ if (mediaStreamRef.current) return;
89
+ base.setLoading(true);
90
+ const constraints = {
91
+ video: {
92
+ facingMode: {
93
+ ideal: facingModeRef.current
94
+ },
95
+ ...(options.fps ? {
96
+ frameRate: {
97
+ ideal: options.fps
98
+ }
99
+ } : {})
100
+ },
101
+ audio: false
102
+ };
103
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
104
+ mediaStreamRef.current = stream;
105
+ setIsStreamingWeb(true);
106
+ base.setLoading(false);
107
+ const track = stream.getVideoTracks()[0];
108
+ const caps = track?.getCapabilities?.() || {};
109
+ setStreamStateWeb(prev => ({
110
+ ...prev,
111
+ minZoom: caps.zoom?.min ?? null,
112
+ maxZoom: caps.zoom?.max ?? null,
113
+ zoom: caps.zoom?.min ?? null
114
+ }));
115
+ } catch (e) {
116
+ base.setLoading(false);
117
+ base.setError({
118
+ message: e.message || "getUserMedia failed"
119
+ });
120
+ }
121
+ }, [base]);
122
+ const webStop = (0, _react.useCallback)(() => {
123
+ if (!mediaStreamRef.current) return;
124
+ mediaStreamRef.current.getTracks().forEach(t => t.stop());
125
+ mediaStreamRef.current = null;
126
+ setIsStreamingWeb(false);
127
+ setStreamStateWeb(STREAM_STATE_DEFAULT);
128
+ }, []);
129
+ const webFlip = (0, _react.useCallback)(async () => {
130
+ facingModeRef.current = facingModeRef.current === "environment" ? "user" : "environment";
131
+ if (mediaStreamRef.current) {
132
+ webStop();
133
+ await webStart();
134
+ }
135
+ }, [webStart, webStop]);
136
+ const webSendCommand = (0, _react.useCallback)(async (type, value) => {
137
+ if (!isStreamingWeb) {
138
+ base.setError({
139
+ message: `sendCommand('${type}') called but stream is not active`
140
+ });
141
+ return;
142
+ }
143
+ switch (type) {
144
+ case "zoom":
145
+ {
146
+ if (typeof value !== "number" || value < 1.0) {
147
+ base.setError({
148
+ message: `sendCommand zoom: value must be a multiplier >= 1.0, got ${value}`
149
+ });
150
+ return;
151
+ }
152
+ await applyTrackConstraints({
153
+ advanced: [{
154
+ zoom: value
155
+ }]
156
+ });
157
+ setStreamStateWeb(prev => ({
158
+ ...prev,
159
+ zoom: value
160
+ }));
161
+ break;
162
+ }
163
+ case "torch":
164
+ {
165
+ if (typeof value !== "boolean") {
166
+ base.setError({
167
+ message: `sendCommand torch: value must be boolean, got ${value}`
168
+ });
169
+ return;
170
+ }
171
+ await applyTrackConstraints({
172
+ advanced: [{
173
+ torch: value
174
+ }]
175
+ });
176
+ setStreamStateWeb(prev => ({
177
+ ...prev,
178
+ torchOn: value
179
+ }));
180
+ break;
181
+ }
182
+ case "fps":
183
+ {
184
+ if (typeof value !== "object" || value === null) {
185
+ base.setError({
186
+ message: `sendCommand fps: value must be { min, max }, got ${value}`
187
+ });
188
+ return;
189
+ }
190
+ const {
191
+ min = null,
192
+ max = null
193
+ } = value;
194
+ const frameRate = {};
195
+ if (min !== null) frameRate.min = min;
196
+ if (max !== null) frameRate.max = max;
197
+ await applyTrackConstraints({
198
+ frameRate
199
+ });
200
+ setStreamStateWeb(prev => ({
201
+ ...prev,
202
+ fpsMin: min,
203
+ fpsMax: max
204
+ }));
205
+ break;
206
+ }
207
+ default:
208
+ base.setError({
209
+ message: `sendCommand: unknown type '${type}'`
210
+ });
211
+ }
212
+ }, [isStreamingWeb, applyTrackConstraints, base]);
213
+
214
+ // Native event registration — skipped on web (WebBridge absent or native bridge unavailable)
215
+ (0, _react.useEffect)(() => {
216
+ if (typeof window === "undefined" || !window.WebBridge || !_NativeBridge.default.isAvailable()) return;
217
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_READY, () => {
218
+ console.log("📹 Video stream ready");
219
+ setIsStreaming(true);
220
+ });
221
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_STOPPED, () => {
222
+ console.log("📹 Video stream stopped");
223
+ setIsStreaming(false);
224
+ });
225
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_QR_DETECTED, data => {
226
+ try {
227
+ const result = typeof data === "string" ? JSON.parse(data) : data;
228
+ console.log("📹 QR detected:", result);
229
+ onQRDetectedRef.current?.(result);
230
+ } catch (e) {
231
+ base.handleNativeError("Failed to parse QR data");
232
+ }
233
+ });
234
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_TORCH_CHANGED, data => {
235
+ try {
236
+ const result = typeof data === "string" ? JSON.parse(data) : data;
237
+ const enabled = result?.enabled ?? false;
238
+ console.log("📹 Torch changed:", enabled);
239
+ setStreamState(prev => ({
240
+ ...prev,
241
+ torchOn: enabled
242
+ }));
243
+ } catch (e) {
244
+ console.warn("[useVideoStream] Failed to parse ON_TORCH_CHANGED data:", e);
245
+ }
246
+ });
247
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_ZOOM_CHANGED, data => {
248
+ try {
249
+ const result = typeof data === "string" ? JSON.parse(data) : data;
250
+ const zoom = result?.zoomLevel ?? null;
251
+ const minZoom = result?.minZoom ?? null;
252
+ const maxZoom = result?.maxZoom ?? null;
253
+ console.log("📹 Zoom changed:", zoom, `(min=${minZoom} max=${maxZoom})`);
254
+ setStreamState(prev => ({
255
+ ...prev,
256
+ zoom,
257
+ minZoom,
258
+ maxZoom
259
+ }));
260
+ } catch (e) {
261
+ console.warn("[useVideoStream] Failed to parse ON_ZOOM_CHANGED data:", e);
262
+ }
263
+ });
264
+ const handleBeforeUnload = () => {
265
+ _NativeBridge.default.videoStream.stop();
266
+ };
267
+ window.addEventListener("beforeunload", handleBeforeUnload);
268
+ return () => {
269
+ window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_READY);
270
+ window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_STOPPED);
271
+ window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_QR_DETECTED);
272
+ window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_TORCH_CHANGED);
273
+ window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_ZOOM_CHANGED);
274
+ window.removeEventListener("beforeunload", handleBeforeUnload);
275
+ _NativeBridge.default.videoStream.stop();
276
+ };
277
+ }, [base.handleNativeError]);
278
+ const nativeStart = (0, _react.useCallback)((options = {}) => {
279
+ base.executeOperation(() => {
280
+ console.log("[useVideoStream] start() options:", JSON.stringify(options));
281
+ _NativeBridge.default.videoStream.start(options);
282
+ }, "start video stream");
283
+ }, [base.executeOperation]);
284
+ const nativeStop = (0, _react.useCallback)(() => {
285
+ console.log("[useVideoStream] stop()");
286
+ _NativeBridge.default.videoStream.stop();
287
+ setIsStreaming(false);
288
+ }, []);
289
+ const nativeFlip = (0, _react.useCallback)(() => {
290
+ console.log("[useVideoStream] flip()");
291
+ _NativeBridge.default.videoStream.flip();
292
+ }, []);
293
+ const nativeSendCommand = (0, _react.useCallback)((type, value) => {
294
+ if (!isStreaming) {
295
+ base.setError({
296
+ message: `sendCommand('${type}') called but stream is not active`
297
+ });
298
+ return;
299
+ }
300
+ console.log(`[useVideoStream] sendCommand(${type}, ${value})`);
301
+ switch (type) {
302
+ case "zoom":
303
+ {
304
+ if (typeof value !== "number" || value < 1.0) {
305
+ base.setError({
306
+ message: `sendCommand zoom: value must be a multiplier >= 1.0 (e.g. 1.0=1x, 2.0=2x), got ${value}`
307
+ });
308
+ return;
309
+ }
310
+ _NativeBridge.default.videoStream.setZoom(value);
311
+ break;
312
+ }
313
+ case "torch":
314
+ {
315
+ if (typeof value !== "boolean") {
316
+ base.setError({
317
+ message: `sendCommand torch: value must be boolean, got ${value}`
318
+ });
319
+ return;
320
+ }
321
+ _NativeBridge.default.videoStream.setTorch(value);
322
+ break;
323
+ }
324
+ case "fps":
325
+ {
326
+ if (typeof value !== "object" || value === null) {
327
+ base.setError({
328
+ message: `sendCommand fps: value must be { min, max }, got ${value}`
329
+ });
330
+ return;
331
+ }
332
+ const {
333
+ min = null,
334
+ max = null
335
+ } = value;
336
+ if (min !== null && (typeof min !== "number" || min < 1)) {
337
+ base.setError({
338
+ message: `sendCommand fps: min must be a positive number, got ${min}`
339
+ });
340
+ return;
341
+ }
342
+ if (max !== null && (typeof max !== "number" || max < 1)) {
343
+ base.setError({
344
+ message: `sendCommand fps: max must be a positive number, got ${max}`
345
+ });
346
+ return;
347
+ }
348
+ if (min !== null && max !== null && min > max) {
349
+ base.setError({
350
+ message: `sendCommand fps: min (${min}) must be <= max (${max})`
351
+ });
352
+ return;
353
+ }
354
+ _NativeBridge.default.videoStream.setFps(min, max);
355
+ setStreamState(prev => ({
356
+ ...prev,
357
+ fpsMin: min,
358
+ fpsMax: max
359
+ }));
360
+ break;
361
+ }
362
+ default:
363
+ base.setError({
364
+ message: `sendCommand: unknown type '${type}'`
365
+ });
366
+ }
367
+ }, [isStreaming, base.setError]);
368
+ if (typeof window === "undefined") {
369
+ return {
370
+ isStreaming: false,
371
+ streamState: STREAM_STATE_DEFAULT,
372
+ error: null,
373
+ isNative: false,
374
+ webFallbackActive: false,
375
+ webFallbackDisabled: false,
376
+ setWebFallback: () => {},
377
+ start: () => {},
378
+ stop: () => {},
379
+ sendCommand: () => {},
380
+ flip: () => {},
381
+ clearError: () => {}
382
+ };
383
+ }
384
+ if (base.webFallbackDisabled) {
385
+ return {
386
+ isStreaming: false,
387
+ streamState: STREAM_STATE_DEFAULT,
388
+ error: null,
389
+ isNative: false,
390
+ webFallbackActive: false,
391
+ webFallbackDisabled: true,
392
+ setWebFallback: base.setWebFallback,
393
+ start: () => {},
394
+ stop: () => {},
395
+ sendCommand: () => {},
396
+ flip: () => {},
397
+ clearError: () => {}
398
+ };
399
+ }
400
+ if (base.webFallbackActive) {
401
+ return {
402
+ isStreaming: isStreamingWeb,
403
+ streamState: streamStateWeb,
404
+ error: base.error,
405
+ isNative: false,
406
+ webFallbackActive: true,
407
+ webFallbackDisabled: false,
408
+ setWebFallback: base.setWebFallback,
409
+ mediaStream: mediaStreamRef.current,
410
+ viewfinderRef,
411
+ start: webStart,
412
+ stop: webStop,
413
+ sendCommand: webSendCommand,
414
+ flip: webFlip,
415
+ clearError: base.clearError
416
+ };
417
+ }
418
+ return {
419
+ isStreaming,
420
+ streamState,
421
+ error: base.error,
422
+ isNative: base.isNative,
423
+ webFallbackActive: false,
424
+ webFallbackDisabled: false,
425
+ setWebFallback: base.setWebFallback,
426
+ viewfinderRef,
427
+ start: nativeStart,
428
+ stop: nativeStop,
429
+ sendCommand: nativeSendCommand,
430
+ flip: nativeFlip,
431
+ clearError: base.clearError
432
+ };
433
+ };
434
+ exports.useVideoStream = useVideoStream;
@@ -1 +1,27 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.registerNativeHandlers=exports.parseNativePayload=exports.noop=exports.createSSRUnavailable=void 0;/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const noop=()=>{};exports.noop=noop;const createSSRUnavailable=methodName=>async()=>{throw new Error(`${methodName} is not available in SSR environment`);};exports.createSSRUnavailable=createSSRUnavailable;const parseNativePayload=payload=>{if(payload==null)return null;if(typeof payload!=="string")return payload;return JSON.parse(payload);};exports.parseNativePayload=parseNativePayload;const registerNativeHandlers=handlers=>{handlers.forEach(([event,handler])=>window.WebBridge.register(event,handler));return()=>{handlers.forEach(([event])=>window.WebBridge.unregister(event));};};exports.registerNativeHandlers=registerNativeHandlers;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.registerNativeHandlers = exports.parseNativePayload = exports.noop = exports.createSSRUnavailable = void 0;
7
+ /* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */
8
+
9
+ const noop = () => {};
10
+ exports.noop = noop;
11
+ const createSSRUnavailable = methodName => async () => {
12
+ throw new Error(`${methodName} is not available in SSR environment`);
13
+ };
14
+ exports.createSSRUnavailable = createSSRUnavailable;
15
+ const parseNativePayload = payload => {
16
+ if (payload == null) return null;
17
+ if (typeof payload !== "string") return payload;
18
+ return JSON.parse(payload);
19
+ };
20
+ exports.parseNativePayload = parseNativePayload;
21
+ const registerNativeHandlers = handlers => {
22
+ handlers.forEach(([event, handler]) => window.WebBridge.register(event, handler));
23
+ return () => {
24
+ handlers.forEach(([event]) => window.WebBridge.unregister(event));
25
+ };
26
+ };
27
+ exports.registerNativeHandlers = registerNativeHandlers;
@@ -1 +1,16 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});var _index=require("./hooks/index.js");Object.keys(_index).forEach(function(key){if(key==="default"||key==="__esModule")return;if(key in exports&&exports[key]===_index[key])return;Object.defineProperty(exports,key,{enumerable:true,get:function(){return _index[key];}});});
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _index = require("./hooks/index.js");
7
+ Object.keys(_index).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _index[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _index[key];
14
+ }
15
+ });
16
+ });
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ApiCollector = void 0;
7
+ var _constants = require("../core/constants.js");
8
+ /**
9
+ * ApiCollector.js
10
+ *
11
+ * Instruments native API call timing — how long each @JavascriptInterface
12
+ * method takes from native receipt until its immediate native handler work is
13
+ * dispatched. It does not measure JS-to-native transport, JS callback delivery,
14
+ * or UI completion.
15
+ *
16
+ * Native events received (via __catalystPerfBatch drain):
17
+ * api-call { callId, method, nativeStartMs, nativeEndMs, durationMs, thread }
18
+ *
19
+ * Spans emitted (one per call, on Catalyst > Native API track):
20
+ * catalyst:api/<method> — nativeStart → nativeEnd
21
+ * color: primary-dark (<50ms), secondary (<200ms), error (>200ms)
22
+ * properties: method, durationMs, callId, thread, simulatorValid: false
23
+ *
24
+ * Reading the track:
25
+ * Blue = fast native call (<50ms) — expected for sync APIs (getDeviceInfo, haptic)
26
+ * Grey = medium native call (<200ms) — native work worth inspecting
27
+ * Red = slow native call (>200ms) — native work worth inspecting
28
+ */
29
+
30
+ class ApiCollector {
31
+ constructor(measure, nativeToWeb) {
32
+ this._measure = measure;
33
+ this._nativeToWeb = nativeToWeb;
34
+ }
35
+ init() {
36
+ // No web-side observers — all events come from native buffer drain
37
+ }
38
+
39
+ /** Called by index.js to feed the normalized perf store. */
40
+ setWaterfallSource(fn) {
41
+ this._notifyWaterfall = fn;
42
+ }
43
+ setInteractionSource(fn) {
44
+ this._getInteraction = fn;
45
+ }
46
+ onBatchEvent(event) {
47
+ if (event.type !== "api-call") return;
48
+ const {
49
+ method,
50
+ callId,
51
+ nativeStartMs,
52
+ nativeEndMs,
53
+ durationMs,
54
+ thread
55
+ } = event;
56
+ const startTime = this._nativeToWeb(nativeStartMs);
57
+ const endTime = this._nativeToWeb(nativeEndMs);
58
+ const interactionId = this._getInteraction?.()?.interactionIdForRange(startTime, endTime) ?? null;
59
+ const color = durationMs > 200 ? _constants.COLOR.ERROR : durationMs > 50 ? _constants.COLOR.SECONDARY : _constants.COLOR.PRIMARY_DARK;
60
+ const startMark = `${_constants.PREFIX.NATIVE_API}:${method}:start:${Math.round(startTime)}`;
61
+ performance.mark(startMark, {
62
+ startTime
63
+ });
64
+ this._notifyWaterfall?.({
65
+ method,
66
+ durationMs,
67
+ callId,
68
+ thread: thread ?? null,
69
+ startTime,
70
+ endTime,
71
+ sessionId: interactionId,
72
+ parentOperation: `Native execution: ${method}`
73
+ });
74
+ this._measure.emit(`${interactionId ? `[${interactionId}] ` : ""}Native API: ${method} - ${durationMs}ms`, startMark, endTime, {
75
+ interactionId,
76
+ parentOperation: `Native execution: ${method}`,
77
+ method,
78
+ durationMs,
79
+ callId,
80
+ thread: thread ?? null,
81
+ simulatorValid: false
82
+ }, _constants.TRACK.NATIVE_API, color);
83
+ }
84
+ }
85
+ exports.ApiCollector = ApiCollector;