@@ -24,6 +24,7 @@ function option(name, fallback) {
2424const output = resolve ( option ( '--output' , resolve ( root , '.cache/diff-data.json' ) ) ) ;
2525const project = option ( '--project' , '' ) ;
2626const portValue = option ( '--port' , '2299' ) ;
27+ const host = option ( '--host' , 'localhost' ) ;
2728if ( ! / ^ \d + $ / . test ( portValue ) || Number ( portValue ) > 65_535 ) {
2829 throw new Error ( '--port must be a number from 0 to 65535' ) ;
2930}
@@ -86,6 +87,15 @@ async function fetchAsset(request) {
8687 return fileResponse ( file ) ;
8788}
8889
90+ function jsonResponse ( value ) {
91+ return new Response ( JSON . stringify ( value ) , {
92+ headers : {
93+ 'cache-control' : 'no-store' ,
94+ 'content-type' : 'application/json; charset=utf-8' ,
95+ } ,
96+ } ) ;
97+ }
98+
8999function nodeRequest ( request ) {
90100 const host = request . headers . host || 'localhost' ;
91101 const init = {
@@ -111,15 +121,20 @@ async function send(nodeResponse, response) {
111121const server = createServer ( async ( request , response ) => {
112122 try {
113123 const webRequest = nodeRequest ( request ) ;
114- if ( new URL ( webRequest . url ) . pathname === '/events' ) {
124+ const url = new URL ( webRequest . url ) ;
125+ if ( url . pathname === '/health' ) {
126+ await send ( response , jsonResponse ( readyState ) ) ;
127+ return ;
128+ }
129+ if ( url . pathname === '/events' ) {
115130 response . writeHead ( 200 , {
116131 'content-type' : 'text/event-stream' ,
117132 'cache-control' : 'no-store' ,
118133 connection : 'keep-alive' ,
119134 } ) ;
120135 response . write ( 'retry: 250\nevent: ready\ndata: {}\n\n' ) ;
121136 eventClients . add ( response ) ;
122- const requestProject = new URL ( webRequest . url ) . searchParams . get ( 'project' ) ;
137+ const requestProject = url . searchParams . get ( 'project' ) ;
123138 if ( project && requestProject === project ) {
124139 console . log ( 'Diffsplain tab: connected' ) ;
125140 }
@@ -148,19 +163,53 @@ watchFile(output, { interval: 100 }, (current, previous) => {
148163} ) ;
149164
150165let selectedPort = Number ( portValue ) ;
166+ let readyState ;
167+ let closing = false ;
168+
169+ function urlFor ( address , port ) {
170+ const formattedAddress = address . includes ( ':' ) ? `[${ address } ]` : address ;
171+ return `http://${ formattedAddress } :${ port } ` ;
172+ }
173+
174+ function isLoopback ( address ) {
175+ return (
176+ address === 'localhost' ||
177+ address === '::1' ||
178+ address === '::ffff:127.0.0.1' ||
179+ / ^ 1 2 7 (?: \. \d { 1 , 3 } ) { 3 } $ / . test ( address )
180+ ) ;
181+ }
151182
152183function listen ( ) {
153- server . listen ( selectedPort , 'localhost' ) ;
184+ server . listen ( selectedPort , host ) ;
154185}
155186
156187server . on ( 'listening' , ( ) => {
188+ if ( closing ) {
189+ server . close ( ) ;
190+ return ;
191+ }
157192 const address = server . address ( ) ;
193+ const readyAddress =
194+ address && typeof address === 'object' ? address . address : host ;
158195 const readyPort =
159196 address && typeof address === 'object' ? address . port : selectedPort ;
197+ const url = urlFor ( host , readyPort ) ;
160198 const projectHash = project
161199 ? `#project=${ encodeURIComponent ( project ) } `
162200 : '' ;
163- console . log ( `Diffsplain: http://localhost:${ readyPort } ${ projectHash } ` ) ;
201+ readyState = {
202+ status : 'ok' ,
203+ address : readyAddress ,
204+ port : readyPort ,
205+ } ;
206+ if ( ! isLoopback ( readyAddress ) ) {
207+ console . warn (
208+ `Warning: Diffsplain is listening on ${ readyAddress } . Anyone who can reach this address can view this review.` ,
209+ ) ;
210+ }
211+ console . log ( `Diffsplain: ${ url } ${ projectHash } ` ) ;
212+ console . log ( JSON . stringify ( { event : 'ready' , ...readyState , url } ) ) ;
164213} ) ;
165214
166215server . on ( 'error' , ( error ) => {
@@ -175,22 +224,20 @@ server.on('error', (error) => {
175224 return ;
176225 }
177226 console . error ( `Could not start Diffsplain: ${ error . message } ` ) ;
178- process . exitCode = 1 ;
227+ close ( 1 ) ;
179228} ) ;
180229
181230listen ( ) ;
182231
183- let closing = false ;
184- function close ( ) {
232+ function close ( exitCode = 0 ) {
185233 if ( closing ) return ;
186234 closing = true ;
235+ process . exitCode = exitCode ;
187236 unwatchFile ( output ) ;
188237 for ( const client of eventClients ) client . end ( ) ;
189238 eventClients . clear ( ) ;
190- server . close ( ( ) => {
191- process . exitCode = 0 ;
192- } ) ;
239+ if ( server . listening ) server . close ( ) ;
193240}
194241
195- process . on ( 'SIGINT' , close ) ;
196- process . on ( 'SIGTERM' , close ) ;
242+ process . on ( 'SIGINT' , ( ) => close ( ) ) ;
243+ process . on ( 'SIGTERM' , ( ) => close ( ) ) ;
0 commit comments