1212import { Effect } from 'effect' ;
1313import type { CdpSessionId , TargetId } from '../../shared/cloudflare-detection.js' ;
1414import { SolverEvents } from './cf-services.js' ;
15- import { MAX_OOPIF_POLLS , OOPIF_POLL_DELAY } from './cf-schedules.js' ;
15+ import { MAX_OOPIF_POLLS , OOPIF_POLL_DELAY , OOPIF_PROBE_TIMEOUT } from './cf-schedules.js' ;
1616
1717/** Effect-returning CDP sender — eliminates the Promise bridge. */
1818type EffectSend = (
@@ -138,36 +138,49 @@ export function phase2OOPIFResolution(
138138 // ── Matching strategies ─────────────────────────────────────────
139139
140140 /**
141- * Try frameId match: Phase 1 gave us the iframe's frameId — attach
142- * each candidate and compare frame tree root ID against iframeFrameId.
141+ * Try frameId match: Phase 1 gave us the iframe's frameId — race all
142+ * candidates concurrently. First match wins, losers interrupted.
143+ *
144+ * Pre-filters by parentFrameId to skip OOPIFs from sibling tabs.
145+ * Per-probe timeout prevents stale targets from blocking (30s CDP default).
143146 */
144147 const tryFrameIdMatch = (
145148 candidates : readonly OOPIFCandidate [ ] ,
146149 poll : number ,
147150 ) : Effect . Effect < OOPIFMatch | null > => {
148151 if ( ! iframeFrameId ) return Effect . succeed ( null ) ;
152+ // Pre-filter: only scan OOPIFs parented to OUR page
153+ const ours = pageFrameId
154+ ? candidates . filter ( ( t ) => t . parentFrameId === pageFrameId )
155+ : candidates ;
156+ if ( ours . length === 0 ) return Effect . succeed ( null ) ;
149157 return Effect . fn ( 'cf.phase2.frameIdScan' ) ( function * ( ) {
150- for ( const target of candidates ) {
151- const sid = yield * attachToTarget ( target , 'frameId' , poll ) ;
152- if ( ! sid ) continue ;
153- const fid = yield * getFrameTreeId ( sid ) ;
154- if ( fid && ( fid === iframeFrameId || target . targetId === iframeFrameId ) ) {
155- yield * events . marker ( pageTargetId , 'cf.oopif_discovered' , {
156- method : 'active' , via, filter : 'frameId_match' ,
157- targetId : target . targetId , url : target . url ?. substring ( 0 , 100 ) ,
158- total_candidates : candidates . length , poll,
159- } ) ;
160- return { sessionId : sid , target, method : 'frameId_match' as const } ;
161- }
162- }
163- return null ;
158+ // Race all candidates — first frameId match wins, losers interrupted
159+ return yield * Effect . raceAll (
160+ ours . map ( ( target ) =>
161+ Effect . fn ( 'cf.phase2.probe' ) ( function * ( ) {
162+ const sid = yield * attachToTarget ( target , 'frameId' , poll ) ;
163+ if ( ! sid ) return yield * Effect . fail ( 'no-session' as const ) ;
164+ const fid = yield * getFrameTreeId ( sid ) ;
165+ if ( ! fid || ( fid !== iframeFrameId && target . targetId !== iframeFrameId ) ) {
166+ return yield * Effect . fail ( 'no-match' as const ) ;
167+ }
168+ yield * events . marker ( pageTargetId , 'cf.oopif_discovered' , {
169+ method : 'active' , via, filter : 'frameId_match' ,
170+ targetId : target . targetId , url : target . url ?. substring ( 0 , 100 ) ,
171+ total_candidates : ours . length , poll,
172+ } ) ;
173+ return { sessionId : sid , target, method : 'frameId_match' as const } satisfies OOPIFMatch ;
174+ } ) ( ) . pipe ( Effect . timeout ( OOPIF_PROBE_TIMEOUT ) ) ,
175+ ) ,
176+ ) . pipe ( Effect . orElseSucceed ( ( ) => null as OOPIFMatch | null ) ) ;
164177 } ) ( ) ;
165178 } ;
166179
167180 /**
168181 * Try parentFrameId match: filter candidates whose parentFrameId matches
169- * the page's root frameId, then cross-validate against iframeFrameId
170- * when available to reject stale OOPIFs .
182+ * the page's root frameId, race concurrently, cross-validate against
183+ * iframeFrameId when available.
171184 */
172185 const tryParentMatch = (
173186 candidates : readonly OOPIFCandidate [ ] ,
@@ -177,30 +190,33 @@ export function phase2OOPIFResolution(
177190 const filtered = candidates . filter ( ( t ) => t . parentFrameId === pageFrameId ) ;
178191 if ( filtered . length === 0 ) return Effect . succeed ( null ) ;
179192 return Effect . fn ( 'cf.phase2.parentScan' ) ( function * ( ) {
180- for ( const target of filtered ) {
181- const sid = yield * attachToTarget ( target , 'parentFrameId' , poll ) ;
182- if ( ! sid ) continue ;
183- // Cross-validate against Phase 1 frameId when available
184- if ( iframeFrameId ) {
185- const fid = yield * getFrameTreeId ( sid ) ;
186- if ( fid && fid !== iframeFrameId && target . targetId !== iframeFrameId ) {
187- yield * events . marker ( pageTargetId , 'cf.oopif_stale' , {
188- via, targetId : target . targetId ,
189- expected_frame_id : ( iframeFrameId as string ) . substring ( 0 , 20 ) ,
190- actual_frame_id : fid . substring ( 0 , 20 ) ,
191- poll,
193+ return yield * Effect . raceAll (
194+ filtered . map ( ( target ) =>
195+ Effect . fn ( 'cf.phase2.probe' ) ( function * ( ) {
196+ const sid = yield * attachToTarget ( target , 'parentFrameId' , poll ) ;
197+ if ( ! sid ) return yield * Effect . fail ( 'no-session' as const ) ;
198+ // Cross-validate against Phase 1 frameId when available
199+ if ( iframeFrameId ) {
200+ const fid = yield * getFrameTreeId ( sid ) ;
201+ if ( fid && fid !== iframeFrameId && target . targetId !== iframeFrameId ) {
202+ yield * events . marker ( pageTargetId , 'cf.oopif_stale' , {
203+ via, targetId : target . targetId ,
204+ expected_frame_id : ( iframeFrameId as string ) . substring ( 0 , 20 ) ,
205+ actual_frame_id : fid . substring ( 0 , 20 ) ,
206+ poll,
207+ } ) ;
208+ return yield * Effect . fail ( 'stale' as const ) ;
209+ }
210+ }
211+ yield * events . marker ( pageTargetId , 'cf.oopif_discovered' , {
212+ method : 'active' , via, filter : 'parentFrameId' ,
213+ targetId : target . targetId , url : target . url ?. substring ( 0 , 100 ) ,
214+ total_candidates : filtered . length , poll,
192215 } ) ;
193- continue ;
194- }
195- }
196- yield * events . marker ( pageTargetId , 'cf.oopif_discovered' , {
197- method : 'active' , via, filter : 'parentFrameId' ,
198- targetId : target . targetId , url : target . url ?. substring ( 0 , 100 ) ,
199- total_candidates : filtered . length , poll,
200- } ) ;
201- return { sessionId : sid , target, method : 'parentFrameId' as const } ;
202- }
203- return null ;
216+ return { sessionId : sid , target, method : 'parentFrameId' as const } satisfies OOPIFMatch ;
217+ } ) ( ) . pipe ( Effect . timeout ( OOPIF_PROBE_TIMEOUT ) ) ,
218+ ) ,
219+ ) . pipe ( Effect . orElseSucceed ( ( ) => null as OOPIFMatch | null ) ) ;
204220 } ) ( ) ;
205221 } ;
206222
0 commit comments