diff --git a/package.json b/package.json index 93d481a1..74baa3aa 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "js-yaml": "^5.4.1", "jwt-decode": "^4.0.0", "semver": "^7.8.5", - "tar-stream": "^3.2.0", + "tar-stream": "^3.2.1", "tmp": "^0.2.7" }, "devDependencies": { @@ -71,7 +71,6 @@ "@types/js-yaml": "^4.0.9", "@types/node": "^24.11.0", "@types/semver": "^7.7.1", - "@types/tar-stream": "^3.1.4", "@types/tmp": "^0.2.6", "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", diff --git a/src/oci/oci.ts b/src/oci/oci.ts index c1dff3a2..8e5386f7 100644 --- a/src/oci/oci.ts +++ b/src/oci/oci.ts @@ -75,66 +75,61 @@ export class OCI { }; } - public static loadArchive(opts: LoadArchiveOpts): Promise { - return new Promise((resolve, reject) => { - const tarex: tar.Extract = tar.extract(); - - let rootIndex: Index; - let rootLayout: ImageLayout; - const indexes: Record = {}; - const manifests: Record = {}; - const images: Record = {}; - const blobs: Record = {}; - - tarex.on('entry', async (header, stream, next) => { - if (header.type === 'file') { - const filename = path.normalize(header.name); - if (filename === IMAGE_INDEX_FILE_V1) { - rootIndex = await OCI.streamToJson(stream); - } else if (filename === IMAGE_LAYOUT_FILE_V1) { - rootLayout = await OCI.streamToJson(stream); - } else if (filename.startsWith(path.join(IMAGE_BLOBS_DIR_V1, path.sep))) { - const blob = await OCI.extractBlob(stream); - const digest = `${filename.split(path.sep)[1]}:${filename.split(path.sep)[filename.split(path.sep).length - 1]}`; - if (OCI.isIndex(blob)) { - indexes[digest] = JSON.parse(blob); - } else if (OCI.isManifest(blob)) { - manifests[digest] = JSON.parse(blob); - } else if (OCI.isImage(blob)) { - images[digest] = JSON.parse(blob); - } else { - blobs[digest] = blob; - } + public static async loadArchive(opts: LoadArchiveOpts): Promise { + const tarex = tar.extract(); + + let rootIndex: Index | undefined; + let rootLayout: ImageLayout | undefined; + const indexes: Record = {}; + const manifests: Record = {}; + const images: Record = {}; + const blobs: Record = {}; + + fs.createReadStream(opts.file) + .pipe(gunzip()) + .pipe(tarex as unknown as NodeJS.WritableStream); + + for await (const entry of tarex) { + const header = entry.header; + const stream = Readable.from(entry as unknown as AsyncIterable); + if (header.type === 'file') { + const filename = path.normalize(header.name); + if (filename === IMAGE_INDEX_FILE_V1) { + rootIndex = await OCI.streamToJson(stream); + } else if (filename === IMAGE_LAYOUT_FILE_V1) { + rootLayout = await OCI.streamToJson(stream); + } else if (filename.startsWith(path.join(IMAGE_BLOBS_DIR_V1, path.sep))) { + const blob = await OCI.extractBlob(stream); + const digest = `${filename.split(path.sep)[1]}:${filename.split(path.sep)[filename.split(path.sep).length - 1]}`; + if (OCI.isIndex(blob)) { + indexes[digest] = JSON.parse(blob); + } else if (OCI.isManifest(blob)) { + manifests[digest] = JSON.parse(blob); + } else if (OCI.isImage(blob)) { + images[digest] = JSON.parse(blob); } else { - reject(new Error(`Invalid OCI archive: unexpected file ${filename}`)); + blobs[digest] = blob; } + } else { + throw new Error(`Invalid OCI archive: unexpected file ${filename}`); } - stream.resume(); - next(); - }); - - tarex.on('finish', () => { - if (!rootIndex || !rootLayout) { - reject(new Error('Invalid OCI archive: missing index or layout')); - } - resolve({ - root: { - index: rootIndex, - layout: rootLayout - }, - indexes: indexes, - manifests: manifests, - images: images, - blobs: blobs - } as Archive); - }); - - tarex.on('error', error => { - reject(error); - }); + } + stream.resume(); + } + if (!rootIndex || !rootLayout) { + throw new Error('Invalid OCI archive: missing index or layout'); + } - fs.createReadStream(opts.file).pipe(gunzip()).pipe(tarex); - }); + return { + root: { + index: rootIndex, + layout: rootLayout + }, + indexes: indexes, + manifests: manifests, + images: images, + blobs: blobs + } as Archive; } // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/yarn.lock b/yarn.lock index eb1baaaf..5006a4a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -476,7 +476,6 @@ __metadata: "@types/js-yaml": "npm:^4.0.9" "@types/node": "npm:^24.11.0" "@types/semver": "npm:^7.7.1" - "@types/tar-stream": "npm:^3.1.4" "@types/tmp": "npm:^0.2.6" "@typescript-eslint/eslint-plugin": "npm:^8.56.1" "@typescript-eslint/parser": "npm:^8.56.1" @@ -496,7 +495,7 @@ __metadata: prettier: "npm:^3.8.1" rimraf: "npm:^6.1.3" semver: "npm:^7.8.5" - tar-stream: "npm:^3.2.0" + tar-stream: "npm:^3.2.1" tmp: "npm:^0.2.7" typescript: "npm:^5.9.3" vitest: "npm:^4.0.18" @@ -1434,15 +1433,6 @@ __metadata: languageName: node linkType: hard -"@types/tar-stream@npm:^3.1.4": - version: 3.1.4 - resolution: "@types/tar-stream@npm:3.1.4" - dependencies: - "@types/node": "npm:*" - checksum: 10/9d1e182315c2189e18293b5a884edb9803c70772d37914870fbdb0bd659970e0d8effe002b19415ee251c422e3f169013668f3acf41feb9371040d0f0867d7b7 - languageName: node - linkType: hard - "@types/tmp@npm:^0.2.6": version: 0.2.6 resolution: "@types/tmp@npm:0.2.6" @@ -4587,15 +4577,15 @@ __metadata: languageName: node linkType: hard -"tar-stream@npm:^3.2.0": - version: 3.2.0 - resolution: "tar-stream@npm:3.2.0" +"tar-stream@npm:^3.2.1": + version: 3.2.1 + resolution: "tar-stream@npm:3.2.1" dependencies: b4a: "npm:^1.6.4" bare-fs: "npm:^4.5.5" fast-fifo: "npm:^1.2.0" streamx: "npm:^2.15.0" - checksum: 10/ce57a81521de73ae7a3b7d55a08da50d6771427c249bfa89a208518e48faf5254c8fa7201a8f5419ab8bde9601a74e6dd512b31a13ec89774aec96178f99a8d3 + checksum: 10/1a09f857e0789489476d99fb716fa6d209e7d19aa73b7a4b23f40eeba46792d5007ca62fde4fca8f643ed71ace1c526a293c655718a0409275a45a8aa86b562a languageName: node linkType: hard