@@ -5,29 +5,44 @@ import { CompileError } from './errors.js';
55// and call the compile / compileC actions. No admin secret is sent, so the
66// service keeps holding no secrets.
77const HASURA_URL = process . env . HASURA_URL ?? 'http://hasura:8080/v1/graphql' ;
8+ // Cap every Hasura call so a hung upstream compiler (zxbasic/z88dk) or a slow
9+ // lookup can't tie up a request indefinitely.
10+ const HASURA_TIMEOUT_MS = parseInt ( process . env . HASURA_TIMEOUT_MS ?? '20000' , 10 ) ;
811
912interface GraphQLResponse < T > {
1013 data ?: T ;
1114 errors ?: Array < { message : string } > ;
1215}
1316
1417async function gql < T > ( query : string , variables : Record < string , unknown > ) : Promise < T > {
15- const res = await fetch ( HASURA_URL , {
16- method : 'POST' ,
17- headers : { 'Content-Type' : 'application/json' } ,
18- body : JSON . stringify ( { query, variables } ) ,
19- } ) ;
20- if ( ! res . ok ) {
21- throw new Error ( `Hasura returned ${ res . status } ` ) ;
22- }
23- const body = ( await res . json ( ) ) as GraphQLResponse < T > ;
24- if ( body . errors ?. length ) {
25- throw new Error ( body . errors . map ( ( e ) => e . message ) . join ( '; ' ) ) ;
26- }
27- if ( ! body . data ) {
28- throw new Error ( 'Hasura returned no data' ) ;
18+ const controller = new AbortController ( ) ;
19+ const timer = setTimeout ( ( ) => controller . abort ( ) , HASURA_TIMEOUT_MS ) ;
20+ try {
21+ const res = await fetch ( HASURA_URL , {
22+ method : 'POST' ,
23+ headers : { 'Content-Type' : 'application/json' } ,
24+ body : JSON . stringify ( { query, variables } ) ,
25+ signal : controller . signal ,
26+ } ) ;
27+ if ( ! res . ok ) {
28+ throw new Error ( `Hasura returned ${ res . status } ` ) ;
29+ }
30+ const body = ( await res . json ( ) ) as GraphQLResponse < T > ;
31+ if ( body . errors ?. length ) {
32+ throw new Error ( body . errors . map ( ( e ) => e . message ) . join ( '; ' ) ) ;
33+ }
34+ if ( ! body . data ) {
35+ throw new Error ( 'Hasura returned no data' ) ;
36+ }
37+ return body . data ;
38+ } catch ( err ) {
39+ if ( controller . signal . aborted ) {
40+ throw new Error ( `Hasura request timed out after ${ HASURA_TIMEOUT_MS } ms` ) ;
41+ }
42+ throw err ;
43+ } finally {
44+ clearTimeout ( timer ) ;
2945 }
30- return body . data ;
3146}
3247
3348export interface ProjectRecord {
0 commit comments