11import "../common/logger" ;
22import fs from "fs/promises" ;
33import { spawn } from "child_process" ;
4- import { response , Request as ServerRequest , Response as ServerResponse } from "express" ;
4+ import { Request as ServerRequest , Response as ServerResponse } from "express" ;
55import { NpmRegistryService , NpmRegistryConfigEntry , NpmRegistryConfig } from "../services/npmRegistry" ;
66
77
8+ type RequestConfig = {
9+ workspaceId : string ;
10+ npmRegistryConfig : NpmRegistryConfig ;
11+ }
12+
13+ type InnerRequestConfig = {
14+ workspaceId ?: string ;
15+ registry : NpmRegistryConfigEntry ;
16+ }
17+
818type PackagesVersionInfo = {
919 "dist-tags" : {
1020 latest : string
@@ -23,24 +33,24 @@ class PackageProcessingQueue {
2333 public static readonly promiseRegistry : { [ packageId : string ] : Promise < void > } = { } ;
2434 public static readonly resolveRegistry : { [ packageId : string ] :( ) => void } = { } ;
2535
26- public static add ( packageId : string ) {
36+ public static add ( packageId : string ) : void {
2737 PackageProcessingQueue . promiseRegistry [ packageId ] = new Promise < void > ( ( resolve ) => {
2838 PackageProcessingQueue . resolveRegistry [ packageId ] = resolve ;
2939 } ) ;
3040 }
3141
32- public static has ( packageId : string ) {
42+ public static has ( packageId : string ) : boolean {
3343 return ! ! PackageProcessingQueue . promiseRegistry [ packageId ] ;
3444 }
3545
36- public static wait ( packageId : string ) {
46+ public static wait ( packageId : string ) : Promise < void > {
3747 if ( ! PackageProcessingQueue . has ( packageId ) ) {
3848 return Promise . resolve ( ) ;
3949 }
4050 return PackageProcessingQueue . promiseRegistry [ packageId ] ;
4151 }
4252
43- public static resolve ( packageId : string ) {
53+ public static resolve ( packageId : string ) : void {
4454 if ( ! PackageProcessingQueue . has ( packageId ) ) {
4555 return ;
4656 }
@@ -78,10 +88,18 @@ export async function fetchRegistryWithConfig(request: ServerRequest, response:
7888 return response . status ( 400 ) . send ( `Invalid package path: ${ path } ` ) ;
7989 }
8090
81- const registryConfig : NpmRegistryConfig = request . body ;
82- const config = NpmRegistryService . getRegistryEntryForPackageWithConfig ( pathPackageInfo . packageId , registryConfig ) ;
91+ if ( ! request . body . workspaceId && ! request . body . npmRegistryConfig ) {
92+ return response . status ( 400 ) . send ( "Missing workspaceId and/or npmRegistryConfig" ) ;
93+ }
94+
95+ const { npmRegistryConfig} : RequestConfig = request . body ;
96+
97+ const registry = NpmRegistryService . getRegistryEntryForPackageWithConfig ( pathPackageInfo . packageId , npmRegistryConfig ) ;
8398
84- const registryResponse = await fetchFromRegistry ( path , config ) ;
99+ const registryResponse = await fetchFromRegistry ( path , registry ) ;
100+ if ( ! registryResponse . ok ) {
101+ return response . status ( registryResponse . status ) . send ( await registryResponse . text ( ) ) ;
102+ }
85103 response . json ( await registryResponse . json ( ) ) ;
86104 } catch ( error ) {
87105 logger . error ( "Error fetching registry" , error ) ;
@@ -99,8 +117,11 @@ export async function fetchRegistry(request: ServerRequest, response: ServerResp
99117 return response . status ( 400 ) . send ( `Invalid package path: ${ path } ` ) ;
100118 }
101119
102- const config = NpmRegistryService . getInstance ( ) . getRegistryEntryForPackage ( pathPackageInfo . packageId ) ;
103- const registryResponse = await fetchFromRegistry ( path , config ) ;
120+ const registry = NpmRegistryService . getInstance ( ) . getRegistryEntryForPackage ( pathPackageInfo . packageId ) ;
121+ const registryResponse = await fetchFromRegistry ( path , registry ) ;
122+ if ( ! registryResponse . ok ) {
123+ return response . status ( registryResponse . status ) . send ( await registryResponse . text ( ) ) ;
124+ }
104125 response . json ( await registryResponse . json ( ) ) ;
105126 } catch ( error ) {
106127 logger . error ( "Error fetching registry" , error ) ;
@@ -124,10 +145,15 @@ export async function fetchPackageFileWithConfig(request: ServerRequest, respons
124145 return response . status ( 400 ) . send ( `Invalid package path: ${ path } ` ) ;
125146 }
126147
127- const registryConfig : NpmRegistryConfig = request . body ;
128- const config = NpmRegistryService . getRegistryEntryForPackageWithConfig ( pathPackageInfo . packageId , registryConfig ) ;
148+ if ( ! request . body . workspaceId && ! request . body . npmRegistryConfig ) {
149+ return response . status ( 400 ) . send ( "Missing workspaceId and/or npmRegistryConfig" ) ;
150+ }
129151
130- fetchPackageFileInner ( request , response , config ) ;
152+ const { workspaceId, npmRegistryConfig} : RequestConfig = request . body ;
153+ const registryConfig : NpmRegistryConfig = npmRegistryConfig ;
154+ const registry = NpmRegistryService . getRegistryEntryForPackageWithConfig ( pathPackageInfo . packageId , registryConfig ) ;
155+
156+ fetchPackageFileInner ( request , response , { workspaceId, registry} ) ;
131157}
132158
133159export async function fetchPackageFile ( request : ServerRequest , response : ServerResponse ) {
@@ -139,12 +165,14 @@ export async function fetchPackageFile(request: ServerRequest, response: ServerR
139165 return response . status ( 400 ) . send ( `Invalid package path: ${ path } ` ) ;
140166 }
141167
142- const config = NpmRegistryService . getInstance ( ) . getRegistryEntryForPackage ( pathPackageInfo . packageId ) ;
143- fetchPackageFileInner ( request , response , config ) ;
168+ const registry = NpmRegistryService . getInstance ( ) . getRegistryEntryForPackage ( pathPackageInfo . packageId ) ;
169+ fetchPackageFileInner ( request , response , { registry } ) ;
144170}
145171
146- async function fetchPackageFileInner ( request : ServerRequest , response : ServerResponse , config : NpmRegistryConfigEntry ) {
172+ async function fetchPackageFileInner ( request : ServerRequest , response : ServerResponse , config : InnerRequestConfig ) {
147173 try {
174+ const { workspaceId, registry} = config
175+ logger . info ( `Fetch file for workspaceId: ${ workspaceId } ` ) ;
148176 const path = request . path . replace ( fetchPackageFileBasePath , "" ) ;
149177 const pathPackageInfo = parsePackageInfoFromPath ( path ) ;
150178 if ( ! pathPackageInfo ) {
@@ -157,7 +185,7 @@ async function fetchPackageFileInner(request: ServerRequest, response: ServerRes
157185
158186 let packageInfo : PackagesVersionInfo | null = null ;
159187 if ( version === "latest" ) {
160- const packageInfo : PackagesVersionInfo | null = await fetchPackageInfo ( packageId , config ) ;
188+ const packageInfo : PackagesVersionInfo | null = await fetchPackageInfo ( packageId , registry ) ;
161189 if ( packageInfo === null ) {
162190 return response . status ( 404 ) . send ( "Not found" ) ;
163191 }
@@ -170,14 +198,15 @@ async function fetchPackageFileInner(request: ServerRequest, response: ServerRes
170198 await PackageProcessingQueue . wait ( packageId ) ;
171199 }
172200
173- const packageBaseDir = `${ CACHE_DIR } /${ packageId } /${ packageVersion } /package` ;
201+ const baseDir = `${ CACHE_DIR } /${ workspaceId ?? "default" } ` ;
202+ const packageBaseDir = `${ baseDir } /${ packageId } /${ packageVersion } /package` ;
174203 const packageExists = await fileExists ( `${ packageBaseDir } /package.json` )
175204 if ( ! packageExists ) {
176205 try {
177206 logger . info ( `Package does not exist, fetch from registy: ${ packageId } @${ packageVersion } ` ) ;
178207 PackageProcessingQueue . add ( packageId ) ;
179208 if ( ! packageInfo ) {
180- packageInfo = await fetchPackageInfo ( packageId , config ) ;
209+ packageInfo = await fetchPackageInfo ( packageId , registry ) ;
181210 }
182211
183212 if ( ! packageInfo || ! packageInfo . versions || ! packageInfo . versions [ packageVersion ] ) {
@@ -186,9 +215,9 @@ async function fetchPackageFileInner(request: ServerRequest, response: ServerRes
186215
187216 const tarball = packageInfo . versions [ packageVersion ] . dist . tarball ;
188217 logger . info ( `Fetching tarball: ${ tarball } ` ) ;
189- await fetchAndUnpackTarball ( tarball , packageId , packageVersion , config ) ;
218+ await fetchAndUnpackTarball ( tarball , packageId , packageVersion , registry , baseDir ) ;
190219 } catch ( error ) {
191- logger . error ( " Error fetching package tarball" , error ) ;
220+ logger . error ( ` Error fetching package: ${ error } ${ ( error as { stack : string } ) . stack } ` ) ;
192221 return response . status ( 500 ) . send ( "Internal server error" ) ;
193222 } finally {
194223 PackageProcessingQueue . resolve ( packageId ) ;
@@ -224,6 +253,7 @@ function parsePackageInfoFromPath(path: string): {packageId: string, organizatio
224253 }
225254
226255 let { packageId, organization, name, version, file} = matches . groups ;
256+ // also test for alpha and beta versions like 0.0.1-beta1
227257 version = / ^ \d + \. \d + \. \d + ( - [ \w \d ] + ) ? / . test ( version ) ? version : "latest" ;
228258
229259 return { packageId, organization, name, version, file} ;
@@ -265,25 +295,28 @@ function fetchPackageInfo(packageName: string, config: NpmRegistryConfigEntry):
265295 } ) ;
266296}
267297
268- async function fetchAndUnpackTarball ( url : string , packageId : string , packageVersion : string , config : NpmRegistryConfigEntry ) {
298+ async function fetchAndUnpackTarball ( url : string , packageId : string , packageVersion : string , config : NpmRegistryConfigEntry , baseDir : string ) {
299+ if ( ! await fileExists ( baseDir ) ) {
300+ await fs . mkdir ( baseDir , { recursive : true } ) ;
301+ }
302+
303+ // Fetch tarball
269304 const response : Response = await fetchFromRegistry ( url , config ) ;
270305 const arrayBuffer = await response . arrayBuffer ( ) ;
271306 const buffer = Buffer . from ( arrayBuffer ) ;
272- const path = `${ CACHE_DIR } /${ url . split ( "https://github.com/" ) . pop ( ) } ` ;
307+ const path = `${ baseDir } /${ url . split ( "https://github.com/" ) . pop ( ) } ` ;
273308 await fs . writeFile ( path , buffer ) ;
274- await unpackTarball ( path , packageId , packageVersion ) ;
275- await fs . unlink ( path ) ;
276- }
277-
278- async function unpackTarball ( path : string , packageId : string , packageVersion : string ) {
279- const destinationPath = `${ CACHE_DIR } /${ packageId } /${ packageVersion } ` ;
309+
310+ // Unpack tarball
311+ const destinationPath = `${ baseDir } /${ packageId } /${ packageVersion } ` ;
280312 await fs . mkdir ( destinationPath , { recursive : true } ) ;
281313 await new Promise < void > ( ( resolve , reject ) => {
282314 const tar = spawn ( "tar" , [ "-xvf" , path , "-C" , destinationPath ] ) ;
283- tar . on ( "close" , ( code ) => {
284- code === 0 ? resolve ( ) : reject ( ) ;
285- } ) ;
315+ tar . on ( "close" , ( code ) => code === 0 ? resolve ( ) : reject ( ) ) ;
286316 } ) ;
317+
318+ // Cleanup
319+ await fs . unlink ( path ) ;
287320}
288321
289322async function fileExists ( filePath : string ) : Promise < boolean > {
0 commit comments