
    Mi:_                    2   S SK r S SKrS SKJrJrJrJr  S SKrS SKrS SK	r	S SK
r
S SKrS SKrS SKrS SKrS SKJr  S SKrSSKJr  SSKJr  SSKJrJr  SSKJr  S SKr/ S	Qr " S
 S5      r  SS\S\S\S-  S\S-  SS4
S jjr " S S5      r " S S5      r g)    N)DictListOptionalTuple)BrowserContext   )load_js_script)DOWNLOAD_PAGE_TIMEOUT)BrowserConfigCrawlerRunConfig)get_chromium_path)z--disable-background-networking%--disable-background-timer-throttlingz(--disable-backgrounding-occluded-windowsz--disable-breakpadz(--disable-client-side-phishing-detectionz4--disable-component-extensions-with-background-pagesz--disable-default-appsz--disable-extensionsz--disable-features=TranslateUIz--disable-hang-monitor!--disable-ipc-flooding-protectionz--disable-popup-blockingz--disable-prompt-on-repostz--disable-sync--force-color-profile=srgbz--metrics-recording-only--no-first-runz--password-store=basicz--use-mock-keychainc                   f   \ rS rSr% Sr\S\S\\   4S j5       r	\\
S'   \\
S'   \\
S'   \R                  \
S	'   \\
S
'   \\
S'   \\
S'           SS\S\\   S\S\S\S\\   S\\   4S jjrS\4S jrS rS rS\4S jrS\4S jrS\\   4S jrS r\SS j5       r\S 5       r\S 5       rSrg)ManagedBrowser,   aI  
Manages the browser process and context. This class allows to connect to the browser using CDP protocol.

Attributes:
    browser_type (str): The type of browser to launch. Supported values: "chromium", "firefox", "webkit".
                        Default: "chromium".
    user_data_dir (str or None): Path to a user data directory for persistent sessions. If None, a
                                 temporary directory may be used. Default: None.
    headless (bool): Whether to run the browser in headless mode (no visible GUI).
                     Default: True.
    browser_process (subprocess.Popen): The process object for the browser.
    temp_dir (str): Temporary directory for user data if not provided.
    debugging_port (int): Port for debugging the browser.
    host (str): Host for debugging the browser.

    Methods:
        start(): Starts the browser process and returns the CDP endpoint URL.
        _get_browser_path(): Returns the browser executable path based on OS and browser type.
        _get_browser_args(): Returns browser-specific command line arguments.
        _get_user_data_dir(): Returns the user data directory path.
        _cleanup(): Terminates the browser process and removes the temporary directory.
        create_profile(): Static method to create a user profile by launching a browser for user interaction.
configreturnc                 <   / SQnU R                   (       d  UR                  / SQ5        U R                  (       a  UR                  SS/5        U R                  (       a  UR                  [        5        U R
                  (       a  UR                  / SQ5        U R                  (       a  UR                  SU R                   35        O9U R                  (       a(  UR                  SU R                  R                   35        [        [        R                  U5      5      $ )z'Common CLI flags for launching Chromium)--no-sandbox--disable-dev-shm-usager   --no-default-browser-check--disable-infobars--window-position=0,0--ignore-certificate-errors%--ignore-certificate-errors-spki-list---disable-blink-features=AutomationControlled--window-position=400,0 --disable-renderer-backgroundingr   r   --mute-audior   G--disable-features=OptimizationHints,MediaRouter,DialMediaRouteProvider--disable-component-update--disable-domain-reliability)--disable-gpu--disable-gpu-compositing--disable-software-rasterizer--aggressive-cache-discard#--js-flags=--max-old-space-size=512z$--blink-settings=imagesEnabled=falsez--disable-remote-fontsz--disable-imagesz--disable-javascriptr(   r   z--proxy-server=)enable_stealthextendmemory_saving_mode
light_modeBROWSER_DISABLE_OPTIONS	text_modeproxyappendproxy_configserverlistdictfromkeys)r   flagss     r/var/www/eduai.edurigo.com/storigo/production/storigo_env/lib/python3.13/site-packages/crawl4ai/browser_manager.pybuild_browser_flags"ManagedBrowser.build_browser_flagsE   s    
. $$LL  
 $$LL,5  LL01LL   <<LL?6<<.9:  LL?6+>+>+E+E*FGHDMM%())    browser_typeuser_data_dirheadlessbrowser_processtemp_dirdebugging_porthostNcdp_urlbrowser_configc	                    UR                   U l         UR                  U l        UR                  U l        SU l        SU l        UR
                  U l        UR                  U l        X@l        SU l        UR                  U l	        Xl
        g)a  
Initialize the ManagedBrowser instance.

Args:
    browser_type (str): The type of browser to launch. Supported values: "chromium", "firefox", "webkit".
                        Default: "chromium".
    user_data_dir (str or None): Path to a user data directory for persistent sessions. If None, a
                                 temporary directory may be used. Default: None.
    headless (bool): Whether to run the browser in headless mode (no visible GUI).
                     Default: True.
    logger (logging.Logger): Logger instance for logging messages. Default: None.
    host (str): Host for debugging the browser. Default: "localhost".
    debugging_port (int): Port for debugging the browser. Default: 9222.
    cdp_url (str or None): CDP URL to connect to the browser. Default: None.
    browser_config (BrowserConfig): Configuration object containing all browser settings. Default: None.
NF)r>   r?   r@   rA   rB   rC   rD   loggershutting_downrE   rF   )	selfr>   r?   r@   rH   rD   rC   rE   rF   s	            r:   __init__ManagedBrowser.__init__   sw    6 +77+99&//#,;;"''	"%--,r=   c                 n	  #    U R                   (       a  U R                   $ U R                  (       d*  [        R                  " SS9U l        U R                  U l        U R                  5       I Sh  vN nU R                  R                  (       a%  UR                  U R                  R                  5         [        R                  S:X  a  [        c  [        S5      e[        R                  " / SQ5       H  nSR                  UR                  R!                  S5      =(       d    / 5      nS	U R"                   3U;   d  MK  S
U R                   3U;   d  M`  UR%                  5         UR'                  SS9  M     GO [(        R*                  " [,        R.                  " SU R"                   35      [(        R0                  S9R3                  5       R5                  5       R7                  5       nU H2  n [<        R$                  " [?        U5      [@        RB                  5        M4     S Hi  n[<        RF                  R                  U R                  U5      n[<        RF                  RI                  U5      (       d  MS  [<        RJ                  " U5        Mk       [        R                  S:X  aX  [(        RR                  " U[(        RT                  [(        RT                  [(        RV                  [(        RX                  -  S9U l-        OF[(        RR                  " U[(        RT                  [(        RT                  [<        R\                  S9U l-        U RN                  (       aG  U R                  R^                  (       a,  U RN                  Ra                  SSR                  U5       3SS9  [b        Rd                  " S5      I Sh  vN   U Rg                  5       I Sh  vN   [b        Rd                  " S5      I Sh  vN   SU Rh                   SU R"                   3$  GN! [8        [(        R:                  4 a    / n GNEf = f! [D         a     GMU  f = f! [L         a(  nU RN                  RQ                  SU 3SS9   SnAGNSnAff = f N N N! [L         a,  n	U Rk                  5       I Sh  vN    [M        SU	 35      eSn	A	ff = f7f)z
Starts the browser process or returns CDP endpoint URL.
If cdp_url is provided, returns it directly.
If user_data_dir is not provided for local browser, creates a temporary directory.

Returns:
    str: CDP endpoint URL
zbrowser-profile-prefixNwin32z.psutil not available, cannot clean old browser)pidnamecmdline rS   --remote-debugging-port=--user-data-dir=   timeoutzlsof -t -i:)stderr)SingletonLockSingletonSocketSingletonCookiezpre-launch cleanup failed: BROWSERtag)stdoutrZ   creationflags)ra   rZ   
preexec_fnzStarting browser with args:       ?   http://:zFailed to start browser: )6rE   r?   tempfilemkdtemprB   _get_browser_argsrF   
extra_argsr-   sysplatformpsutilRuntimeErrorprocess_iterjoininfogetrC   killwait
subprocesscheck_outputshlexsplitDEVNULLdecodestrip
splitlinesFileNotFoundErrorCalledProcessErrorosintsignalSIGTERMProcessLookupErrorpathexistsremove	ExceptionrH   warningPopenPIPEDETACHED_PROCESSCREATE_NEW_PROCESS_GROUPrA   setpgrpverbosedebugasynciosleep_initial_startup_checkrD   cleanup)
rJ   argspclpidsrQ   ffp_ees
             r:   startManagedBrowser.start   s     <<<< !!$,,4FGDM!%D ++--))KK++667(	S||w&>&'WXX,,-GHA!&&**Y"7"=2>B243F3F2GHBN.t/A/A.BCrIq) I"//!KK+d6I6I5J(KL#-#5#5  #   CC&..9   QAd&8&8!<Bww~~b))		" Q 	= ||w&'1'7'7%??%??","="=
@c@c"c	($ (2'7'7%??%??!zz	($ {{t22::!!2388D>2BC! "  --$$$--///--"""TYYKq)<)<(=>>a .> *:+H+HI D
 .   	SKK"=bT B	R	S@ %/" 	=,,.  7s;<<	=s  A,R5.P	/AR54BQ =Q #Q 5R58A.P &Q ,.P/AQ 3Q R5D#Q< 2Q63Q< 
Q8Q< (Q:)Q< R5P,(Q +P,,Q /
P>9Q =P>>Q 
Q3Q.(R5.Q33R56Q< 8Q< :Q< <
R2R-RR--R22R5c                   #    U R                   (       d  g[        R                  " S5      I Sh  vN   U R                   R                  5       b  Su  p U R                   R	                  SS9u  pU R                  R                  SSU R                   R                  U(       a  UR                  5       OSU(       a  UR                  5       OSS.S	9  gg N! [
        R                   a     N{f = f7f)
z
Perform a quick check to make sure the browser started successfully.
This only runs once at startup rather than continuously monitoring.
Nrd   )r=   r=   rX   z^Browser process terminated during startup | Code: {code} | STDOUT: {stdout} | STDERR: {stderr}ERROR codera   rZ   messager`   params)rA   r   r   pollcommunicaterv   TimeoutExpiredrH   error
returncoder{   )rJ   ra   rZ   s      r:   r   %ManagedBrowser._initial_startup_check  s     
 ## mmC   $$&2%NF!%!5!5!A!A#!A!N KKx 00;;17fmmoR17fmmoR   3 	! ,, s4   ,C-C#C-C .A$C-C*'C-)C**C-c           	      x  #    U R                   (       GaM   [        R                  " [        R                  " U R                   R                  R
                  5      [        R                  " U R                   R                  R
                  5      5      I Sh  vN u  pU R                   R                  5       b  U R                  (       dh  U R                  R                  SSU R                   R                  UR                  5       UR                  5       S.S9  U R                  5       I Sh  vN   gU R                  R                  SSSU R                   R                  0S9  ggg N N;! [         aF  nU R                  (       d+  U R                  R                  S	SS
[!        U5      0S9   SnAg SnAgSnAff = f7f)a  
Monitor the browser process for unexpected termination.

How it works:
1. Read stdout and stderr from the browser process.
2. If the process has terminated, log the error message and terminate the browser.
3. If the shutting_down flag is set, log the normal termination message.
4. If any other error occurs, log the error message.

Note: This method should be called in a separate task to avoid blocking the main event loop.
This is DEPRECATED and should not be used for builtin browsers that need to outlive the Python process.
Nz\Browser process terminated unexpectedly | Code: {code} | STDOUT: {stdout} | STDERR: {stderr}r   r   r   z2Browser process terminated normally | Code: {code}INFOr   z)Error monitoring browser process: {error}r   )rA   r   gather	to_threadra   readrZ   r   rI   rH   r   r   r{   r   rr   r   str)rJ   ra   rZ   r   s       r:   _monitor_browser_process'ManagedBrowser._monitor_browser_process3  sq     '.~~%%d&:&:&A&A&F&FG%%d&:&:&A&A&F&FG( " '',,.:--)) %C '(,(<(<(G(G*0--/*0--/$ *  #lln,,(($X &$*D,@,@,K,K#L )  ;  "" -  ))KK%% K# 'Q0 &  *s`   F:A>E' E#BE' )E%*E' .F:/1E'  F:#E' %E' '
F717F2(
F:2F77F:c                     [         R                  S:X  a  SSSS.nO![         R                  S:X  a  SSS	S.nOS
SS	S.nUR                  U R                  5      $ )z@Returns the browser executable path based on OS and browser typedarwinz</Applications/Google Chrome.app/Contents/MacOS/Google Chromez0/Applications/Firefox.app/Contents/MacOS/firefoxz./Applications/Safari.app/Contents/MacOS/Safari)chromiumfirefoxwebkitrP   z5C:\Program Files\Google\Chrome\Application\chrome.exez,C:\Program Files\Mozilla Firefox\firefox.exeNzgoogle-chromer   )rl   rm   rs   r>   )rJ   pathss     r:   _get_browser_path_WIP$ManagedBrowser._get_browser_path_WIPb  se    <<8#ZMJE
 \\W$XLE ,$E yy**++r=   c                 L   #    [        U R                  5      I S h  vN nU$  N7fN)r   r>   )rJ   browser_paths     r:   _get_browser_path ManagedBrowser._get_browser_pathy  s$     .t/@/@AA Bs   $"$c                   #    U R                  5       I Sh  vN /nU R                  S:X  a  SU R                   3SU R                   3/nU R                  (       a  UR                  S5        U R                  R                  (       aZ  U R                  R                  (       a?  UR                  SU R                  R                   SU R                  R                   35        UR                  U R                  U R                  5      5        X-   $ U R                  S:X  aI  S	[        U R                  5      S
U R                  /nU R                  (       a  UR                  S5        X-   $ [        SU R                   S35      e GNk7f)z/Returns full CLI args for launching the browserNr   rU   rV   z--headless=new--window-size=,r   --remote-debugging-portz	--profile
--headlesszBrowser type z not supported)r   r>   rC   r?   r@   r3   rF   viewport_heightviewport_widthr-   r;   r   NotImplementedError)rJ   baser9   s      r:   rj    ManagedBrowser._get_browser_args}  sT    ,,../
**4+>+>*?@"4#5#5"67E }}-.""22t7J7J7Y7Y~d.A.A.P.P-QQRSWSfSfSvSvRwxyLL11$2E2EFG | )+)D''(""	E }}\* | &d6G6G5H&WXX/ /s   FFE+Fc           	      :  #    SU l         U R                  (       Ga]   U R                  R                  5       (       Gd<  U R                  R                  5         [	        S5       H>  nU R                  R                  5       b    O"[
        R                  " S5      I Sh  vN   M@     U R                  R                  5       c  [        R                  S:X  a:   [        R                  " SSSS	[        U R                  R                  5      /5        ON [        R                   " [        R"                  " U R                  R                  5      [$        R&                  5        [
        R                  " S5      I Sh  vN   U R0                  (       aQ  [        R2                  R5                  U R0                  5      (       a"   [6        R8                  " U R0                  5        ggg GNA! [         a    U R                  R                  5          Nf = f! [(        [*        4 a     Nf = f N! [         a0  nU R,                  R/                  S
SS[        U5      0S9   SnANSnAff = f! [         a0  nU R,                  R/                  SSS[        U5      0S9   SnAgSnAff = f7f)z/Cleanup browser process and temporary directoryT
   N皙?rP   taskkillz/Fz/Tz/PIDz"Error terminating browser: {error}r   r   r   z+Error removing temporary directory: {error})rI   rA   r   	terminateranger   r   rl   rm   rv   runr   rQ   r   rt   r   killpggetpgidr   SIGKILLr   OSErrorrH   r   rB   r   r   shutilrmtree)rJ   _r   s      r:   r   ManagedBrowser.cleanup  s     "! ++0022((224"2Y//446B!%mmC000 ' ++002:<<72< *
D$PSTXThThTlTlPm/n o
% "		"**T5I5I5M5M*NPVP^P^ _ &mmC000 ==RWW^^DMM::dmm, ;=3 1 $- < $ 4 4 9 9 ;< %7#@ % $%0 !!@#SV, "   !!I#SV, " s   JA>H! G7H! 8G H! AH	 H! 4H5H! 9?J9 I JH! $HH! HH! 	HH! HH! !
I+&IJIJ
J(&JJJJc                 T   #    SSK Jn  U" US9nUR                  XS9I Sh  vN $  N7f)a  
This method has been moved to the BrowserProfiler class.

Creates a browser profile by launching a browser for interactive user setup
and waits until the user closes it. The profile is stored in a directory that
can be used later with BrowserConfig.user_data_dir.

Please use BrowserProfiler.create_profile() instead.

Example:
    ```python
    from crawl4ai.browser_profiler import BrowserProfiler
    
    profiler = BrowserProfiler()
    profile_path = await profiler.create_profile(profile_name="my-login-profile")
    ```
r   BrowserProfiler)rH   )profile_namerF   N)browser_profilerr   create_profile)rF   r   rH   r   profilers        r:   r   ManagedBrowser.create_profile  s0     & 	6 #&1,,,,ffffs   (&(c                  <    SSK Jn   U " 5       nUR                  5       $ )aa  
This method has been moved to the BrowserProfiler class.

Lists all available browser profiles in the Crawl4AI profiles directory.

Please use BrowserProfiler.list_profiles() instead.

Example:
    ```python
    from crawl4ai.browser_profiler import BrowserProfiler
    
    profiler = BrowserProfiler()
    profiles = profiler.list_profiles()
    ```
r   r   )r   r   list_profiles)r   r   s     r:   r   ManagedBrowser.list_profiles  s     " 	6 #$%%''r=   c                 >    SSK Jn  U" 5       nUR                  U 5      $ )aO  
This method has been moved to the BrowserProfiler class.

Delete a browser profile by name or path.

Please use BrowserProfiler.delete_profile() instead.

Example:
    ```python
    from crawl4ai.browser_profiler import BrowserProfiler
    
    profiler = BrowserProfiler()
    success = profiler.delete_profile("my-profile")
    ```
r   r   )r   r   delete_profile)profile_name_or_pathr   r   s      r:   r   ManagedBrowser.delete_profile  s#    " 	6 #$&&';<<r=   )rF   rA   r>   rE   rC   r@   rD   rH   rI   rB   r?   )r   NFN	localhost$  NN)NNN)__name__
__module____qualname____firstlineno____doc__staticmethodr   r   r   r;   __annotations__boolrv   r   r   r   rK   r   r   r   r   r   rj   r   r   r   r   __static_attributes__ r=   r:   r   r   ,   sT   0 7*M 7*d3i 7* 7*r N%%%M
I ''+"!%26%-%-  }%- 	%- %- %- #%- !/%-Ng=S g=R8-^,s ,. c 81h g g0 ( (, = =r=   r   srcdstcrawlerRunConfigbrowserConfigr   c                   #    U R                  5       I Sh  vN nU(       a  UR                  U5      I Sh  vN   U R                  5       I Sh  vN nUR                  S/ 5       H  nUS   nUR                  S/ 5      nU(       d  M#  UR                  (       a  UR                  S   OUR                  5       I Sh  vN n	U	R                  USS9I Sh  vN   U H  u  pU	R                  SX5      I Sh  vN   M!     M     U(       a4  UR                  (       a#  UR                  UR                  5      I Sh  vN   U(       a  UR                  (       as  UR                  S	/5      I Sh  vN   UR                  UR                  R                  UR                  R                  UR                  R                  S
.5      I Sh  vN   U$  GN GN GN GN GN N N Nr N7f)u   
Bring everything that *can* be changed at runtime from `src` → `dst`.

1. Cookies
2. localStorage (and sessionStorage, same API)
3. Extra headers, permissions, geolocation if supplied in configs
NoriginsoriginlocalStorager   domcontentloaded)
wait_untilz (k,v)=>localStorage.setItem(k,v)geolocationlatitude	longitudeaccuracy)cookiesadd_cookiesstorage_staters   pagesnew_pagegotoevaluateheadersset_extra_http_headersr   grant_permissionsset_geolocationr  r  r  )r   r   r   r   r  stater   urlkvspagekvs               r:   clone_runtime_stater    s     KKM!Goog&&& ##%%E))Ir*Xjj,"yysyy|CLLN.Bii(:i;;;DA-- BAIII  + ..(()>)>??? ,88##]O444!!,88AA-99CC,88AA
 	
 	
 JC "& & /C;I
 	@ 	5	
s   G G	G GG GA,G ;G<G G G 4G5AG 7G81G )G*AG GG G G G G G G G G c                       \ rS rSr% Sr0 r\\\4   \	S'   Sr
\\R                     \	S'   Sr\\R                     \	S'   \S\R                  4S j5       r\SS	\S
\4S jj5       r\S	\4S j5       r\S 5       rSrg)_CDPConnectionCacheiI  aC  
Class-level cache for Playwright + CDP browser connections.

When enabled via BrowserConfig(cache_cdp_connection=True), multiple
BrowserManager instances connecting to the same cdp_url will share
a single Playwright subprocess and CDP WebSocket. Reference-counted;
the connection is closed when the last user releases it.
_cacheN_lock
_lock_loopr   c                     [         R                  " 5       nU R                  b  U R                  ULa   [         R                  " 5       U l        Xl        U R                  $ r   )r   get_running_loopr  r  Lock)clsloops     r:   	_get_lock_CDPConnectionCache._get_lockW  sA    '')99d :CI!Nyyr=   rE   use_undetectedc                   #    U R                  5        ISh  vN   XR                  ;   au  U R                  U   u  p4nUR                  5       (       a(  X4US-   4U R                  U'   X44sSSS5      ISh  vN   $  UR                  5       I Sh  vN   U R                  U	 U(       a  SSKJn  OSSKJn  U" 5       R                  5       I Sh  vN nUR                  R                  U5      I Sh  vN nX4S4U R                  U'   X44sSSS5      ISh  vN   $  GN N N! [         a     Nf = f Nc NB N ! , ISh  vN  (       d  f       g= f7f)z>Get or create a cached (playwright, browser) for this cdp_url.Nr   r   async_playwright)r!  r  is_connectedstopr   patchright.async_apir&  playwright.async_apir   r   connect_over_cdp)r  rE   r#  pwbrowsercountr&  s          r:   acquire_CDPConnectionCache.acquire_  s     ==??**$%(ZZ%8"U''))+-	*BCJJw'; #??'')OO JJw' AA')//11BKK88AAG#%"2CJJw;+ #?? $   2A' #???s   EDEAD6(E4D5E;D DD 9D6D0"D6/D20D6ED4EED  
D-*D6,D--D62D64E6E<D?=E	Ec                    #    U R                  5        ISh  vN   XR                  ;  a   SSS5      ISh  vN   gU R                  U   u  p#nUS::  a@   UR                  5       I Sh  vN    UR	                  5       I Sh  vN   U R                  U	 OX#US-
  4U R                  U'   SSS5      ISh  vN   g N N NT! [         a     N^f = f NM! [         a     NWf = f N-! , ISh  vN  (       d  f       g= f7f)z>Decrement ref count; close connection when last user releases.Nr   )r!  r  closer   r(  )r  rE   r,  r-  r.  s        r:   release_CDPConnectionCache.releasey  s      ==??jj( #?? "%G!4Bz!--/))'')OO JJw'')EAI&>

7# #?? *   $   #???s   C>B:C>C$C>B<C>C$C )B>*C /CCC"C$)C>4C"5C><C>>C  
C
C$CC$C
CC$CC$"C>$C;*C-+C;7C>c                   #    U R                  5        ISh  vN   [        U R                  R                  5       5       HG  nU R                  U   u  p#n UR	                  5       I Sh  vN    UR                  5       I Sh  vN   MI     U R                  R                  5         SSS5      ISh  vN   g N NN! [
         a     NXf = f NG! [
         a     M  f = f N,! , ISh  vN  (       d  f       g= f7f)zAForce-close all cached connections. Call on application shutdown.N)r!  r6   r  keysr2  r   r(  clear)r  rE   r,  r-  r   s        r:   	close_all_CDPConnectionCache.close_all  s      ==??

 12!$G!4Q!--/))'')OO 3 JJ #?? *   $   #???s   C8B5C89CB9(B7)B9.CC	CC$C8/C0C87B99
CCCC	C
CCCCC8C5$C'%C51C8r   F)r   r   r   r   r   r  r   r   r   r   r  r   r   r  r  AbstractEventLoopclassmethodr!  r   r/  r3  r8  r   r   r=   r:   r  r  I  s      "FDe!$(E8GLL!(6:J223:',,   C   2 ?C ? ?&  r=   r  c                      \ rS rSr% SrSr0 r\\S'   Sr	\
R                  \S'   \S\
R                  4S j5       r\S*S\4S	 jj5       rS+S
\S\4S jjrS rS\4S jrS\S\4S jrS\4S jrS,S jrS,S jrS\S\4S jrS\4S jr  S+S\S\4S jjrS-S\4S jjrS\S\4S jr S r!S r"S\S\4S jr#S\4S jr$S\4S  jr%S! r&S" r'S\4S# jr(S$ r)S%\4S& jr*S' r+S( r,S)r-g).BrowserManageri  a  
Manages the browser instance and context.

Attributes:
    config (BrowserConfig): Configuration object containing all browser settings
    logger: Logger instance for recording events and errors
    browser (Browser): The browser instance
    default_context (BrowserContext): The default browser context
    managed_browser (ManagedBrowser): The managed browser instance
    playwright (Playwright): The Playwright instance
    sessions (dict): Dictionary to store session information
    session_ttl (int): Session timeout in seconds
N_global_pages_in_use_global_pages_lockr   c                 h    U R                   c  [        R                  " 5       U l         U R                   $ )zLGet or create the global pages lock (lazy initialization for async context).)r@  r   r  )r  s    r:   _get_global_lockBrowserManager._get_global_lock  s*     !!)%,\\^C"%%%r=   r#  c                    #    U(       a  SSK Jn  OSSKJn  U" 5       R                  5       I S h  vN U l        U R                  $  N7f)Nr   r%  )r)  r&  r*  r   _playwright_instance)r  r#  r&  s      r:   get_playwrightBrowserManager.get_playwright  s7     ==)9);)A)A)C#C ''' $Ds   -AAArF   c           
         Xl         X l        X0l        SU l        SU l        SU l        SU l        SU l        SU l        0 U l	        SU l
        0 U l        [        R                  " 5       U l        0 U l        0 U l        0 U l        SU l        [        R                  " 5       U l        SU l        SU l        SU l        0 U l        [        R                  " 5       U l        SU l        [        R4                  " 5       U l        U R6                  R9                  5         SU l        U R                   R<                  (       a#  U R                  (       d  SSKJ n  U" 5       U l        U R                   RB                  (       a  [E        U R                   RF                  U R                   RH                  U R                   RJ                  U R                  U R                   RL                  U R                   RN                  U R                   S	9U l        gg)
a#  
Initialize the BrowserManager with a browser configuration.

Args:
    browser_config (BrowserConfig): Configuration object containing all browser settings
    logger: Logger instance for recording events and errors
    use_undetected (bool): Whether to use undetected browser (Patchright)
NFi     r   r      )StealthAdapter)r>   r?   r@   rH   rC   rE   rF   )(r   rH   r#  r-  default_contextmanaged_browser
playwright_using_cached_cdp_launched_persistentsessionssession_ttlcontexts_by_configr   r  _contexts_lock_context_refcounts_context_last_used_page_to_sig_max_contexts
_page_lock_browser_endpoint_key_pages_served_browser_version_pending_cleanup_pending_cleanup_lock_max_pending_browsersEvent_cleanup_slot_availableset_stealth_adapterr,   browser_adapterrK  use_managed_browserr   r>   r?   r@   rC   rE   )rJ   rF   rH   r#  rK  s        r:   rK   BrowserManager.__init__  s    &4, ##!&$)!  #%%lln #%"$
 ",,. 59"  ! "%,\\^"%&"'.}}$$$((* !%;;%%d.A.A7$2$4D! ;;**#1![[55"kk77--{{#{{99++#{{$D  +r=   c                   #    U R                   b  U R                  5       I Sh  vN   U R                  R                  (       a}  U R                  R                  (       ab  SU l        SU R                  l        [        R                  U R                  R                  U R                  5      I Sh  vN u  U l         U l
        OGSU l        U R                  (       a  SSKJn  OSSKJn  U" 5       R                  5       I Sh  vN U l         U R                  R                  (       Ga  U R                  R                  (       Gd  U R
                  (       Gd  Sn[         R#                  U R                  5       Vs/ s H  nUR%                  U5      (       a  M  UPM     nnU R                  R&                  (       a%  UR)                  U R                  R&                  5        U R                  R*                  [-        [.        R1                  U5      5      U R                  R2                  U R                  R4                  S.U R                  R6                  =(       d    SU R                  R8                  U R                  R:                  S.nU R                  R<                  (       ac  U R                  R<                  R>                  U R                  R<                  R@                  U R                  R<                  RB                  S	.US
'   U R                  RD                  (       a  U R                  RD                  US'   U R                  RF                  =(       d    [H        RJ                  " SS9nU R                   RL                  RN                  " U40 UD6I Sh  vN U l(        SU l
        SU l)        U RU                  U RP                  5      I Sh  vN   U RW                  5       U l,        U RX                  [Z        R\                  ;  a%  [_        5       [Z        R\                  U RX                  '   gU R                  R                  (       d  U R                  R                  (       Gat  SU R                  l        U R
                  (       d  U R                  R                  (       d"  U R`                  R                  5       I Sh  vN OU R                  R                  nU Rc                  U5      I Sh  vN (       d  [e        SU S35      eU R                   RL                  Rg                  U5      I Sh  vN U l
        U R                  Rh                  nU R                  Rj                  (       Ga  U Rl                  (       a1  U Rl                  Ro                  SU R                  Rj                   3SS9  U(       aC  US   U l(        U Rl                  (       a'  U Rl                  Ro                  S[q        U5       S3SS9  O[r        Rt                  " S5      I Sh  vN   U R                  Rh                  nU(       a  US   U l(        OxU Rl                  (       a  U Rl                  Rw                  SSS9  U Ry                  5       I Sh  vN U l(        O/U(       a  US   U l(        OU Ry                  5       I Sh  vN U l(        U RU                  U RP                  5      I Sh  vN   OU R{                  5       n	U R                  R|                  S:X  a4  U R                   R~                  R                  " S0 U	D6I Sh  vN U l
        OU R                  R|                  S:X  a4  U R                   R                  R                  " S0 U	D6I Sh  vN U l
        O3U R                   RL                  R                  " S0 U	D6I Sh  vN U l
        U R                  U l(        U RW                  5       U l,        U RX                  [Z        R\                  ;  a&  [_        5       [Z        R\                  U RX                  '   gg GN GNo GN"s  snf  GN GNd GNx GNL GN GN0 GN GN GNv GN  N N7f)a  
Start the browser instance and set up the default context.

How it works:
1. Check if Playwright is already initialized.
2. If not, initialize Playwright.
3. If managed browser is used, start it and connect to the CDP endpoint.
4. If managed browser is not used, launch the browser and set up the default context.

Note: This method should be called in a separate task to avoid blocking the main event loop.
NTFr   r%  )z--proxy-serverr   z--user-data-dirr   z--window-sizewidthheight)r@   r   viewport
user_agentignore_https_errorsaccept_downloadsr5   usernamepasswordr2   r  zcrawl4ai-persistent-rN   zCDP endpoint at z is not ready after startupz$Using pre-existing browser context: r^   r_   zFound z% existing context(s), using first oneg?z/Pre-created context not found, creating new oner   r   r   )BrN  r2  r   cache_cdp_connectionrE   rO  re  r  r/  r#  r-  r)  r&  r*  r   use_persistent_contextr   r;   
startswithrk   r-   r@   r6   r7   r8   r   r   rl  rm  rn  r4   r5   rp  rq  r  r?   rh   ri   r   launch_persistent_contextrL  rP  setup_context_compute_browser_endpoint_keyrZ  r>  r?  rb  rM  _verify_cdp_readyr   r+  contextsbrowser_context_idrH   r   lenr   r   r   create_browser_context_build_browser_argsr>   r   launchr   )
rJ   r&  _skip_prefixesflagcli_argslaunch_kwargsr?   rE   ry  browser_argss
             r:   r   BrowserManager.start  s+     ??&**, ;;++0C0C%)D".2DKK+2E2M2M##T%8%83 -)DOT\ &+D"""AA %5$6$<$<$>>DO KK...KK'''***N +>>t{{KKD~6 K  
 {{%% 6 67 !KK00T]]845![[77"kk99 #kk44<'+{{'F'F$(KK$@$@
M {{''"kk66== $ 8 8 A A $ 8 8 A A*g& {{((151J1Jo. KK55 9I9I-:M
 oo..HH!%2   
  DL(,D%$$T%9%9::: *.)K)K)MD&))1T1TTRURW33D4N4NO;;$++"A"A"A.2DKK+))DHKKDWDW 4 4 : : <<<]a]h]h]p]p "33G<<<#&6wi?Z$[\\%)__%=%=%N%Nw%WW||,,H {{---;;KK%%>t{{?]?]>^_% & 
 +3A;D({{))$S]O3XY ) *  "--,,,#||44H/7{,  ;; KK// Q$- 0  6:5P5P5R/R,'/{$-1-H-H-J'J$$$T%9%9:::335L {{''94%)__%<%<%C%C%Sl%SS))X5%)__%;%;%B%B%R\%RR%)__%=%=%D%D%T|%TT#'<<D  &*%G%G%I"%%^-P-PPNQeN//0J0JK Qs - ?,F ; = =  X* - 0S (K:  TRTs(  !a	`B	a	-`.Aa	=`>A6a	4`"`"G8a	`'5a	`*C.a	5`-6.a	$`0%A a	%`3&C a	`6A*a	1`92/a	!`<"'a		`?
Aa	"a#Aa	0a13a	$a%A5a	a	a	"a	*a	-a	0a	3a	6a	9a	<a	?a	a	a	a	c                 :   U R                   R                  (       a%  U R                  U R                   R                  5      $ U R                  (       a=  [	        U R                  SS5      n[	        U R                  SS5      nU(       a  SU SU 3$ S[        U 5       3$ )aG  
Compute a unique key identifying this browser connection.

For CDP connections, uses the normalized CDP URL so all BrowserManager
instances connecting to the same browser share page tracking.
For standalone browsers, uses instance id since each is independent.

Returns:
    str: Unique identifier for this browser connection
rC   NrD   r   cdp:http://rg   z	instance:)r   rE   _normalize_cdp_urlrM  getattrid)rJ   portrD   s      r:   rw  ,BrowserManager._compute_browser_endpoint_key  s     ;;**4;;+>+>?? 4//1A4HD4//ED$TF!D622 2d8*%%r=   rE   c                     SSK Jn  U" U5      nUR                  =(       d    SnUR                  =(       d    SnSU SU 3$ )a  
Normalize a CDP URL to a canonical form for consistent tracking.

Handles various formats:
- http://localhost:9222
- ws://localhost:9222/devtools/browser/xxx
- http://localhost:9222?browser_id=xxx

Returns:
    str: Normalized CDP key in format "cdp:http://host:port"
r   )urlparser   r   r  rg   )urllib.parser  hostnamer  )rJ   rE   r  parsedrD   r  s         r:   r  !BrowserManager._normalize_cdp_url  s@     	*'"-+{{"dTF!D6**r=   c                     U R                   (       a;  U R                   [        R                  ;   a  [        R                  U R                      $ [        5       $ )z7Get the set of pages currently in use for this browser.)rZ  r>  r?  rb  )rJ   s    r:   _get_pages_in_use BrowserManager._get_pages_in_use  s<    %%$*D*DHkHk*k!66t7Q7QRRur=   c                    U R                   (       ap  U R                   [        R                  ;  a%  [        5       [        R                  U R                   '   [        R                  U R                      R	                  U5        gg)zMark a page as in use.N)rZ  r>  r?  rb  addrJ   r  s     r:   _mark_page_in_use BrowserManager._mark_page_in_use  s]    %%))1T1TTRURW33D4N4NO//0J0JKOOPTU &r=   c                     U R                   (       aL  U R                   [        R                  ;   a-  [        R                  U R                      R                  U5        ggg)z(Release a page from the in-use tracking.N)rZ  r>  r?  discardr  s     r:   _release_page_from_use%BrowserManager._release_page_from_use  sG    %%$*D*DHkHk*k//0J0JKSSTXY +l%r=   c           	      j  #    SSK nSSKJnJn  UR	                  S5      (       a  U R
                  R                  SSS9  gU" U5      nU" UR                  UR                  S	S
UR                  S
45      nU R
                  R                  SU 3SS9  [        S5       H  n UR                  5        ISh  vN nUR                  XbR                  SS9S9 ISh  vN n	U	R                  S:X  aD  U R
                  R                  SUS-    S3SS9   SSS5      ISh  vN   SSS5      ISh  vN     gSSS5      ISh  vN   SSS5      ISh  vN   SSU-  -  nU R
                  R                  SUS S3SS9  [         R"                  " U5      I Sh  vN   M     U R
                  R                  SSS9  g GN N N N N! , ISh  vN  (       d  f       N= f N! , ISh  vN  (       d  f       N= f! [         a-  n
U R
                  R                  SUS-    SU
 3SS9   Sn
A
NSn
A
ff = f N7f)zVerify CDP endpoint is ready with exponential backoff.

Supports multiple URL formats:
- HTTP URLs: http://localhost:9222
- HTTP URLs with query params: http://localhost:9222?browser_id=XXX
- WebSocket URLs: ws://localhost:9222/devtools/browser/XXX
r   N)r  
urlunparse)zws://zwss://z6WebSocket CDP URL provided, skipping HTTP verificationr^   r_   Tz/json/versionr   zStarting CDP verification for rW   re   )totalrX      zCDP endpoint ready after r   z	 attemptszCDP check attempt z	 failed: rd   gffffff?zWaiting z.2fzs before next CDP check...z(CDP verification failed after 5 attemptsF)aiohttpr  r  r  rt  rH   r   schemenetlocqueryr   ClientSessionrs   ClientTimeoutstatusr   r   r   )rJ   rE   r  r  r  r  
verify_urlattemptsessionresponser   delays               r:   rx   BrowserManager._verify_cdp_ready  s     	5 122KK V]fg '"MMMMLL!
 
 	::,GYWQxGa"0022g&{{:?T?T[\?T?]{^^bj#??c1 KK--0I'TU+V_.`fo-p#'  _^ 322^^ 32 3'>*EKKs3MNT]^--&&&   	D9U 3^ 3^^^^ 3222
  a!!$6w{m9QC"PV_!`a 's  BH3 G74F65G78!GF9
G3GGF;G G7+F=,G70H32G=F?>GG7GG7A H3H1#H36G79G;G=G7?GGG
GGG7G4	#G&$G4	0G73H34G77
H.#H)$H3)H..H3c                    SSSSSSSSS	S
SSSSSSSSSSSSU R                   R                   SU R                   R                   3/nU R                   R                  (       a  UR	                  SS/5        U R                   R
                  (       a  UR	                  [        5        U R                   R                  (       a  UR	                  / SQ5        U R                   R                  (       a%  UR	                  U R                   R                  5        [        [        R                  U5      5      nU R                   R                  US.nU R                   R                  (       a  U R                   R                  US'   U R                   R                  (       ak  U R                   R                  =(       d3    [         R"                  R%                  [         R&                  " 5       S5      US'   [         R(                  " US   SS 9  U R                   R*                  (       a  [,        R.                  " S![0        5        U R                   R2                  (       al  S"S#KJn  U" U R                   R2                  R8                  U R                   R2                  R:                  U R                   R2                  R<                  S$9nXBS%'   U$ )&z+Build browser launch arguments from config.r&   r'   r(   r   r   r   r   r   r   r   r   r   r    r!   r   r   r"   r   r#   r$   r%   r   r   r)   r*   r+   )r@   r   channel	downloadsdownloads_pathT)exist_okzHBrowserConfig.proxy is deprecated and ignored. Use proxy_config instead.r   ProxySettingsro  r2   )r   r   r   r.   r-   r/   r0   r1   rk   r6   r7   r8   r@   chrome_channelrn  r  r   r   rq   getcwdmakedirsr2   warningswarnDeprecationWarningr4   r*  r  r5   rp  rq  )rJ   r   r  r  proxy_settingss        r:   r}  "BrowserManager._build_browser_args!  s    '+%( #)3;%./(3U(*T[[778$++:U:U9VW1
6 ;;))KK,5 
 ;;!!KK/0;;  KK	 ;;!!KK../ DMM$'($(KK$8$8$G;;%%&*kk&@&@L#;;''-1[[-G-G .277<<		[LL)* KK%56F;;MMZ" ;;##:*{{//6611::11::N
 %3!r=   contextr   c                 ,  #    U R                   R                  (       a-  UR                  U R                   R                  5      I Sh  vN   U R                   R                  (       a-  UR	                  U R                   R                  5      I Sh  vN   U R                   R
                  (       a  UR                  SS9I Sh  vN   U R                   R                  (       a  UR                  [        5        UR                  [        5        U R                   R                  (       aF  SUR                  R                  S'   U R                   R                  UR                  R                  S'   U R                   R                  (       ak  U R                   R                  U R                   R                  S.nUR                  U R                   R                  5        UR                  U5      I Sh  vN   SnU(       as  UR                   (       ab  UR                   nUR#                  S5      (       a  UnO=UR$                  (       a,  UR$                  R#                  S5      (       a  UR$                  nU(       a  UR	                  SS	US
./5      I Sh  vN   U(       a\  UR&                  (       d"  UR(                  (       d  UR*                  (       a)  UR-                  [/        S5      5      I Sh  vN   SUl        U(       a1  UR2                  (       a   UR-                  S5      I Sh  vN   SUl        U R                   R6                  (       a7  U R                   R6                   H  nUR-                  U5      I Sh  vN   M     gg GN: GN GN GN N N Nn N7f)a  
Set up a browser context with the configured options.

How it works:
1. Set extra HTTP headers if provided.
2. Add cookies if provided.
3. Load storage state if provided.
4. Accept downloads if enabled.
5. Set default timeouts for navigation and download.
6. Set user agent if provided.
7. Set browser hints if provided.
8. Set proxy if provided.
9. Set downloads path if provided.
10. Set storage state if provided.
11. Set cache if provided.
12. Set extra HTTP headers if provided.
13. Add cookies if provided.
14. Set default timeouts for navigation and download if enabled.
15. Set user agent if provided.
16. Set browser hints if provided.

Args:
    context (BrowserContext): The browser context to set up
    crawlerRunConfig (CrawlerRunConfig): Configuration object containing all browser settings
    is_default (bool): Flag indicating if this is the default context
Returns:
    None
N)r   Trn  r  )
User-Agentz	sec-ch-ua)rf   zhttps://cookiesEnabledtrue)rR   valuer  navigator_overriderz
                const _origAttachShadow = Element.prototype.attachShadow;
                Element.prototype.attachShadow = function(init) {
                    return _origAttachShadow.call(this, {...init, mode: 'open'});
                };
            )r   r  r  r  r  r  rn  set_default_timeoutr
   set_default_navigation_timeoutr  	_impl_obj_optionsrl  browser_hintupdater  rt  base_urloverride_navigatorsimulate_usermagicadd_init_scriptr	    _crawl4ai_nav_overrider_injectedflatten_shadow_dom_crawl4ai_shadow_dom_injectedinit_scripts)rJ   r  r   
is_defaultcombined_headers
cookie_urlr  scripts           r:   rv  BrowserManager.setup_contextu  s    D ;;001D1DEEE;;%%dkk&9&9:::;;$$''T'222;;''''(=>223HI{{))AE!!**+=> KK.. !!**$
 ;;!!"kk44![[55  ##DKK$7$78001ABBB 
 0 4 4"&&C~~566 
!**/?/H/H/S/STk/l/l-66
%% !1!')    33#11#))--n=R.STTT;?8  0 C C)) +    59G1 ;;##++22--f555 3 $K F ; 3$ C" U
 6s   ANN ANN1N ND+N,N	-B NNAN)N*7N!N"AN5N6NNN	NNNNNc           	        #    U R                   c  [        S5      eU R                  R                  R	                  SU R                  R
                  5      nU R                  R                  U R                  R                  S.nU R                  R                  (       a  SU R                  R                  0OSn/ SQn/ SQn/ SQnUUUU R                  R                  U R                  R                  U R                  R                  U R                  R                  U R                  R                  S	.nU(       a_  UR                  (       aN  S
SKJn	  U	" UR                  R"                  UR                  R$                  UR                  R&                  S9nXHS'   U R                  R(                  (       a  SSS.n
UR+                  U
5        U(       a  UR,                  (       a  UR,                  US'   UR.                  (       a  UR.                  US'   UR0                  (       al  UR0                  R2                  UR0                  R4                  UR0                  R6                  S.US'   UR	                  S/ 5      nUR9                  S5        XS'   U R                   R:                  " S0 UD6I Sh  vN n/ nU R                  R<                  (       a  UR?                  U5        U R                  R(                  (       a  UR?                  U5        U(       a'  U H!  nURA                  SU 3S 5      I Sh  vN   M#     U R                  RB                  (       a$  U H  nURA                  US 5      I Sh  vN   M      U$  N NK N7f)z
Creates and returns a new browser context with configured settings.
Applies text-only mode settings if text_mode is enabled in config.

Returns:
    Context: Browser context object with the specified configurations
Nz{Cannot create new browser contexts when using use_persistent_context=True. Persistent context uses a single shared context.r  rh  r5   )csslessscsssass),jpgjpegpnggifwebpsvgicobmptiffpsdwoffwoff2ttfotfeotmp4webmoggavimovwmvflvm4vmp3wavaacm4aopusflacpdfdocdocxxlsxlsxpptpptxziprar7ztargzxmlswfwasm)z**/google-analytics.com/**z**/googletagmanager.com/**z**/googlesyndication.com/**z**/doubleclick.net/**z**/adservice.google.com/**z**/adsystem.com/**z**/adzerk.net/**z**/adnxs.com/**z**/ads.linkedin.com/**z**/facebook.net/**z**/analytics.twitter.com/**z**/ads-twitter.com/**z**/hotjar.com/**z**/clarity.ms/**z**/scorecardresearch.com/**z**/pixel.wp.com/**z**/amazon-adsystem.com/**z**/mixpanel.com/**z**/segment.com/**)rl  rk  r2   rn  r  rm  device_scale_factorjava_script_enabledr   r  ro  r2   F)	has_touch	is_mobilelocaletimezone_idr  r   permissionsz**/*.c                 "    U R                  5       $ r   abortroutes    r:   <lambda>7BrowserManager.create_browser_context.<locals>.<lambda>^  s
    r=   c                 "    U R                  5       $ r   r  r  s    r:   r  r  b  s
    5;;=r=   r   )"r-  ro   r   r  rs   rl  r   r   r2   rn  r  rm  r
  r  r4   r*  r  r5   rp  rq  r1   r  r  r  r   r  r  r  r3   new_context	avoid_cssr-   r  	avoid_ads)rJ   r   rl  viewport_settingsr  css_extensionsstatic_extensionsad_tracker_patternscontext_settingsr  text_mode_settingspermsr  to_blockextpatterns                   r:   r|  %BrowserManager.create_browser_context  s     <<)  [[((,,\4;;;Q;QR
[[//kk11
 ;?++:K:K(DKK$5$56QU 9
"
0 %)# $ < <![[66#';;#B#B#';;#B#B#';;#B#B	
 ,,>!.+88??-::CC-::CC"
 -;);;  """
 ##$67 &&-=-D-D *++2B2N2N /++ 0 < < E E!1!=!=!G!G 0 < < E E3 / ),,]B?]+27/ 00D3CDD ;;  OON+;;  OO-.mmeC5M3NOOO   ;;  .mmG-HIII / # E P Js7   J*N ,M:-BN 0M<1>N /M>0N <N >N c                 H   SSK n0 nUR                  nUb+  [        USS5      [        USS5      [        USS5      S.US'   OSUS'   UR                  US'   UR                  US	'   UR
                  nUb(  UR                  UR                  UR                  S
.US'   OSUS'   UR                  US'   UR                  US'   UR                  US'   U R                  US'   UR                  US[        S9n[        R                   " UR#                  S5      5      R%                  5       $ )a  
Hash ONLY the CrawlerRunConfig fields that affect browser context
creation (create_browser_context) or context setup (setup_context).

Whitelist approach: fields like css_selector, word_count_threshold,
screenshot, verbose, etc. do NOT cause a new context to be created.
r   Nr5   rp  rq  ro  r4   r  r  r  r   r  r  r  r\  T)	sort_keysdefaultzutf-8)jsonr4   r  r  r  r   r  r  r  r  r  r  r\  dumpsr   hashlibsha256encode	hexdigest)rJ   r   r*  sig_dictpcgeosignature_jsons          r:   _make_config_signature%BrowserManager._make_config_signaturef  s4    	 **>!"h5#B
D9#B
D9(H^$ (,H^$-44"2">">**?LL ]]LL'H]# '+H]# *:)L)L%&$4$B$B!,22 (,'<'<#$HcJ~~n33G<=GGIIr=   c                 :   [        U R                  5      U R                  ::  a  g[        U R                  R                  5       S S9nU H  u  p#U R                  R                  US5      S:X  d  M'  U R                  R                  US5      nU R                  R                  US5        U R                  R                  US5        U R                  R                  5        VVs/ s H  u  pVXb:X  d  M  UPM     nnnU H  nU R                  U	 M     Us  $    gs  snnf )a.  
If contexts exceed the limit, find the least-recently-used context
with zero active crawls and remove it from all tracking dicts.

MUST be called while holding self._contexts_lock.

Returns the BrowserContext to close (caller closes it OUTSIDE the
lock), or None if no eviction is needed or possible.
Nc                     U S   $ )Nr   r   )items    r:   r  :BrowserManager._evict_lru_context_locked.<locals>.<lambda>  s    T!Wr=   )keyr   )
r{  rS  rX  sortedrV  itemsrU  rs   poprW  )rJ   
candidates	evict_sigr   ctxr   sstale_pagess           r:   _evict_lru_context_locked(BrowserManager._evict_lru_context_locked  s    t&&'4+=+== ##))+$

 'LI&&**9a8A=--11)TB''++It<''++It< #'"3"3"9"9";";$!q~A";   %A))!, %
 ' s   "D1Dc           	      "  #    U R                   (       a%   U R                   R                  U5      I Sh  vN   gg N! [         aF  nU R                  (       a+  U R                  R	                  SSS[        U5      0S9   SnAg SnAgSnAff = f7f)z2Apply stealth to a page if stealth mode is enabledNz(Failed to apply stealth to page: {error}STEALTHr   r   )rc  apply_stealthr   rH   r   r   )rJ   r  r   s      r:   _apply_stealth_to_page%BrowserManager._apply_stealth_to_page  sy       ++99$??? !? ;;KK'' J% 'Q0 (  s7   B< :< B< 
B7B=
BBB	target_idc           	        #     UR                    HQ  n[        US5      (       d  M  [        UR                  S5      (       d  M3  UR                  R                  U:X  d  MO  Us  $    [        U R                  S5      (       Ga9  [        U R                  R                  S5      (       Ga  UR                  UR                   (       a  UR                   S   OS5      I Sh  vN nU(       a   UR                  S5      I Sh  vN nUR                  S/ 5      nU H  nUR                  S5      U:X  d  M  UR                  S	5      S
:X  d  M1  [        R                  " S5      I Sh  vN   UR                   (       d  Mb  UR                   S   s  UR                  5       I Sh  vN   $    UR                  5       I Sh  vN   UR                   (       a  UR                   S   $ g N N N NF N-! UR                  5       I Sh  vN    f = f! [         aA  nU R                  (       a&  U R                  R                  SSS[        U5      0S9   SnAgSnAff = f7f)a  
Get an existing page by its CDP target ID.

This is used when connecting to a pre-created browser context with an existing page.
Playwright may not immediately see targets created via raw CDP commands, so we
use CDP to get all targets and find the matching one.

Args:
    context: The browser context to search in
    target_id: The CDP target ID to find

Returns:
    Page object if found, None otherwise
r  
_target_id_connectionr   NzTarget.getTargetstargetInfostargetIdtyper  r   z(Failed to get page by target ID: {error}r^   r   r   )r  hasattrr  rL  r-  new_cdp_sessionsendrs   r   r   detachr   rH   r   r   )	rJ   r  rJ  r  cdp_sessionresulttargetstargetr   s	            r:   _get_page_by_target_id%BrowserManager._get_page_by_target_id  s    '	4--'$..,2W2W~~00I=#	 & t||[11gdll>T>TVc6d6d$+$;$;PWP]P]GMM!<Lcg$hh3'2'7'78K'L!L"(**]B"?&-F%zz*5B#)::f#5#?*1--*<$<$<'.}}}/6}}Q/?(?)00222 '. *00222 }}}}Q''+ i "M %=
 3k00222  	{{##F!#SV, $ 
 	s   IH H H H "I#A8H GH (G% <G=.G% /G% G% G G% 7G% H G!H I G% "H 5G#6#H IH G% G% !H #H %H 9G<:H  H 
I7I	I	IIc                   ^#    U R                  5         UR                  (       ag  UR                  U R                  ;   aM  U R                  UR                     u  p#nX#[        R                  " 5       4U R                  UR                  '   X24$ U R                  R
                  (       Ga  U R                  R                  (       Gab  U R                  U5      nSnU R                   ISh  vN   XPR                  ;   a  U R                  U   nO_U R                  U5      I Sh  vN nU R                  X!5      I Sh  vN   X R                  U'   SU R                  U'   U R                  5       nU R                  R                  US5      S-   U R                  U'   [        R                  " 5       U R                   U'   SSS5      ISh  vN   Ub   UR#                  5       I Sh  vN    WR'                  5       I Sh  vN nU R+                  U5      I Sh  vN   XPR,                  U'   GOoU R                  R.                  (       a  U R                  U5      I Sh  vN nU R0                  n[3        XxXR                  5      I Sh  vN n UR#                  5       I Sh  vN   UnU R4                   ISh  vN   UR'                  5       I Sh  vN nSSS5      ISh  vN   U R+                  W5      I Sh  vN   GOU R0                  nU R                  R6                  (       a  U R                  R8                  (       a  U R;                  X R                  R8                  5      I Sh  vN nU(       di  U R4                   ISh  vN   UR'                  5       I Sh  vN nU R=                  U5        SSS5      ISh  vN   U R+                  U5      I Sh  vN   GOU R=                  U5        GOU R                  R>                  (       a  U R                  R
                  (       di  U R4                   ISh  vN   UR'                  5       I Sh  vN nU R=                  U5        SSS5      ISh  vN   U R+                  W5      I Sh  vN   GO[@        RC                  5        ISh  vN   URD                  n	U RG                  5       m[I        U4S jU	 5       S5      n
U
(       a  U
nO1UR'                  5       I Sh  vN nU R+                  U5      I Sh  vN   U R=                  U5        SSS5      ISh  vN   GO`U R                  U5      nSnU R                   ISh  vN   XPR                  ;   a  U R                  U   nO_U R                  U5      I Sh  vN nU R                  X!5      I Sh  vN   X R                  U'   SU R                  U'   U R                  5       nU R                  R                  US5      S-   U R                  U'   [        R                  " 5       U R                   U'   SSS5      ISh  vN   Ub   UR#                  5       I Sh  vN    WR'                  5       I Sh  vN nU R+                  U5      I Sh  vN   XPR,                  U'   UR                  (       a/  UW[        R                  " 5       4U R                  UR                  '   U =RJ                  S-  sl%        U RM                  5       I Sh  vN   WU4$  GN GN GN GNL! , ISh  vN  (       d  f       GNb= f GNN! [$         a     GNYf = f GNI! [$         a{    U R                   ISh  vN    XPR                  ;   a)  [)        SU R                  U   S-
  5      U R                  U'   SSS5      ISh  vN    e ! , ISh  vN  (       d  f       e = ff = f GN GNz GNS GN=! [$         a     GNHf = f GN: GN% GN! , ISh  vN  (       d  f       GN.= f GN GN GN GN GNc! , ISh  vN  (       d  f       GNy= f GNh GN GN GN! , ISh  vN  (       d  f       GN= f GN GN GNp GNZ GN<! , ISh  vN  (       d  f       GN= f GN0 GN GN GN]! , ISh  vN  (       d  f       GNs= f GN_! [$         a     GNjf = f GNZ! [$         a{    U R                   ISh  vN    XPR                  ;   a)  [)        SU R                  U   S-
  5      U R                  U'   SSS5      ISh  vN    e ! , ISh  vN  (       d  f       e = ff = f GN GNT7f)z
Get a page for the given session ID, creating a new one if needed.

Args:
    crawlerRunConfig (CrawlerRunConfig): Configuration object containing all browser settings

Returns:
    (page, context): The Page and its BrowserContext
Nr   r   c              3   6   >#    U  H  oT;  d  M
  Uv   M     g 7fr   r   ).0r   pages_in_uses     r:   	<genexpr>*BrowserManager.get_page.<locals>.<genexpr>l  s      KEql5JEs   		)'_cleanup_expired_sessions
session_idrQ  timer   re  create_isolated_contextr4  rT  rS  r|  rv  rU  rC  rs   	monotonicrV  r2  r   r	  maxrH  rW  r  rL  r  rY  rz  rJ  rY  r  rE   r>  rB  r  r  nextr[  _maybe_bump_browser_version)rJ   r   r  r  r   config_signatureto_closetmp_contextr@  r  available_pager^  s              @r:   get_pageBrowserManager.get_page  s     	&&( &&+;+F+F$--+W#}}-=-H-HIG1:A9UDMM*556=  ;;***
 {{222#'#>#>?O#P ...'+B+BB"&"9"9:J"K(,(C(CDT(U"U"00KKKDK//0@ADE//0@A#'#A#A#C //334DaH1L ++,<= AE@PD++,<= /." '&nn...
!(!1!1!33D 11$777*:!!$'**$($?$?@P$QQ**/BRT_T_``%++---   ???!$/D +?11$777.. ;;11dkk6K6K!%!<!<WkkF[F[!\\D#'???)0)9)9);#;D 2248 $3? #99$??? ..t4 {{**4;;3R3R#'???)0)9)9);#;D 2248 $3? #99$???
 $2#B#B#D#D$+MME+/+A+A+CL-1 KE K $.N  .'5 .5-=-=-?'?&*&A&A$&G G G 2248 $E#D#D$  $::;KLH***#'>'>>"556FGG %)$?$?@P$QQG,,WGGG@G++,<=@AD++,<=#==?H ++//0@!DqH ''(89 =ANN<L''(89 +*$ #"..***
$--// --d333&6d# &&:A49UDMM*556a ..000W}e / #VK /...& /$ 
 4  #222+/F/FFHK !4#:#:;K#Lq#PID334DE  322
   322
  8 R` .  
 +/ +???7 ]#2#; $3??? @ $3#; $3??? @
 $E (@ G $E#D#D#D* +
 RG +***( +  
 0 ...'+B+BBDGt667GH1LE//0@A /..
  /..
  4 	1s3  C$e'\(e+4\\	 \8\9A?\8e\e\0 \- \0 %] 8]9] =e_Ae_)e?_ e_ __ e/_)0e3_2_,_2e_/e0`1A0e!`"e=`>e```+e6`7e`7Ae/`:0e3a`=ae(a )eae a!!e$Aa-4a$5a-a'a-#e.a*/)ebe4bbb)b*A?b)e4b5e=b2 b/b2 c )c*c .eeA:e=e>	e	\\e\*\\*%e-\0 0
\>:e=\>>e] _	]!_	#9^._	'^*(_	._	4^75_	_		eee_ 
_&"e%_&&e,_2/e2`
8_;9`
	eee`e`4"`%#`4/	e:e=a ea	a
a	e!e$a-'a-*e-b3a64b 	ebbeb,bb,'e/b2 2
c <e?c  ec e c#!e%9d0e)d,*e0e	6d97e	eeerb  c                 `  #    XR                   ;   Gaw  U R                   U   u  p#nU R                  U5        SnU R                   ISh  vN   U R                  R	                  US5      nUb  X`R
                  ;   a  [        SU R
                  U   S-
  5      U R
                  U'   U R                  R                  (       dv  U R
                  R                  US5      S:X  aV  U R                  R	                  US5        U R
                  R	                  US5        U R                  R	                  US5        SnSSS5      ISh  vN   UR                  5       I Sh  vN   U(       a  UR                  5       I Sh  vN   U R                   U	 gg GNC NM! , ISh  vN  (       d  f       Nb= f NQ N47f)ze
Kill a browser session and clean up resources.

Args:
    session_id (str): The session ID to kill.
FNr   r   T)rQ  r  rT  rW  r=  rU  rf  r   re  rs   rS  rV  r2  )rJ   rb  r  r  r   should_close_contextsigs          r:   kill_sessionBrowserManager.kill_session  s[     &#}}Z8G1''-#( ***''++D$7?s.E.E'E3642237!;4D++C0
  ;;::2266sA>!C 3377TB 3377TB 3377TB370 +* **,#mmo%%j)- '
 +*** %sg   AF.F	F.C)F5F. FF.F*F.7F,8F.F.F'FF'#F.,F.c                 &    U R                  U5        g)uv   
Release a page from the in-use tracking set (global tracking).
Sync variant — does NOT decrement context refcount.
N)r  r  s     r:   release_pageBrowserManager.release_page  s    
 	##D)r=   c                   #    U R                  U5        SnSnU R                   ISh  vN   U R                  R                  US5      nUbG  X R                  ;   a8  [        SU R                  U   S-
  5      U R                  U'   U R                  U   nSSS5      ISh  vN   Ub!  US:X  a  U R                  U5      I Sh  vN   ggg N N+! , ISh  vN  (       d  f       N@= f N%7f)z
Release a page and decrement its context's refcount under the lock.

Should be called from the async crawl finally block instead of
release_page() so the context lifecycle is properly tracked.
Nr   r   )r  rT  rW  r=  rU  rf  _maybe_cleanup_old_browser)rJ   r  rq  refcounts       r:   release_page_with_context(BrowserManager.release_page_with_context  s      	##D)&&&##''d3C3*A*A#A/2t..s3a70'',  2237 '& ?x1}11#666  -? '&&& 7sQ   &C(CC(A'CC(C
!C( C&C(
C(C#CC#C(c                 Z    U R                   R                  nUS::  a  gU R                  U:  $ )z6Check if page threshold reached for browser recycling.r   F)r   max_pages_before_recycler[  )rJ   limits     r:   _should_recycleBrowserManager._should_recycle  s,    44A:!!U**r=   c                   #    U R                  5       (       d  g U R                   ISh  vN   U R                  5       (       d   SSS5      ISh  vN   g[        U R                  5      U R                  :  a]  U R
                  (       a0  U R
                  R                  SSS[        U R                  5      0S9  U R                  R                  5         GORU R                  n/ n/ nU R                   ISh  vN   [        U R                  R                  5       5       HG  nU R                  R                  US5      S:  a  UR                  U5        M6  UR                  U5        MI     SSS5      ISh  vN   U R
                  (       a@  U R
                  R!                  SSUUS-   U R"                  [        U5      [        U5      S	.S9  [$        R&                  " 5       nU H  nUUS
.U R                  U'   M     U =R                  S-  sl	        SU l         SSS5      ISh  vN   OISSS5      ISh  vN    [$        R(                  " U R                  R+                  5       SS9I Sh  vN   GMW  U H  nU R                   ISh  vN   U R2                  R/                  US5      nU R                  R/                  US5        U R4                  R/                  US5        SSS5      ISh  vN   Wc  M   UR7                  5       I Sh  vN   M     U(       a:  U R
                  (       a(  U R
                  R                  SS[        U5      US.S9  ggg GN) GN GNc GN! , ISh  vN  (       d  f       GN= f GNX GNJ! , ISh  vN  (       d  f       GN`= f GN2! [$        R,                   Ga    U R                   ISh  vN    [        U R                  R                  5       5       Vs/ s H'  nU R                  R                  US5      S:X  d  M%  UPM)     Os  snf nnU H  nU R                  R/                  US5        M!     U(       GaC  U R
                  (       a&  U R
                  R1                  SSS[        U5      0S9  U H  nU R                   ISh  vN    U R2                  R/                  US5      nU R                  R/                  US5        U R4                  R/                  US5        SSS5      ISh  vN    O! , ISh  vN  (       d  f       O= fWc  M   UR7                  5       I Sh  vN    M  ! [8         a     M  f = f   [        U R                  5      U R                  :  a  U R                  R;                  5         SSS5      ISh  vN     GM;  ! , ISh  vN  (       d  f        GNT= ff = f GNC GN! , ISh  vN  (       d  f       GN= f GN! [8         a     GM  f = f7f)zBump browser version if threshold reached, moving old browser to pending cleanup.

New requests automatically get a new browser (via new signature).
Old browser drains naturally and gets cleaned up when refcount hits 0.
Nz3Waiting for old browser to drain (pending: {count})r^   r.  r   r   z^Bumping browser version {old} -> {new} after {count} pages ({active} active, {idle} idle sigs)r   )oldnewr.  activeidle)versiondoneg      >@rX   z9Force-cleaned {count} stuck pending entries after timeoutzCImmediately cleaned up {count} idle contexts from version {version})r.  r  )r  r^  r{  r]  r_  rH   r   ra  r7  r\  rT  r6   rU  r6  rs   r3   rr   r[  r   r`  wait_forru   TimeoutErrorr=  r   rS  rV  r2  r   rb  )	rJ   old_versionactive_sigs	idle_sigsrq  
done_eventrA  
stuck_sigsr  s	            r:   rh  *BrowserManager._maybe_bump_browser_version  s     ##%% 111++-- 211 t,,-1K1KK{{))$Y )$+S1F1F-G#H * 
 00668 #'"7"7K"$K "I#222#'(?(?(D(D(F#GC#66::3BQF + 2 23 7 ) 0 0 5	 $H  32 {{(( %E )'2'2Q)-););*-k*:(+I$ ) 
  ")J*'2$.6--c2  + ))Q.))*D& q 21111x!?&&00557  } B C***1155c4@''++C6''++C6 +* "!--/))  KK]!$YKH   %9U 2(  3222) 2111z '' ?  555#'(=(=(B(B(D#E"#Ea2266q!<A #E"J "  *--11#t<  *!;; KK//(c$-(/Z'A 0  $.C'+':':':*.*A*A*E*Ec4*P $ 7 7 ; ;C F $ 7 7 ; ;C F (;':':':':':  '2!)*1--/$9$9'0 !)$(!) $. t4458R8RR 88<<>5 655555?B +*** *  s2  (W?MW?N W?MW?B N 6M7N :A/M)N 4M5B"N W?"M:#W?3M=4W?90N )N*N .W?W	W?
AWW?*W+W?5W-W*	W-AW?W?N N M7	%M(&M7	2N :W?=W? NN	NW?N %WOW&V).$PPBV)R V)"AT		7V)T
V)	T TT V))U<T?
=UV)
U	V)U	AV)WV" W%W?)W	/V20W	<W?W?WW?W?W'	WW'	"W?*W--
W<7W?;W<<W?rq  c           	      l  #    U R                    ISh  vN   XR                  ;  a   SSS5      ISh  vN   gU R                  R                  U5      nUS   nU R                  (       a   U R                  R	                  SSX1SS S.S9  U R
                   ISh  vN   U R                  R                  US5      nU R                  R                  US5        U R                  R                  US5        SSS5      ISh  vN   Wb   UR                  5       I Sh  vN   U R                  R                  5        VVs/ s H  u  pVUS   U:X  d  M  UPM     nnnU(       d.  U R                  (       a  U R                  R                  SSSU0S9  [        U R                  5      U R                  :  a  U R                  R!                  5         SSS5      ISh  vN   g GN GN GNG N! , ISh  vN  (       d  f       N= f N! [         a     Nf = fs  snnf  NB! , ISh  vN  (       d  f       g= f7f)	zQClean up an old browser's context if its refcount hit 0 and it's pending cleanup.Nr  z?Cleaning up context from browser version {version} (sig: {sig})r^      )r  rq  r   z6All contexts from browser version {version} cleaned up)r^  r]  r=  rH   r   rT  rS  rU  rV  r2  r   r<  rr   r{  r_  ra  rb  )rJ   rq  cleanup_infor  r  rA  rr   remaining_olds           r:   ry  )BrowserManager._maybe_cleanup_old_browserl  s    ---/// .--  0044S9L&y1K{{!!]!'2s8D "  ***1155c4@''++C6''++C6 +* "!--/)) "&!6!6!<!<!>!>ga	?k1 !>  
 !;;KK$$ X% );7 %  4(()D,F,FF,,002W .-- +*** *  9 .---s   H4GH4HH4GH4A!HG!HAG&3H>G$?HHH HH<HHA5H
H4HH4H4!H$H&G=	,G/-G=	9H H
HHH	HH4H1 H#!H1-H4c                 (   [         R                   " 5       nU R                  R                  5        VVVs/ s H  u  nu    p4X-
  U R                  :  d  M  UPM!     nnnnU H(  n[        R
                  " U R                  U5      5        M*     gs  snnnf )z'Clean up expired sessions based on TTL.N)rc  rQ  r<  rR  r   create_taskrr  )rJ   current_timesidr   	last_usedexpired_sessionss         r:   ra  (BrowserManager._cleanup_expired_sessions  s    yy{ +/--*=*=*?
*?&&a'$*:*:: *? 	 

 $C 1 1# 67 $
s   BBc           
      0  #    U R                   (       Ga.  [        U R                  R                  5       5      nU H  nU R	                  U5      I Sh  vN   M     U R
                  R                  5        H  n UR                  5       I Sh  vN   M     U R
                  R                  5         U R                  R                  5         U R                  R                  5         U R                  R                  5         [        R                  U R                  R                   5      I Sh  vN   SU l        SU l        SU l         gU R                  R                   (       Ga  U R                  R&                  (       Ga  [        U R                  R                  5       5      nU H  nU R	                  U5      I Sh  vN   M     U R
                  R                  5        H  n UR                  5       I Sh  vN   M     U R
                  R                  5         U R                  R                  5         U R                  R                  5         U R                  R                  5         U R"                  (       av   U R"                  R                  5       I Sh  vN   SU l        U R                  R.                  S:  a2  [0        R2                  " U R                  R.                  5      I Sh  vN   U R$                  (       a)  U R$                  R5                  5       I Sh  vN   SU l        gU R6                  (       Gad  [        U R                  R                  5       5      nU H  nU R	                  U5      I Sh  vN   M     U R
                  R                  5        H  n UR                  5       I Sh  vN   M     U R
                  R                  5         U R                  R                  5         U R                  R                  5         U R                  R                  5         U R8                  (       a*   U R8                  R                  5       I Sh  vN   SU l        U R$                  (       a)  U R$                  R5                  5       I Sh  vN   SU l        SU l        gU R                  R:                  (       a  [0        R2                  " S5      I Sh  vN   [        U R                  R                  5       5      nU H  nU R	                  U5      I Sh  vN   M     U R
                  R                  5        H  n UR                  5       I Sh  vN   M     U R
                  R                  5         U R                  R                  5         U R                  R                  5         U R                  R                  5         U R"                  (       a)  U R"                  R                  5       I Sh  vN   SU l        U R>                  (       aG  [0        R2                  " S5      I Sh  vN   U R>                  RA                  5       I Sh  vN   SU l        U R$                  (       a*  U R$                  R5                  5       I Sh  vN   SU l        gg GN7 GN ! [         a     GM(  f = f GNx GN GN! [         a     GM  f = f GN*! [         aB  nU R(                  (       a&  U R(                  R+                  SSS[-        U5      0S9   SnAGNoSnAff = f GN) GN GN GNj! [         a     GM  f = f GN! [         a     GNf = f GN GNt GN6 GN! [         a2  nU R(                  R=                  S	S
S[-        U5      0S9   SnAGMP  SnAff = f GN GNo GNP GN7f)z)Close all browser resources and clean up.NFz-Error disconnecting from CDP browser: {error}r^   r   r   r   rd   zError closing context: {error}r   )!rO  r6   rQ  r6  rr  rS  valuesr2  r   r7  rU  rV  rW  r  r3  r   rE   r-  rN  cdp_cleanup_on_closerH   r   r   cdp_close_delayr   r   r(  rP  rL  sleep_on_closer   rM  r   )rJ   session_idsrb  r@  r   s        r:   r2  BrowserManager.close  s     !!!t}}1134K)
''
333 *..557))+%% 8
 ##))+##))+##))+##%%--dkk.A.ABBBDL"DO%*D";; {{///"4==#5#5#78"-J++J777 #.  2299;C!iik)) <
 ''--/''--/''--/!!'') <<"ll00222 $(DL{{22Q6%mmDKK,G,GHHH ??//..000&*DO $$$t}}1134K)
''
333 *..557))+%% 8
 ##))+##))+##))+##% ##..44666 (,$oo**,,,"&(-D%;;%%--$$$4==--/0%J##J/// & **113Ciik!! 4 	%%'%%'%%'!<<,,$$&&&DL--$$$&&..000#'D ??//&&((("DO o 4 &   C 8
 *$  3$ ;; KK--(W$-(/Q'8 .  I 1 4 &   7  
 - % 0
 " !!<#SV, "   ' %0 )s+  A^Z%^8ZZZB^(Z )B^7Z#8%^Z)1Z&2Z)6A=^4Z> Z;Z> A^$\%2^\A^2\3%^\,\-\1A=^/\. \+\. 5^\?A^]A ^]%^6]	]
]B^(^
)5^^!^ ^9^:^;^Z
Z^Z^#^&Z))
Z83^7Z88^;Z> >
\
7\?^\

^^^\
\(#^'\((^+\. .
\<8^;\<<^^^]
^&^;^^^^^^)rZ  r\  ra  rV  rU  rT  rP  rX  r_  rY  rW  r[  r]  r^  rc  rO  r-  r   rS  rL  rH   rM  rN  rR  rQ  r#  r:  )NF)r   Nr   ).r   r   r   r   r   rE  r?  r7   r   r@  r   r  r<  rB  r   rF  r   rK   r   r   rw  r  rb  r  r  r  rx  r}  r   r   rv  r|  r4  rC  rH  rY  rm  rr  ru  r{  r  rh  ry  ra  r2  r   r   r=   r:   r>  r>    s     
 "$$#'++& & & ($ ( (D} DSW DLgTR&s &4+# +# +(3 VZ
*s *t *XRT Rn .2	j6j6 +j6XC=M CJ-J7G -JC -J^ D6N 6s 6po/? ob*S *>*7,+ +zx-3C -3^	8@#r=   r>  )NN)!r   rc  typingr   r   r   r   r   rl   r   rh   rn   r   rv   rx   r*  r   r,  
js_snippetr	   r   r
   async_configsr   r   utilsr   r  r0   r   r  r  r>  r   r=   r:   <module>r     s      . . 	 
       /  & ) : $  .f= f=X 15*.	0	0	0 '-0 !4'	0
 
0hR RjH# H#r=   