@@ -371,78 +371,73 @@ async function syncServiceWorkspace(
371371 skippedRoutes : SkippedRoute [ ] ,
372372) : Promise < void > {
373373 const { pat, controlPlane, project, region, globalEnvironmentId, signal, onProgress } = ctx ;
374- const bufferId = await db . bufferChanges ( ) ;
375374
376- try {
377- const serviceName = service . name ?? `Gateway Service ${ service . id } ` ;
378-
379- // Upsert workspace for this service
380- let workspace : Workspace ;
381- if ( existingWorkspace ) {
382- if ( existingWorkspace . name !== serviceName ) {
383- workspace = await insoservices . workspace . update ( existingWorkspace , { name : serviceName } ) ;
384- counts . services . updated ++ ;
385- } else {
386- workspace = existingWorkspace ;
387- }
388- } else {
389- workspace = await insoservices . workspace . create ( { parentId : project . _id , name : serviceName , scope : 'collection' , konnectServiceId : service . id } ) ;
390- counts . services . created ++ ;
391- }
392- counts . services . total ++ ;
375+ const serviceName = service . name ?? `Gateway Service ${ service . id } ` ;
393376
394- // Set project-level env as the active global env for this workspace
395- const workspaceMeta = await insoservices . workspaceMeta . getOrCreateByParentId ( workspace . _id ) ;
396- if ( workspaceMeta . activeGlobalEnvironmentId !== globalEnvironmentId ) {
397- await insoservices . workspaceMeta . update ( workspaceMeta , { activeGlobalEnvironmentId : globalEnvironmentId } ) ;
377+ // Upsert workspace for this service
378+ let workspace : Workspace ;
379+ if ( existingWorkspace ) {
380+ if ( existingWorkspace . name !== serviceName ) {
381+ workspace = await insoservices . workspace . update ( existingWorkspace , { name : serviceName } ) ;
382+ counts . services . updated ++ ;
383+ } else {
384+ workspace = existingWorkspace ;
398385 }
399- await insoservices . cookieJar . getOrCreateForParentId ( workspace . _id ) ;
386+ } else {
387+ workspace = await insoservices . workspace . create ( { parentId : project . _id , name : serviceName , scope : 'collection' , konnectServiceId : service . id } ) ;
388+ counts . services . created ++ ;
389+ }
390+ counts . services . total ++ ;
400391
401- const incomingRoutes = await fetchRoutesForService ( pat , controlPlane . id , service . id , region , signal ) ;
402- const existingData = await loadExistingRequestData ( workspace . _id ) ;
403- const incomingKeys = new Set < string > ( ) ;
404- const incomingRouteIds = new Set < string > ( ) ;
392+ // Set project-level env as the active global env for this workspace
393+ const workspaceMeta = await insoservices . workspaceMeta . getOrCreateByParentId ( workspace . _id ) ;
394+ if ( workspaceMeta . activeGlobalEnvironmentId !== globalEnvironmentId ) {
395+ await insoservices . workspaceMeta . update ( workspaceMeta , { activeGlobalEnvironmentId : globalEnvironmentId } ) ;
396+ }
397+ await insoservices . cookieJar . getOrCreateForParentId ( workspace . _id ) ;
405398
406- for ( const route of incomingRoutes ) {
407- signal ?. throwIfAborted ( ) ;
408- incomingRouteIds . add ( route . id ) ;
409- const isL4 = route . protocols . every ( p => L4_PROTOCOLS . has ( p ) ) ;
410- const isGrpc = route . protocols . some ( p => p === 'grpc' || p === 'grpcs' ) ;
411- const isWs = route . protocols . some ( p => p === 'ws' || p === 'wss' ) ;
399+ const incomingRoutes = await fetchRoutesForService ( pat , controlPlane . id , service . id , region , signal ) ;
400+ const existingData = await loadExistingRequestData ( workspace . _id ) ;
401+ const incomingKeys = new Set < string > ( ) ;
402+ const incomingRouteIds = new Set < string > ( ) ;
412403
413- const routeName = routeDisplayName ( route ) ;
404+ for ( const route of incomingRoutes ) {
405+ signal ?. throwIfAborted ( ) ;
406+ incomingRouteIds . add ( route . id ) ;
407+ const isL4 = route . protocols . every ( p => L4_PROTOCOLS . has ( p ) ) ;
408+ const isGrpc = route . protocols . some ( p => p === 'grpc' || p === 'grpcs' ) ;
409+ const isWs = route . protocols . some ( p => p === 'ws' || p === 'wss' ) ;
414410
415- if ( isL4 ) {
416- counts . routes . skipped ++ ;
417- skippedRoutes . push ( { routeName, reason : `Unsupported protocol: ${ route . protocols . join ( ', ' ) } ` , serviceName } ) ;
418- continue ;
419- }
411+ const routeName = routeDisplayName ( route ) ;
420412
421- // Routes matched by SNI cannot be represented — Insomnia derives SNI implicitly
422- // from the URL hostname and has no SNI override.
423- if ( ( route . snis ?. length ?? 0 ) > 0 ) {
424- counts . routes . skipped ++ ;
425- skippedRoutes . push ( { routeName, reason : 'Route uses SNI matching — unsupported in Insomnia' , serviceName } ) ;
426- continue ;
427- }
413+ if ( isL4 ) {
414+ counts . routes . skipped ++ ;
415+ skippedRoutes . push ( { routeName, reason : `Unsupported protocol: ${ route . protocols . join ( ', ' ) } ` , serviceName } ) ;
416+ continue ;
417+ }
428418
429- if ( isGrpc ) {
430- await syncGrpcRoute ( route , workspace . _id , existingData . maps . grpc , counts . routes , incomingKeys ) ;
431- } else {
432- // Host header only applies to HTTP/WS — gRPC uses :authority which Insomnia derives from the URL
433- const headers = [
434- ...( route . hosts ?. [ 0 ] ? [ { name : 'Host' , value : route . hosts [ 0 ] } ] : [ ] ) ,
435- ...Object . entries ( route . headers ?? { } ) . map ( ( [ name , values ] ) => ( { name, value : values [ 0 ] } ) ) ,
436- ] ;
437- await ( isWs ? syncWsRoute ( route , workspace . _id , headers , existingData . maps . ws , counts . routes , incomingKeys ) : syncHttpRoute ( route , workspace . _id , headers , existingData . maps . http , counts . routes , incomingKeys ) ) ;
438- }
419+ // Routes matched by SNI cannot be represented — Insomnia derives SNI implicitly
420+ // from the URL hostname and has no SNI override.
421+ if ( ( route . snis ?. length ?? 0 ) > 0 ) {
422+ counts . routes . skipped ++ ;
423+ skippedRoutes . push ( { routeName, reason : 'Route uses SNI matching — unsupported in Insomnia' , serviceName } ) ;
424+ continue ;
439425 }
440426
441- await deleteStaleRequests ( existingData , incomingKeys , incomingRouteIds , counts . routes ) ;
442- onProgress ?.( `Synced ${ serviceName } in ${ controlPlane . name } ` ) ;
443- } finally {
444- await db . flushChanges ( bufferId ) ;
427+ if ( isGrpc ) {
428+ await syncGrpcRoute ( route , workspace . _id , existingData . maps . grpc , counts . routes , incomingKeys ) ;
429+ } else {
430+ // Host header only applies to HTTP/WS — gRPC uses :authority which Insomnia derives from the URL
431+ const headers = [
432+ ...( route . hosts ?. [ 0 ] ? [ { name : 'Host' , value : route . hosts [ 0 ] } ] : [ ] ) ,
433+ ...Object . entries ( route . headers ?? { } ) . map ( ( [ name , values ] ) => ( { name, value : values [ 0 ] } ) ) ,
434+ ] ;
435+ await ( isWs ? syncWsRoute ( route , workspace . _id , headers , existingData . maps . ws , counts . routes , incomingKeys ) : syncHttpRoute ( route , workspace . _id , headers , existingData . maps . http , counts . routes , incomingKeys ) ) ;
436+ }
445437 }
438+
439+ await deleteStaleRequests ( existingData , incomingKeys , incomingRouteIds , counts . routes ) ;
440+ onProgress ?.( `Synced ${ serviceName } in ${ controlPlane . name } ` ) ;
446441}
447442
448443/** Upserts the project-level environment workspace and syncs Konnect proxy URL vars into it. Returns the environment id. */
@@ -532,28 +527,33 @@ async function syncControlPlane(
532527
533528 const ctx : ServiceSyncContext = { pat, controlPlane, project, region, globalEnvironmentId, signal, onProgress } ;
534529 const CONCURRENCY = 5 ;
535- for ( let i = 0 ; i < services . length ; i += CONCURRENCY ) {
536- signal ?. throwIfAborted ( ) ;
537- const batch = services . slice ( i , i + CONCURRENCY ) ;
538- const batchResults = await Promise . all ( batch . map ( async service => {
539- const localCounts = { services : zeroCounts ( ) , routes : zeroCounts ( ) } ;
540- const localSkipped : SkippedRoute [ ] = [ ] ;
541- await syncServiceWorkspace ( ctx , service , existingWorkspaceByServiceId . get ( service . id ) , localCounts , localSkipped ) ;
542- return { counts : localCounts , skipped : localSkipped } ;
543- } ) ) ;
544- for ( const { counts, skipped } of batchResults ) {
545- mergeCounts ( acc . serviceCounts , counts . services ) ;
546- mergeCounts ( acc . routeCounts , counts . routes ) ;
547- acc . skippedRoutes . push ( ...skipped ) ;
530+ const bufferId = await db . bufferChanges ( ) ;
531+ try {
532+ for ( let i = 0 ; i < services . length ; i += CONCURRENCY ) {
533+ signal ?. throwIfAborted ( ) ;
534+ const batch = services . slice ( i , i + CONCURRENCY ) ;
535+ const batchResults = await Promise . all ( batch . map ( async service => {
536+ const localCounts = { services : zeroCounts ( ) , routes : zeroCounts ( ) } ;
537+ const localSkipped : SkippedRoute [ ] = [ ] ;
538+ await syncServiceWorkspace ( ctx , service , existingWorkspaceByServiceId . get ( service . id ) , localCounts , localSkipped ) ;
539+ return { counts : localCounts , skipped : localSkipped } ;
540+ } ) ) ;
541+ for ( const { counts, skipped } of batchResults ) {
542+ mergeCounts ( acc . serviceCounts , counts . services ) ;
543+ mergeCounts ( acc . routeCounts , counts . routes ) ;
544+ acc . skippedRoutes . push ( ...skipped ) ;
545+ }
548546 }
549- }
550547
551- // Delete stale workspaces (services removed from this Control Plane)
552- for ( const workspace of existingWorkspaces ) {
553- if ( ! incomingServiceIds . has ( workspace . konnectServiceId ! ) ) {
554- await insoservices . workspace . remove ( workspace ) ;
555- acc . serviceCounts . deleted ++ ;
548+ // Delete stale workspaces (services removed from this Control Plane)
549+ for ( const workspace of existingWorkspaces ) {
550+ if ( ! incomingServiceIds . has ( workspace . konnectServiceId ! ) ) {
551+ await insoservices . workspace . remove ( workspace ) ;
552+ acc . serviceCounts . deleted ++ ;
553+ }
556554 }
555+ } finally {
556+ await db . flushChanges ( bufferId ) ;
557557 }
558558}
559559
0 commit comments