( files: ImplementationFile[], readFile: ReadFile, readFileMetadata: ReadFileMetadata, apiName: string, )
| 304 | type CustomFetchFunc = (files: ImplementationFile[]) => Promise<ImplementationEntry[]>; |
| 305 | |
| 306 | async function fetchFiles( |
| 307 | files: ImplementationFile[], |
| 308 | readFile: ReadFile, |
| 309 | readFileMetadata: ReadFileMetadata, |
| 310 | apiName: string, |
| 311 | ) { |
| 312 | const sem = semaphore(MAX_CONCURRENT_DOWNLOADS); |
| 313 | const promises = [] as Promise<ImplementationEntry | { error: boolean }>[]; |
| 314 | files.forEach(file => { |
| 315 | promises.push( |
| 316 | new Promise(resolve => |
| 317 | sem.take(async () => { |
| 318 | try { |
| 319 | const [data, fileMetadata] = await Promise.all([ |
| 320 | readFile(file.path, file.id, { parseText: true }), |
| 321 | readFileMetadata(file.path, file.id), |
| 322 | ]); |
| 323 | resolve({ file: { ...file, ...fileMetadata }, data: data as string }); |
| 324 | sem.leave(); |
| 325 | } catch (error) { |
| 326 | sem.leave(); |
| 327 | console.error(`failed to load file from ${apiName}: ${file.path}`); |
| 328 | resolve({ error: true }); |
| 329 | } |
| 330 | }), |
| 331 | ), |
| 332 | ); |
| 333 | }); |
| 334 | return Promise.all(promises).then(loadedEntries => |
| 335 | loadedEntries.filter(loadedEntry => !(loadedEntry as { error: boolean }).error), |
| 336 | ) as Promise<ImplementationEntry[]>; |
| 337 | } |
| 338 | |
| 339 | export async function entriesByFolder( |
| 340 | listFiles: () => Promise<ImplementationFile[]>, |
no test coverage detected