@@ -28,9 +28,12 @@ import { findChromiumChannelBestEffort, registryDirectory } from '../../server/r
2828import { minimist } from '../cli-client/minimist' ;
2929import { saveOutputFile } from '../trace/traceUtils' ;
3030import { DashboardConnection } from './dashboardController' ;
31+ import { RegistrySessionProvider } from './registrySessionProvider' ;
32+ import { IdentitySessionProvider } from './identitySessionProvider' ;
3133
3234import type * as api from '../../..' ;
3335import type { AnnotationData } from '@dashboard/dashboardChannel' ;
36+ import type { SessionProvider } from './sessionProvider' ;
3437
3538// HMR: build-time flag — `true` in watch builds, `false` in release. esbuild
3639// replaces the identifier via `define`, so the static branch pays zero runtime
@@ -42,9 +45,10 @@ type DashboardServer = {
4245 reveal : ( options : DashboardOptions ) => void ;
4346 triggerAnnotate : ( ) => void ;
4447 registerAnnotateWaiter : ( socket : net . Socket ) => void ;
48+ close : ( ) => Promise < void > ;
4549} ;
4650
47- async function startDashboardServer ( options : DashboardOptions ) : Promise < DashboardServer > {
51+ async function startDashboardServer ( provider : SessionProvider , options : DashboardOptions ) : Promise < DashboardServer > {
4852 const httpServer = new HttpServer ( ) ;
4953 const dashboardDir = libPath ( 'vite' , 'dashboard' ) ;
5054
@@ -67,7 +71,7 @@ async function startDashboardServer(options: DashboardOptions): Promise<Dashboar
6771 httpServer . createWebSocket ( ( ) => {
6872 let connection : DashboardConnection ;
6973 // eslint-disable-next-line prefer-const
70- connection = new DashboardConnection ( ( ) => connections . delete ( connection ) , ( ) => {
74+ connection = new DashboardConnection ( provider , ( ) => connections . delete ( connection ) , ( ) => {
7175 if ( currentReveal . pageId )
7276 connection . revealPage ( currentReveal . pageId ) ;
7377 else if ( currentReveal . sessionName )
@@ -131,7 +135,8 @@ async function startDashboardServer(options: DashboardOptions): Promise<Dashboar
131135 socket . on ( 'error' , cleanup ) ;
132136 } ;
133137
134- return { url : httpServer . urlPrefix ( 'human-readable' ) , reveal, triggerAnnotate, registerAnnotateWaiter } ;
138+ const close = ( ) => httpServer . stop ( ) ;
139+ return { url : httpServer . urlPrefix ( 'human-readable' ) , reveal, triggerAnnotate, registerAnnotateWaiter, close } ;
135140}
136141
137142function attachDashboardStaticServer ( httpServer : HttpServer , dashboardDir : string ) {
@@ -157,13 +162,13 @@ async function attachDashboardDevServer(httpServer: HttpServer) {
157162// HMR end
158163
159164async function innerOpenDashboardApp ( options : DashboardOptions ) : Promise < { page : api . Page ; server : DashboardServer } > {
160- const server = await startDashboardServer ( options ) ;
161- const { page } = await launchApp ( 'dashboard' ) ;
165+ const server = await startDashboardServer ( new RegistrySessionProvider ( ) , options ) ;
166+ const { page } = await launchApp ( 'dashboard' , { onClose : ( ) => gracefullyProcessExitDoNotHang ( 0 ) } ) ;
162167 await page . goto ( server . url ) ;
163168 return { page, server } ;
164169}
165170
166- async function launchApp ( appName : string ) {
171+ async function launchApp ( appName : string , options ?: { onClose ?: ( ) => void } ) {
167172 const channel = findChromiumChannelBestEffort ( 'javascript' ) ;
168173 const context = await playwright . chromium . launchPersistentContext ( '' , {
169174 ignoreDefaultArgs : [ '--enable-automation' ] ,
@@ -192,9 +197,7 @@ async function launchApp(appName: string) {
192197 } ) ;
193198 }
194199
195- page . on ( 'close' , ( ) => {
196- gracefullyProcessExitDoNotHang ( 0 ) ;
197- } ) ;
200+ page . on ( 'close' , ( ) => options ?. onClose ?.( ) ) ;
198201
199202 const image = await fs . promises . readFile ( libPath ( 'tools' , 'dashboard' , 'appIcon.png' ) ) ;
200203 // This is local Playwright, so I can access private methods.
@@ -299,7 +302,7 @@ export async function openDashboardApp() {
299302 console . error ( 'Unhandled promise rejection:' , error ) ;
300303 } ) ;
301304 if ( options . port !== undefined ) {
302- const { url } = await startDashboardServer ( options ) ;
305+ const { url } = await startDashboardServer ( new RegistrySessionProvider ( ) , options ) ;
303306 // eslint-disable-next-line no-console
304307 console . log ( `Listening on ${ url } ` ) ;
305308 selfDestructOnParentGone ( ) ;
@@ -352,6 +355,22 @@ export async function openDashboardApp() {
352355 await statePromise ;
353356}
354357
358+ export async function openDashboardForContext ( context : api . BrowserContext ) : Promise < void > {
359+ const server = await startDashboardServer ( new IdentitySessionProvider ( context ) , { } ) ;
360+
361+ let closed = false ;
362+ const close = async ( ) => {
363+ if ( closed )
364+ return ;
365+ closed = true ;
366+ await server . close ( ) ;
367+ } ;
368+
369+ const { page } = await launchApp ( 'dashboard' , { onClose : ( ) => { void close ( ) ; } } ) ;
370+ context . on ( 'close' , ( ) => { void close ( ) ; } ) ;
371+ await page . goto ( server . url ) ;
372+ }
373+
355374async function runKillClient ( ) : Promise < void > {
356375 const socketPath = dashboardSocketPath ( ) ;
357376 await new Promise < void > ( resolve => {
0 commit comments