@@ -7,6 +7,8 @@ import { XMLParser } from 'fast-xml-parser'
77
88import { getServiceChildLogger } from '@crowd/logging'
99
10+ import { resolveRegistryBaseUrl } from './registry'
11+
1012const log = getServiceChildLogger ( 'maven' )
1113
1214// ─── Types ────────────────────────────────────────────────────────────────────
@@ -56,14 +58,6 @@ interface PomPerson {
5658
5759// ─── Config ───────────────────────────────────────────────────────────────────
5860
59- // Lazy getter — evaluated at call time so the entry point can set MAVEN_FETCHER_BASE_URL
60- // before the first HTTP request without worrying about module load order.
61- // Backfill sets MAVEN_FETCHER_BASE_URL from MAVEN_FETCHER_BASE_URL_BACKFILL (GCS mirror);
62- // the incremental Temporal worker sets it from MAVEN_FETCHER_BASE_URL_INCREMENTAL (repo1).
63- function getMavenRepo ( ) : string {
64- return process . env . MAVEN_FETCHER_BASE_URL ?? 'https://repo1.maven.org/maven2'
65- }
66-
6761const REQUEST_TIMEOUT_MS = 15_000
6862
6963const parser = new XMLParser ( {
@@ -78,10 +72,12 @@ const parser = new XMLParser({
7872const MAX_RETRIES = 3
7973const RETRY_BASE_MS = 2_000
8074
75+ // prettier-ignore
8176async function sleep ( ms : number ) : Promise < void > {
8277 return new Promise ( ( r ) => setTimeout ( r , ms ) )
8378}
8479
80+ // prettier-ignore
8581async function getWithRetry ( url : string ) : Promise < string > {
8682 for ( let attempt = 0 ; attempt <= MAX_RETRIES ; attempt ++ ) {
8783 try {
@@ -108,17 +104,18 @@ async function getWithRetry(url: string): Promise<string> {
108104
109105// ─── POM fetch ────────────────────────────────────────────────────────────────
110106
111- export function buildPomUrl ( groupId : string , artifactId : string , version : string ) : string {
112- const groupPath = groupId . replace ( / \. / g, '/' )
113- return `${ getMavenRepo ( ) } /${ groupPath } /${ artifactId } /${ version } /${ artifactId } -${ version } .pom`
114- }
115-
116- export async function fetchPom (
107+ export function buildPomUrl (
117108 groupId : string ,
118109 artifactId : string ,
119110 version : string ,
120- url : string ,
121- ) : Promise < PomData | null > {
111+ baseUrl ?: string ,
112+ ) : string {
113+ const groupPath = groupId . replace ( / \. / g, '/' )
114+ return `${ baseUrl ?? resolveRegistryBaseUrl ( groupId ) } /${ groupPath } /${ artifactId } /${ version } /${ artifactId } -${ version } .pom`
115+ }
116+
117+ // prettier-ignore
118+ export async function fetchPom ( groupId : string , artifactId : string , version : string , url : string ) : Promise < PomData | null > {
122119 try {
123120 const data = await getWithRetry ( url )
124121 const parsed = parser . parse ( data )
@@ -160,6 +157,7 @@ export async function fetchPom(
160157const POM_CACHE_MAX_ENTRIES = 5_000
161158
162159const pomCache = new Map < string , PomData > ( )
160+ // prettier-ignore
163161const inFlight = new Map < string , Promise < PomData | null > > ( )
164162const pomCacheStats = { hits : 0 , coalesced : 0 , misses : 0 , evictions : 0 }
165163
@@ -186,11 +184,8 @@ function cacheSet(key: string, pom: PomData): void {
186184 * await it instead of issuing a duplicate request.
187185 * - Miss → performs the network fetch; caches the result only if non-null.
188186 */
189- async function fetchPomCached (
190- groupId : string ,
191- artifactId : string ,
192- version : string ,
193- ) : Promise < PomData | null > {
187+ // prettier-ignore
188+ async function fetchPomCached ( groupId : string , artifactId : string , version : string , baseUrl ?: string ) : Promise < PomData | null > {
194189 const key = pomCacheKey ( groupId , artifactId , version )
195190
196191 const cached = pomCache . get ( key )
@@ -208,7 +203,12 @@ async function fetchPomCached(
208203 }
209204
210205 pomCacheStats . misses ++
211- const promise = fetchPom ( groupId , artifactId , version , buildPomUrl ( groupId , artifactId , version ) )
206+ const promise = fetchPom (
207+ groupId ,
208+ artifactId ,
209+ version ,
210+ buildPomUrl ( groupId , artifactId , version , baseUrl ) ,
211+ )
212212 . then ( ( pom ) => {
213213 if ( pom ) cacheSet ( key , pom )
214214 return pom
@@ -264,14 +264,9 @@ interface ResolvedFields {
264264 hops : number
265265}
266266
267- async function resolveWithInheritance (
268- groupId : string ,
269- artifactId : string ,
270- version : string ,
271- depth = 0 ,
272- visited = new Set < string > ( ) ,
273- ) : Promise < ResolvedFields > {
274- const pom = await fetchPomCached ( groupId , artifactId , version )
267+ // prettier-ignore
268+ async function resolveWithInheritance ( groupId : string , artifactId : string , version : string , depth = 0 , visited = new Set < string > ( ) , baseUrl ?: string ) : Promise < ResolvedFields > {
269+ const pom = await fetchPomCached ( groupId , artifactId , version , baseUrl )
275270 if ( ! pom ) return emptyFields ( depth )
276271
277272 const licenses = extractLicenses ( pom )
@@ -299,6 +294,7 @@ async function resolveWithInheritance(
299294 parent . version ,
300295 depth + 1 ,
301296 visited ,
297+ resolveRegistryBaseUrl ( parent . groupId ) ,
302298 )
303299 return {
304300 description : extractStr ( pom . description ) ?? parentFields . description ,
@@ -333,14 +329,11 @@ async function resolveWithInheritance(
333329 * Currently unused: kept as a lightweight option for high-throughput paths that
334330 * don't need parent inheritance.
335331 */
336- export async function extractArtifactDirect (
337- groupId : string ,
338- artifactId : string ,
339- version : string ,
340- ) : Promise < PomExtractionResult > {
332+ // prettier-ignore
333+ export async function extractArtifactDirect ( groupId : string , artifactId : string , version : string , baseUrl ?: string ) : Promise < PomExtractionResult > {
341334 const purl = `pkg:maven/${ groupId } /${ artifactId } @${ version } `
342- const pomUrl = buildPomUrl ( groupId , artifactId , version )
343- const pom = await fetchPomCached ( groupId , artifactId , version )
335+ const pomUrl = buildPomUrl ( groupId , artifactId , version , baseUrl )
336+ const pom = await fetchPomCached ( groupId , artifactId , version , baseUrl )
344337
345338 if ( ! pom ) {
346339 return {
@@ -387,15 +380,12 @@ export async function extractArtifactDirect(
387380 * the parent chain to inherit licenses and SCM when not in the direct POM.
388381 * Always returns a result object; errors are captured in `result.error`.
389382 */
390- export async function extractArtifact (
391- groupId : string ,
392- artifactId : string ,
393- version : string ,
394- ) : Promise < PomExtractionResult > {
383+ // prettier-ignore
384+ export async function extractArtifact ( groupId : string , artifactId : string , version : string , baseUrl ?: string ) : Promise < PomExtractionResult > {
395385 const purl = `pkg:maven/${ groupId } /${ artifactId } @${ version } `
396386
397- const pomUrl = buildPomUrl ( groupId , artifactId , version )
398- const rootPom = await fetchPomCached ( groupId , artifactId , version )
387+ const pomUrl = buildPomUrl ( groupId , artifactId , version , baseUrl )
388+ const rootPom = await fetchPomCached ( groupId , artifactId , version , baseUrl )
399389 if ( ! rootPom ) {
400390 return {
401391 groupId,
@@ -415,7 +405,14 @@ export async function extractArtifact(
415405 }
416406
417407 try {
418- const resolved = await resolveWithInheritance ( groupId , artifactId , version )
408+ const resolved = await resolveWithInheritance (
409+ groupId ,
410+ artifactId ,
411+ version ,
412+ 0 ,
413+ new Set ( ) ,
414+ baseUrl ,
415+ )
419416 return {
420417 groupId,
421418 artifactId,
@@ -499,7 +496,7 @@ function extractLicenses(pom: PomData): string[] {
499496 const raw = pom . licenses ?. license
500497 if ( ! raw ) return [ ]
501498 const list = Array . isArray ( raw ) ? raw : [ raw ]
502- return ( list as Array < { name ?: unknown } > )
499+ return ( list as { name ?: unknown } [ ] )
503500 . map ( ( l ) => extractStr ( l ?. name ) )
504501 . filter ( ( n ) : n is string => n !== null )
505502}
0 commit comments