@@ -3,29 +3,26 @@ import type {
33 TestCollectorEvents ,
44 BundlerEvents ,
55 TestExecutionOptions ,
6+ ConsoleLevel ,
7+ ConsoleEvent ,
68} from '@react-native-harness/bridge' ;
79import { getBridgeClient } from '@react-native-harness/bridge/client' ;
810import { store } from '../ui/state.js' ;
911import { getTestRunner , TestRunner } from '../runner/index.js' ;
1012import { getTestCollector , TestCollector } from '../collector/index.js' ;
1113import { combineEventEmitters , EventEmitter } from '../utils/emitter.js' ;
14+ import { createRateLimiter } from '../utils/rateLimiter.js' ;
1215import { getWSServer } from './getWSServer.js' ;
1316import { getBundler , evaluateModule , Bundler } from '../bundler/index.js' ;
1417import { markTestsAsSkippedByName } from '../filtering/index.js' ;
1518import { setup } from '../render/setup.js' ;
1619import { runSetupFiles } from './setup-files.js' ;
1720import { setClient } from './store.js' ;
1821
19- type ConsoleLevel = 'log' | 'warn' | 'error' | 'info' | 'debug' ;
20- type EmitEventFn = (
21- type : string ,
22- data : {
23- type : string ;
24- level : ConsoleLevel ;
25- args : string [ ] ;
26- timestamp : number ;
27- }
28- ) => void ;
22+ type EmitEventFn = ( type : ConsoleEvent [ 'type' ] , data : ConsoleEvent ) => void ;
23+
24+ // Rate limit: 100 messages per second
25+ const CONSOLE_RATE_LIMIT = { maxCalls : 100 , windowMs : 1000 } ;
2926
3027// Console forwarding setup - intercepts console calls and emits events to host
3128const setupConsoleForwarding = ( emitEvent : EmitEventFn ) : ( ( ) => void ) => {
@@ -37,19 +34,44 @@ const setupConsoleForwarding = (emitEvent: EmitEventFn): (() => void) => {
3734 debug : console . debug ,
3835 } ;
3936
37+ const rateLimiter = createRateLimiter ( CONSOLE_RATE_LIMIT ) ;
38+ let droppedCount = 0 ;
39+ let lastDropWarningTime = 0 ;
40+
4041 const createForwarder =
4142 ( level : ConsoleLevel ) =>
4243 ( ...args : unknown [ ] ) => {
4344 // Call original console method
4445 originalConsole [ level ] ( ...args ) ;
46+
47+ // Check rate limit before forwarding
48+ if ( ! rateLimiter . tryAcquire ( ) ) {
49+ droppedCount ++ ;
50+ // Log a warning at most once per second about dropped messages
51+ const now = Date . now ( ) ;
52+ if ( now - lastDropWarningTime > 1000 ) {
53+ lastDropWarningTime = now ;
54+ originalConsole . warn (
55+ `[Harness] Console rate limit exceeded, ${ droppedCount } messages dropped`
56+ ) ;
57+ }
58+ return ;
59+ }
60+
4561 // Forward to host via bridge
4662 try {
4763 emitEvent ( 'console' , {
4864 type : 'console' ,
4965 level,
5066 args : args . map ( ( arg ) => {
5167 try {
52- return typeof arg === 'object' ? JSON . stringify ( arg ) : String ( arg ) ;
68+ if ( arg instanceof Error ) {
69+ return arg . stack ?? `${ arg . name } : ${ arg . message } ` ;
70+ }
71+ if ( typeof arg === 'object' && arg !== null ) {
72+ return JSON . stringify ( arg ) ;
73+ }
74+ return String ( arg ) ;
5375 } catch {
5476 return String ( arg ) ;
5577 }
@@ -70,6 +92,7 @@ const setupConsoleForwarding = (emitEvent: EmitEventFn): (() => void) => {
7092 // Return cleanup function to restore original console
7193 return ( ) => {
7294 Object . assign ( console , originalConsole ) ;
95+ rateLimiter . reset ( ) ;
7396 } ;
7497} ;
7598
@@ -116,11 +139,7 @@ export const getClient = async () => {
116139
117140 // Setup console forwarding to emit console events to host
118141 cleanupConsole = setupConsoleForwarding ( ( type , data ) => {
119- // Use type assertion since 'console' is a new event type not in the current type definitions
120- ( client . rpc . emitEvent as ( type : string , data : unknown ) => void ) (
121- type ,
122- data
123- ) ;
142+ client . rpc . emitEvent ( type , data ) ;
124143 } ) ;
125144
126145 await runSetupFiles ( {
0 commit comments