@@ -14,11 +14,73 @@ export interface InputMessage {
1414}
1515
1616export class InputHandler {
17+ private lastMoveTime = 0 ;
18+ private lastScrollTime = 0 ;
19+ private pendingMove : InputMessage | null = null ;
20+ private pendingScroll : InputMessage | null = null ;
21+ private moveTimer : ReturnType < typeof setTimeout > | null = null ;
22+ private scrollTimer : ReturnType < typeof setTimeout > | null = null ;
23+
1724 constructor ( ) {
1825 mouse . config . mouseSpeed = 1000 ;
1926 }
2027
2128 async handleMessage ( msg : InputMessage ) {
29+ // Validation: Text length sanitation
30+ if ( msg . text && msg . text . length > 500 ) {
31+ msg . text = msg . text . substring ( 0 , 500 ) ;
32+ }
33+
34+ // Validation: Sane bounds for coordinates
35+ const MAX_COORD = 2000 ;
36+ if ( typeof msg . dx === 'number' && Number . isFinite ( msg . dx ) ) {
37+ msg . dx = Math . max ( - MAX_COORD , Math . min ( MAX_COORD , msg . dx ) ) ;
38+ }
39+ if ( typeof msg . dy === 'number' && Number . isFinite ( msg . dy ) ) {
40+ msg . dy = Math . max ( - MAX_COORD , Math . min ( MAX_COORD , msg . dy ) ) ;
41+ }
42+
43+ // Throttling: Limit high-frequency events to ~125fps (8ms)
44+ if ( msg . type === 'move' ) {
45+ const now = Date . now ( ) ;
46+ if ( now - this . lastMoveTime < 8 ) {
47+ this . pendingMove = msg ;
48+ if ( ! this . moveTimer ) {
49+ this . moveTimer = setTimeout ( ( ) => {
50+ this . moveTimer = null ;
51+ if ( this . pendingMove ) {
52+ const pending = this . pendingMove ;
53+ this . pendingMove = null ;
54+ this . handleMessage ( pending ) . catch ( ( err ) => {
55+ console . error ( 'Error processing pending move event:' , err ) ;
56+ } ) ;
57+ }
58+ } , 8 ) ;
59+ }
60+ return ;
61+ }
62+ this . lastMoveTime = now ;
63+ } else if ( msg . type === 'scroll' ) {
64+ const now = Date . now ( ) ;
65+ if ( now - this . lastScrollTime < 8 ) {
66+ this . pendingScroll = msg ;
67+ if ( ! this . scrollTimer ) {
68+ this . scrollTimer = setTimeout ( ( ) => {
69+ this . scrollTimer = null ;
70+ if ( this . pendingScroll ) {
71+ const pending = this . pendingScroll ;
72+ this . pendingScroll = null ;
73+ this . handleMessage ( pending ) . catch ( ( err ) => {
74+ console . error ( 'Error processing pending move event:' , err ) ;
75+ } ) ;
76+ }
77+ } , 8 ) ;
78+ }
79+ return ;
80+ }
81+ this . lastScrollTime = now ;
82+ }
83+
2284 switch ( msg . type ) {
2385 case 'move' :
2486 if (
@@ -38,7 +100,13 @@ export class InputHandler {
38100
39101 case 'click' :
40102 if ( msg . button ) {
41- const btn = msg . button === 'left' ? Button . LEFT : msg . button === 'right' ? Button . RIGHT : Button . MIDDLE ;
103+ const btn =
104+ msg . button === 'left'
105+ ? Button . LEFT
106+ : msg . button === 'right'
107+ ? Button . RIGHT
108+ : Button . MIDDLE ;
109+
42110 if ( msg . press ) {
43111 await mouse . pressButton ( btn ) ;
44112 } else {
@@ -83,7 +151,10 @@ export class InputHandler {
83151
84152 const scaledDelta =
85153 Math . sign ( msg . delta ) *
86- Math . min ( Math . abs ( msg . delta ) * sensitivityFactor , MAX_ZOOM_STEP ) ;
154+ Math . min (
155+ Math . abs ( msg . delta ) * sensitivityFactor ,
156+ MAX_ZOOM_STEP
157+ ) ;
87158
88159 const amount = Math . round ( - scaledDelta ) ;
89160
@@ -106,6 +177,7 @@ export class InputHandler {
106177 if ( msg . key ) {
107178 console . log ( `Processing key: ${ msg . key } ` ) ;
108179 const nutKey = KEY_MAP [ msg . key . toLowerCase ( ) ] ;
180+
109181 if ( nutKey !== undefined ) {
110182 await keyboard . type ( nutKey ) ;
111183 } else if ( msg . key . length === 1 ) {
@@ -119,9 +191,11 @@ export class InputHandler {
119191 case 'combo' :
120192 if ( msg . keys && msg . keys . length > 0 ) {
121193 const nutKeys : ( Key | string ) [ ] = [ ] ;
194+
122195 for ( const k of msg . keys ) {
123196 const lowerKey = k . toLowerCase ( ) ;
124197 const nutKey = KEY_MAP [ lowerKey ] ;
198+
125199 if ( nutKey !== undefined ) {
126200 nutKeys . push ( nutKey ) ;
127201 } else if ( lowerKey . length === 1 ) {
@@ -141,15 +215,17 @@ export class InputHandler {
141215
142216 try {
143217 for ( const k of nutKeys ) {
144- if ( typeof k === " string" ) {
218+ if ( typeof k === ' string' ) {
145219 await keyboard . type ( k ) ;
146220 } else {
147221 await keyboard . pressKey ( k ) ;
148222 pressedKeys . push ( k ) ;
149223 }
150224 }
151225
152- await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ;
226+ await new Promise ( resolve =>
227+ setTimeout ( resolve , 10 )
228+ ) ;
153229 } finally {
154230 for ( const k of pressedKeys . reverse ( ) ) {
155231 await keyboard . releaseKey ( k ) ;
0 commit comments