diff --git a/.pnp.cjs b/.pnp.cjs index 61ededd..57f5526 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -17,6 +17,7 @@ const RAW_RUNTIME_STATE = ],\ "enableTopLevelFallback": true,\ "ignorePatternData": "(^(?:\\\\.yarn\\\\/sdks(?:\\\\/(?!\\\\.{1,2}(?:\\\\/|$))(?:(?:(?!(?:^|\\\\/)\\\\.{1,2}(?:\\\\/|$)).)*?)|$))$)",\ + "pnpZipBackend": "libzip",\ "fallbackExclusionList": [\ ["clean-architecture", ["workspace:."]]\ ],\ @@ -28,7 +29,7 @@ const RAW_RUNTIME_STATE = "packageLocation": "./",\ "packageDependencies": [\ ["@types/jest", "npm:29.5.14"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["clean-architecture", "workspace:."],\ ["jest", "virtual:0bdaf34b18f64a61d994ea5745297b16ca55696ece813438927b218d5900be7b3b1afb8777870498f0544991088e71be7d6609a8d92700ec537ab224121fd8a4#npm:29.7.0"],\ ["ts-jest", "virtual:0bdaf34b18f64a61d994ea5745297b16ca55696ece813438927b218d5900be7b3b1afb8777870498f0544991088e71be7d6609a8d92700ec537ab224121fd8a4#npm:29.3.1"],\ @@ -882,7 +883,7 @@ const RAW_RUNTIME_STATE = "packageDependencies": [\ ["@jest/console", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["chalk", "npm:4.1.2"],\ ["jest-message-util", "npm:29.7.0"],\ ["jest-util", "npm:29.7.0"],\ @@ -908,7 +909,7 @@ const RAW_RUNTIME_STATE = ["@jest/test-result", "npm:29.7.0"],\ ["@jest/transform", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["@types/node-notifier", null],\ ["ansi-escapes", "npm:4.3.2"],\ ["chalk", "npm:4.1.2"],\ @@ -948,7 +949,7 @@ const RAW_RUNTIME_STATE = ["@jest/environment", "npm:29.7.0"],\ ["@jest/fake-timers", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["jest-mock", "npm:29.7.0"]\ ],\ "linkType": "HARD"\ @@ -990,7 +991,7 @@ const RAW_RUNTIME_STATE = ["@jest/fake-timers", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ ["@sinonjs/fake-timers", "npm:10.3.0"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["jest-message-util", "npm:29.7.0"],\ ["jest-mock", "npm:29.7.0"],\ ["jest-util", "npm:29.7.0"]\ @@ -1029,7 +1030,7 @@ const RAW_RUNTIME_STATE = ["@jest/transform", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ ["@jridgewell/trace-mapping", "npm:0.3.19"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["@types/node-notifier", null],\ ["chalk", "npm:4.1.2"],\ ["collect-v8-coverage", "npm:1.0.2"],\ @@ -1137,7 +1138,7 @@ const RAW_RUNTIME_STATE = ["@jest/types", "npm:29.6.3"],\ ["@types/istanbul-lib-coverage", "npm:2.0.4"],\ ["@types/istanbul-reports", "npm:3.0.1"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["@types/yargs", "npm:17.0.24"],\ ["chalk", "npm:4.1.2"]\ ],\ @@ -1346,7 +1347,7 @@ const RAW_RUNTIME_STATE = "packageLocation": "../../../../cache/others/berry/cache/@types-graceful-fs-npm-4.1.6-1eadcf742d-10c0.zip/node_modules/@types/graceful-fs/",\ "packageDependencies": [\ ["@types/graceful-fs", "npm:4.1.6"],\ - ["@types/node", "npm:22.14.0"]\ + ["@types/node", "npm:22.15.3"]\ ],\ "linkType": "HARD"\ }]\ @@ -1392,10 +1393,10 @@ const RAW_RUNTIME_STATE = }]\ ]],\ ["@types/node", [\ - ["npm:22.14.0", {\ - "packageLocation": "../../../../cache/others/berry/cache/@types-node-npm-22.14.0-a09e354ed5-10c0.zip/node_modules/@types/node/",\ + ["npm:22.15.3", {\ + "packageLocation": "../../../../cache/others/berry/cache/@types-node-npm-22.15.3-ab71dd4f9d-10c0.zip/node_modules/@types/node/",\ "packageDependencies": [\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["undici-types", "npm:6.21.0"]\ ],\ "linkType": "HARD"\ @@ -1938,7 +1939,7 @@ const RAW_RUNTIME_STATE = "packageLocation": "./",\ "packageDependencies": [\ ["@types/jest", "npm:29.5.14"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["clean-architecture", "workspace:."],\ ["jest", "virtual:0bdaf34b18f64a61d994ea5745297b16ca55696ece813438927b218d5900be7b3b1afb8777870498f0544991088e71be7d6609a8d92700ec537ab224121fd8a4#npm:29.7.0"],\ ["ts-jest", "virtual:0bdaf34b18f64a61d994ea5745297b16ca55696ece813438927b218d5900be7b3b1afb8777870498f0544991088e71be7d6609a8d92700ec537ab224121fd8a4#npm:29.3.1"],\ @@ -2959,7 +2960,7 @@ const RAW_RUNTIME_STATE = ["@jest/expect", "npm:29.7.0"],\ ["@jest/test-result", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["chalk", "npm:4.1.2"],\ ["co", "npm:4.6.0"],\ ["dedent", "virtual:f7679858c638e2e5ade31901dd2b1e5007918fdc7d84fefb11f4200f46ba2e43b9d662fb793507b517bb1e725144e51f6d68f60f9f6100fd52144f042f58f0bc#npm:1.5.1"],\ @@ -3064,7 +3065,7 @@ const RAW_RUNTIME_STATE = ["@babel/core", "npm:7.22.15"],\ ["@jest/test-sequencer", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["@types/ts-node", null],\ ["babel-jest", "virtual:939a7fe9ef9185d9378b96533b42cb7bd61bd3e34d0edd537f69a76537c5748d3b4733d3050f6f18ab17ac63143f81e7f121e602a5d38c959251dc0768083e9b#npm:29.7.0"],\ ["chalk", "npm:4.1.2"],\ @@ -3151,7 +3152,7 @@ const RAW_RUNTIME_STATE = ["@jest/environment", "npm:29.7.0"],\ ["@jest/fake-timers", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["jest-environment-node", "npm:29.7.0"],\ ["jest-mock", "npm:29.7.0"],\ ["jest-util", "npm:29.7.0"]\ @@ -3174,7 +3175,7 @@ const RAW_RUNTIME_STATE = "packageDependencies": [\ ["@jest/types", "npm:29.6.3"],\ ["@types/graceful-fs", "npm:4.1.6"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["anymatch", "npm:3.1.3"],\ ["fb-watchman", "npm:2.0.2"],\ ["fsevents", "patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"],\ @@ -3263,7 +3264,7 @@ const RAW_RUNTIME_STATE = "packageLocation": "../../../../cache/others/berry/cache/jest-mock-npm-29.7.0-22c4769d06-10c0.zip/node_modules/jest-mock/",\ "packageDependencies": [\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["jest-mock", "npm:29.7.0"],\ ["jest-util", "npm:29.7.0"]\ ],\ @@ -3339,7 +3340,7 @@ const RAW_RUNTIME_STATE = ["@jest/test-result", "npm:29.7.0"],\ ["@jest/transform", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["chalk", "npm:4.1.2"],\ ["emittery", "npm:0.13.1"],\ ["graceful-fs", "npm:4.2.11"],\ @@ -3371,7 +3372,7 @@ const RAW_RUNTIME_STATE = ["@jest/test-result", "npm:29.7.0"],\ ["@jest/transform", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["chalk", "npm:4.1.2"],\ ["cjs-module-lexer", "npm:1.2.3"],\ ["collect-v8-coverage", "npm:1.0.2"],\ @@ -3425,7 +3426,7 @@ const RAW_RUNTIME_STATE = "packageLocation": "../../../../cache/others/berry/cache/jest-util-npm-29.6.3-6ffdea2c1c-10c0.zip/node_modules/jest-util/",\ "packageDependencies": [\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["chalk", "npm:4.1.2"],\ ["ci-info", "npm:3.8.0"],\ ["graceful-fs", "npm:4.2.11"],\ @@ -3438,7 +3439,7 @@ const RAW_RUNTIME_STATE = "packageLocation": "../../../../cache/others/berry/cache/jest-util-npm-29.7.0-ff1d59714b-10c0.zip/node_modules/jest-util/",\ "packageDependencies": [\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["chalk", "npm:4.1.2"],\ ["ci-info", "npm:3.8.0"],\ ["graceful-fs", "npm:4.2.11"],\ @@ -3469,7 +3470,7 @@ const RAW_RUNTIME_STATE = "packageDependencies": [\ ["@jest/test-result", "npm:29.7.0"],\ ["@jest/types", "npm:29.6.3"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["ansi-escapes", "npm:4.3.2"],\ ["chalk", "npm:4.1.2"],\ ["emittery", "npm:0.13.1"],\ @@ -3484,7 +3485,7 @@ const RAW_RUNTIME_STATE = ["npm:29.7.0", {\ "packageLocation": "../../../../cache/others/berry/cache/jest-worker-npm-29.7.0-4d3567fed6-10c0.zip/node_modules/jest-worker/",\ "packageDependencies": [\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["jest-util", "npm:29.7.0"],\ ["jest-worker", "npm:29.7.0"],\ ["merge-stream", "npm:2.0.0"],\ @@ -4680,7 +4681,7 @@ const RAW_RUNTIME_STATE = ["@tsconfig/node12", "npm:1.0.11"],\ ["@tsconfig/node14", "npm:1.0.3"],\ ["@tsconfig/node16", "npm:1.0.4"],\ - ["@types/node", "npm:22.14.0"],\ + ["@types/node", "npm:22.15.3"],\ ["@types/swc__core", null],\ ["@types/swc__wasm", null],\ ["@types/typescript", null],\ @@ -9114,14 +9115,16 @@ class ZipOpenFS extends MountFS { return new ZipFS(p, { baseFs, readOnly: readOnlyArchives, - stats: baseFs.statSync(p) + stats: baseFs.statSync(p), + customZipImplementation: opts.customZipImplementation }); }; const factoryPromise = async (baseFs, p) => { const zipOptions = { baseFs, readOnly: readOnlyArchives, - stats: await baseFs.statPromise(p) + stats: await baseFs.statPromise(p), + customZipImplementation: opts.customZipImplementation }; return () => { return new ZipFS(p, zipOptions); @@ -9136,6 +9139,236 @@ class ZipOpenFS extends MountFS { } } +class LibzipError extends Error { + code; + constructor(message, code) { + super(message); + this.name = `Libzip Error`; + this.code = code; + } +} +class LibZipImpl { + libzip; + lzSource; + zip; + listings; + symlinkCount; + filesShouldBeCached = true; + constructor(opts) { + const buffer = `buffer` in opts ? opts.buffer : opts.baseFs.readFileSync(opts.path); + this.libzip = getInstance(); + const errPtr = this.libzip.malloc(4); + try { + let flags = 0; + if (opts.readOnly) + flags |= this.libzip.ZIP_RDONLY; + const lzSource = this.allocateUnattachedSource(buffer); + try { + this.zip = this.libzip.openFromSource(lzSource, flags, errPtr); + this.lzSource = lzSource; + } catch (error) { + this.libzip.source.free(lzSource); + throw error; + } + if (this.zip === 0) { + const error = this.libzip.struct.errorS(); + this.libzip.error.initWithCode(error, this.libzip.getValue(errPtr, `i32`)); + throw this.makeLibzipError(error); + } + } finally { + this.libzip.free(errPtr); + } + const entryCount = this.libzip.getNumEntries(this.zip, 0); + const listings = new Array(entryCount); + for (let t = 0; t < entryCount; ++t) + listings[t] = this.libzip.getName(this.zip, t, 0); + this.listings = listings; + this.symlinkCount = this.libzip.ext.countSymlinks(this.zip); + if (this.symlinkCount === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + getSymlinkCount() { + return this.symlinkCount; + } + getListings() { + return this.listings; + } + stat(entry) { + const stat = this.libzip.struct.statS(); + const rc = this.libzip.statIndex(this.zip, entry, 0, 0, stat); + if (rc === -1) + throw this.makeLibzipError(this.libzip.getError(this.zip)); + const size = this.libzip.struct.statSize(stat) >>> 0; + const mtime = this.libzip.struct.statMtime(stat) >>> 0; + const crc = this.libzip.struct.statCrc(stat) >>> 0; + return { size, mtime, crc }; + } + makeLibzipError(error) { + const errorCode = this.libzip.struct.errorCodeZip(error); + const strerror = this.libzip.error.strerror(error); + const libzipError = new LibzipError(strerror, this.libzip.errors[errorCode]); + if (errorCode === this.libzip.errors.ZIP_ER_CHANGED) + throw new Error(`Assertion failed: Unexpected libzip error: ${libzipError.message}`); + return libzipError; + } + setFileSource(target, compression, buffer) { + const lzSource = this.allocateSource(buffer); + try { + const newIndex = this.libzip.file.add(this.zip, target, lzSource, this.libzip.ZIP_FL_OVERWRITE); + if (newIndex === -1) + throw this.makeLibzipError(this.libzip.getError(this.zip)); + if (compression !== null) { + const rc = this.libzip.file.setCompression(this.zip, newIndex, 0, compression[0], compression[1]); + if (rc === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + return newIndex; + } catch (error) { + this.libzip.source.free(lzSource); + throw error; + } + } + setMtime(entry, mtime) { + const rc = this.libzip.file.setMtime(this.zip, entry, 0, mtime, 0); + if (rc === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + getExternalAttributes(index) { + const attrs = this.libzip.file.getExternalAttributes(this.zip, index, 0, 0, this.libzip.uint08S, this.libzip.uint32S); + if (attrs === -1) + throw this.makeLibzipError(this.libzip.getError(this.zip)); + const opsys = this.libzip.getValue(this.libzip.uint08S, `i8`) >>> 0; + const attributes = this.libzip.getValue(this.libzip.uint32S, `i32`) >>> 0; + return [opsys, attributes]; + } + setExternalAttributes(index, opsys, attributes) { + const rc = this.libzip.file.setExternalAttributes(this.zip, index, 0, 0, opsys, attributes); + if (rc === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + locate(name) { + return this.libzip.name.locate(this.zip, name, 0); + } + getFileSource(index) { + const stat = this.libzip.struct.statS(); + const rc = this.libzip.statIndex(this.zip, index, 0, 0, stat); + if (rc === -1) + throw this.makeLibzipError(this.libzip.getError(this.zip)); + const size = this.libzip.struct.statCompSize(stat); + const compressionMethod = this.libzip.struct.statCompMethod(stat); + const buffer = this.libzip.malloc(size); + try { + const file = this.libzip.fopenIndex(this.zip, index, 0, this.libzip.ZIP_FL_COMPRESSED); + if (file === 0) + throw this.makeLibzipError(this.libzip.getError(this.zip)); + try { + const rc2 = this.libzip.fread(file, buffer, size, 0); + if (rc2 === -1) + throw this.makeLibzipError(this.libzip.file.getError(file)); + else if (rc2 < size) + throw new Error(`Incomplete read`); + else if (rc2 > size) + throw new Error(`Overread`); + const memory = this.libzip.HEAPU8.subarray(buffer, buffer + size); + const data = Buffer.from(memory); + return { data, compressionMethod }; + } finally { + this.libzip.fclose(file); + } + } finally { + this.libzip.free(buffer); + } + } + deleteEntry(index) { + const rc = this.libzip.delete(this.zip, index); + if (rc === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + addDirectory(path) { + const index = this.libzip.dir.add(this.zip, path); + if (index === -1) + throw this.makeLibzipError(this.libzip.getError(this.zip)); + return index; + } + getBufferAndClose() { + try { + this.libzip.source.keep(this.lzSource); + if (this.libzip.close(this.zip) === -1) + throw this.makeLibzipError(this.libzip.getError(this.zip)); + if (this.libzip.source.open(this.lzSource) === -1) + throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); + if (this.libzip.source.seek(this.lzSource, 0, 0, this.libzip.SEEK_END) === -1) + throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); + const size = this.libzip.source.tell(this.lzSource); + if (size === -1) + throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); + if (this.libzip.source.seek(this.lzSource, 0, 0, this.libzip.SEEK_SET) === -1) + throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); + const buffer = this.libzip.malloc(size); + if (!buffer) + throw new Error(`Couldn't allocate enough memory`); + try { + const rc = this.libzip.source.read(this.lzSource, buffer, size); + if (rc === -1) + throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); + else if (rc < size) + throw new Error(`Incomplete read`); + else if (rc > size) + throw new Error(`Overread`); + let result = Buffer.from(this.libzip.HEAPU8.subarray(buffer, buffer + size)); + if (process.env.YARN_IS_TEST_ENV && process.env.YARN_ZIP_DATA_EPILOGUE) + result = Buffer.concat([result, Buffer.from(process.env.YARN_ZIP_DATA_EPILOGUE)]); + return result; + } finally { + this.libzip.free(buffer); + } + } finally { + this.libzip.source.close(this.lzSource); + this.libzip.source.free(this.lzSource); + } + } + allocateBuffer(content) { + if (!Buffer.isBuffer(content)) + content = Buffer.from(content); + const buffer = this.libzip.malloc(content.byteLength); + if (!buffer) + throw new Error(`Couldn't allocate enough memory`); + const heap = new Uint8Array(this.libzip.HEAPU8.buffer, buffer, content.byteLength); + heap.set(content); + return { buffer, byteLength: content.byteLength }; + } + allocateUnattachedSource(content) { + const error = this.libzip.struct.errorS(); + const { buffer, byteLength } = this.allocateBuffer(content); + const source = this.libzip.source.fromUnattachedBuffer(buffer, byteLength, 0, 1, error); + if (source === 0) { + this.libzip.free(error); + throw this.makeLibzipError(error); + } + return source; + } + allocateSource(content) { + const { buffer, byteLength } = this.allocateBuffer(content); + const source = this.libzip.source.fromBuffer(this.zip, buffer, byteLength, 0, 1); + if (source === 0) { + this.libzip.free(buffer); + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + return source; + } + discard() { + this.libzip.discard(this.zip); + } +} + +const ZIP_UNIX = 3; +const STORE = 0; +const DEFLATE = 8; const DEFAULT_COMPRESSION_LEVEL = `mixed`; function toUnixTimestamp(time) { if (typeof time === `string` && String(+time) === time) @@ -9177,22 +9410,12 @@ function makeEmptyArchive() { 0 ]); } -class LibzipError extends Error { - code; - constructor(message, code) { - super(message); - this.name = `Libzip Error`; - this.code = code; - } -} class ZipFS extends BasePortableFakeFS { - libzip; baseFs; path; stats; - zip; - lzSource; level; + zipImpl; listings = /* @__PURE__ */ new Map(); entries = /* @__PURE__ */ new Map(); /** @@ -9208,9 +9431,11 @@ class ZipFS extends BasePortableFakeFS { readOnly = false; constructor(source, opts = {}) { super(); + if (opts.readOnly) + this.readOnly = true; const pathOptions = opts; this.level = typeof pathOptions.level !== `undefined` ? pathOptions.level : DEFAULT_COMPRESSION_LEVEL; - source ??= makeEmptyArchive(); + const ZipImplCls = opts.customZipImplementation ?? LibZipImpl; if (typeof source === `string`) { const { baseFs = new NodeFS() } = pathOptions; this.baseFs = baseFs; @@ -9236,36 +9461,19 @@ class ZipFS extends BasePortableFakeFS { this.stats = makeDefaultStats(); } } - this.libzip = getInstance(); - const errPtr = this.libzip.malloc(4); - try { - let flags = 0; - if (opts.readOnly) { - flags |= this.libzip.ZIP_RDONLY; - this.readOnly = true; + if (typeof source === `string`) { + if (opts.create) { + this.zipImpl = new ZipImplCls({ buffer: makeEmptyArchive(), readOnly: this.readOnly }); + } else { + this.zipImpl = new ZipImplCls({ path: source, baseFs: this.baseFs, readOnly: this.readOnly, size: this.stats.size }); } - if (typeof source === `string`) - source = pathOptions.create ? makeEmptyArchive() : this.baseFs.readFileSync(source); - const lzSource = this.allocateUnattachedSource(source); - try { - this.zip = this.libzip.openFromSource(lzSource, flags, errPtr); - this.lzSource = lzSource; - } catch (error) { - this.libzip.source.free(lzSource); - throw error; - } - if (this.zip === 0) { - const error = this.libzip.struct.errorS(); - this.libzip.error.initWithCode(error, this.libzip.getValue(errPtr, `i32`)); - throw this.makeLibzipError(error); - } - } finally { - this.libzip.free(errPtr); + } else { + this.zipImpl = new ZipImplCls({ buffer: source ?? makeEmptyArchive(), readOnly: this.readOnly }); } this.listings.set(PortablePath.root, /* @__PURE__ */ new Set()); - const entryCount = this.libzip.getNumEntries(this.zip, 0); - for (let t = 0; t < entryCount; ++t) { - const raw = this.libzip.getName(this.zip, t, 0); + const listings = this.zipImpl.getListings(); + for (let t = 0; t < listings.length; t++) { + const raw = listings[t]; if (ppath.isAbsolute(raw)) continue; const p = ppath.resolve(PortablePath.root, raw); @@ -9274,19 +9482,9 @@ class ZipFS extends BasePortableFakeFS { this.registerListing(p); } } - this.symlinkCount = this.libzip.ext.countSymlinks(this.zip); - if (this.symlinkCount === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); + this.symlinkCount = this.zipImpl.getSymlinkCount(); this.ready = true; } - makeLibzipError(error) { - const errorCode = this.libzip.struct.errorCodeZip(error); - const strerror = this.libzip.error.strerror(error); - const libzipError = new LibzipError(strerror, this.libzip.errors[errorCode]); - if (errorCode === this.libzip.errors.ZIP_ER_CHANGED) - throw new Error(`Assertion failed: Unexpected libzip error: ${libzipError.message}`); - return libzipError; - } getExtractHint(hints) { for (const fileName of this.entries.keys()) { const ext = this.pathUtils.extname(fileName); @@ -9316,45 +9514,14 @@ class ZipFS extends BasePortableFakeFS { return makeEmptyArchive(); } try { - this.libzip.source.keep(this.lzSource); - if (this.libzip.close(this.zip) === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - if (this.libzip.source.open(this.lzSource) === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - if (this.libzip.source.seek(this.lzSource, 0, 0, this.libzip.SEEK_END) === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - const size = this.libzip.source.tell(this.lzSource); - if (size === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - if (this.libzip.source.seek(this.lzSource, 0, 0, this.libzip.SEEK_SET) === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - const buffer = this.libzip.malloc(size); - if (!buffer) - throw new Error(`Couldn't allocate enough memory`); - try { - const rc = this.libzip.source.read(this.lzSource, buffer, size); - if (rc === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - else if (rc < size) - throw new Error(`Incomplete read`); - else if (rc > size) - throw new Error(`Overread`); - let result = Buffer.from(this.libzip.HEAPU8.subarray(buffer, buffer + size)); - if (process.env.YARN_IS_TEST_ENV && process.env.YARN_ZIP_DATA_EPILOGUE) - result = Buffer.concat([result, Buffer.from(process.env.YARN_ZIP_DATA_EPILOGUE)]); - return result; - } finally { - this.libzip.free(buffer); - } + return this.zipImpl.getBufferAndClose(); } finally { - this.libzip.source.close(this.lzSource); - this.libzip.source.free(this.lzSource); this.ready = false; } } discardAndClose() { this.prepareClose(); - this.libzip.discard(this.zip); + this.zipImpl.discard(); this.ready = false; } saveAndClose() { @@ -9608,16 +9775,14 @@ class ZipFS extends BasePortableFakeFS { statImpl(reason, p, opts = {}) { const entry = this.entries.get(p); if (typeof entry !== `undefined`) { - const stat = this.libzip.struct.statS(); - const rc = this.libzip.statIndex(this.zip, entry, 0, 0, stat); - if (rc === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); + const stat = this.zipImpl.stat(entry); + const crc = stat.crc; + const size = stat.size; + const mtimeMs = stat.mtime * 1e3; const uid = this.stats.uid; const gid = this.stats.gid; - const size = this.libzip.struct.statSize(stat) >>> 0; const blksize = 512; - const blocks = Math.ceil(size / blksize); - const mtimeMs = (this.libzip.struct.statMtime(stat) >>> 0) * 1e3; + const blocks = Math.ceil(stat.size / blksize); const atimeMs = mtimeMs; const birthtimeMs = mtimeMs; const ctimeMs = mtimeMs; @@ -9628,7 +9793,6 @@ class ZipFS extends BasePortableFakeFS { const type = this.listings.has(p) ? fs.constants.S_IFDIR : this.isSymbolicLink(entry) ? fs.constants.S_IFLNK : fs.constants.S_IFREG; const defaultMode = type === fs.constants.S_IFDIR ? 493 : 420; const mode = type | this.getUnixMode(entry, defaultMode) & 511; - const crc = this.libzip.struct.statCrc(stat); const statInstance = Object.assign(new StatEntry(), { uid, gid, size, blksize, blocks, atime, birthtime, ctime, mtime, atimeMs, birthtimeMs, ctimeMs, mtimeMs, mode, crc }); return opts.bigint === true ? convertToBigIntStats(statInstance) : statInstance; } @@ -9654,13 +9818,10 @@ class ZipFS extends BasePortableFakeFS { throw new Error(`Unreachable`); } getUnixMode(index, defaultMode) { - const rc = this.libzip.file.getExternalAttributes(this.zip, index, 0, 0, this.libzip.uint08S, this.libzip.uint32S); - if (rc === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - const opsys = this.libzip.getValue(this.libzip.uint08S, `i8`) >>> 0; - if (opsys !== this.libzip.ZIP_OPSYS_UNIX) + const [opsys, attributes] = this.zipImpl.getExternalAttributes(index); + if (opsys !== ZIP_UNIX) return defaultMode; - return this.libzip.getValue(this.libzip.uint32S, `i32`) >>> 16; + return attributes >>> 16; } registerListing(p) { const existingListing = this.listings.get(p); @@ -9695,10 +9856,7 @@ class ZipFS extends BasePortableFakeFS { } deleteEntry(p, index) { this.unregisterEntry(p); - const rc = this.libzip.delete(this.zip, index); - if (rc === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } + this.zipImpl.deleteEntry(index); } resolveFilename(reason, p, resolveLastComponent = true, throwIfNoEntry = true) { if (!this.ready) @@ -9731,7 +9889,7 @@ class ZipFS extends BasePortableFakeFS { resolvedP = ppath.resolve(parentP, ppath.basename(resolvedP)); if (!resolveLastComponent || this.symlinkCount === 0) break; - const index = this.libzip.name.locate(this.zip, resolvedP.slice(1), 0); + const index = this.zipImpl.locate(resolvedP.slice(1)); if (index === -1) break; if (this.isSymbolicLink(index)) { @@ -9743,118 +9901,57 @@ class ZipFS extends BasePortableFakeFS { } return resolvedP; } - allocateBuffer(content) { - if (!Buffer.isBuffer(content)) - content = Buffer.from(content); - const buffer = this.libzip.malloc(content.byteLength); - if (!buffer) - throw new Error(`Couldn't allocate enough memory`); - const heap = new Uint8Array(this.libzip.HEAPU8.buffer, buffer, content.byteLength); - heap.set(content); - return { buffer, byteLength: content.byteLength }; - } - allocateUnattachedSource(content) { - const error = this.libzip.struct.errorS(); - const { buffer, byteLength } = this.allocateBuffer(content); - const source = this.libzip.source.fromUnattachedBuffer(buffer, byteLength, 0, 1, error); - if (source === 0) { - this.libzip.free(error); - throw this.makeLibzipError(error); - } - return source; - } - allocateSource(content) { - const { buffer, byteLength } = this.allocateBuffer(content); - const source = this.libzip.source.fromBuffer(this.zip, buffer, byteLength, 0, 1); - if (source === 0) { - this.libzip.free(buffer); - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } - return source; - } setFileSource(p, content) { const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content); const target = ppath.relative(PortablePath.root, p); - const lzSource = this.allocateSource(content); - try { - const newIndex = this.libzip.file.add(this.zip, target, lzSource, this.libzip.ZIP_FL_OVERWRITE); - if (newIndex === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - if (this.level !== `mixed`) { - const method = this.level === 0 ? this.libzip.ZIP_CM_STORE : this.libzip.ZIP_CM_DEFLATE; - const rc = this.libzip.file.setCompression(this.zip, newIndex, 0, method, this.level); - if (rc === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } - } - this.fileSources.set(newIndex, buffer); - return newIndex; - } catch (error) { - this.libzip.source.free(lzSource); - throw error; + let compression = null; + if (this.level !== `mixed`) { + const method = this.level === 0 ? STORE : DEFLATE; + compression = [method, this.level]; } + const newIndex = this.zipImpl.setFileSource(target, compression, buffer); + this.fileSources.set(newIndex, buffer); + return newIndex; } isSymbolicLink(index) { if (this.symlinkCount === 0) return false; - const attrs = this.libzip.file.getExternalAttributes(this.zip, index, 0, 0, this.libzip.uint08S, this.libzip.uint32S); - if (attrs === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - const opsys = this.libzip.getValue(this.libzip.uint08S, `i8`) >>> 0; - if (opsys !== this.libzip.ZIP_OPSYS_UNIX) + const [opsys, attrs] = this.zipImpl.getExternalAttributes(index); + if (opsys !== ZIP_UNIX) return false; - const attributes = this.libzip.getValue(this.libzip.uint32S, `i32`) >>> 16; + const attributes = attrs >>> 16; return (attributes & fs.constants.S_IFMT) === fs.constants.S_IFLNK; } getFileSource(index, opts = { asyncDecompress: false }) { const cachedFileSource = this.fileSources.get(index); if (typeof cachedFileSource !== `undefined`) return cachedFileSource; - const stat = this.libzip.struct.statS(); - const rc = this.libzip.statIndex(this.zip, index, 0, 0, stat); - if (rc === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - const size = this.libzip.struct.statCompSize(stat); - const compressionMethod = this.libzip.struct.statCompMethod(stat); - const buffer = this.libzip.malloc(size); - try { - const file = this.libzip.fopenIndex(this.zip, index, 0, this.libzip.ZIP_FL_COMPRESSED); - if (file === 0) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - try { - const rc2 = this.libzip.fread(file, buffer, size, 0); - if (rc2 === -1) - throw this.makeLibzipError(this.libzip.file.getError(file)); - else if (rc2 < size) - throw new Error(`Incomplete read`); - else if (rc2 > size) - throw new Error(`Overread`); - const memory = this.libzip.HEAPU8.subarray(buffer, buffer + size); - const data = Buffer.from(memory); - if (compressionMethod === 0) { - this.fileSources.set(index, data); - return data; - } else if (opts.asyncDecompress) { - return new Promise((resolve, reject) => { - zlib__default.default.inflateRaw(data, (error, result) => { - if (error) { - reject(error); - } else { + const { data, compressionMethod } = this.zipImpl.getFileSource(index); + if (compressionMethod === STORE) { + if (this.zipImpl.filesShouldBeCached) + this.fileSources.set(index, data); + return data; + } else if (compressionMethod === DEFLATE) { + if (opts.asyncDecompress) { + return new Promise((resolve, reject) => { + zlib__default.default.inflateRaw(data, (error, result) => { + if (error) { + reject(error); + } else { + if (this.zipImpl.filesShouldBeCached) this.fileSources.set(index, result); - resolve(result); - } - }); + resolve(result); + } }); - } else { - const decompressedData = zlib__default.default.inflateRawSync(data); + }); + } else { + const decompressedData = zlib__default.default.inflateRawSync(data); + if (this.zipImpl.filesShouldBeCached) this.fileSources.set(index, decompressedData); - return decompressedData; - } - } finally { - this.libzip.fclose(file); + return decompressedData; } - } finally { - this.libzip.free(buffer); + } else { + throw new Error(`Unsupported compression method: ${compressionMethod}`); } } async fchmodPromise(fd, mask) { @@ -9876,10 +9973,7 @@ class ZipFS extends BasePortableFakeFS { throw new Error(`Assertion failed: The entry should have been registered (${resolvedP})`); const oldMod = this.getUnixMode(entry, fs.constants.S_IFREG | 0); const newMod = oldMod & ~511 | mask; - const rc = this.libzip.file.setExternalAttributes(this.zip, entry, 0, 0, this.libzip.ZIP_OPSYS_UNIX, newMod << 16); - if (rc === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } + this.zipImpl.setExternalAttributes(entry, ZIP_UNIX, newMod << 16); } async fchownPromise(fd, uid, gid) { return this.chownPromise(this.fdToPath(fd, `fchown`), uid, gid); @@ -10053,10 +10147,7 @@ class ZipFS extends BasePortableFakeFS { const entry = this.entries.get(resolvedP); if (entry === void 0) throw new Error(`Unreachable`); - const rc = this.libzip.file.setMtime(this.zip, entry, 0, toUnixTimestamp(mtime), 0); - if (rc === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } + this.zipImpl.setMtime(entry, toUnixTimestamp(mtime)); } async mkdirPromise(p, opts) { return this.mkdirSync(p, opts); @@ -10116,9 +10207,7 @@ class ZipFS extends BasePortableFakeFS { this.deleteEntry(p, index); } hydrateDirectory(resolvedP) { - const index = this.libzip.dir.add(this.zip, ppath.relative(PortablePath.root, resolvedP)); - if (index === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); + const index = this.zipImpl.addDirectory(ppath.relative(PortablePath.root, resolvedP)); this.registerListing(resolvedP); this.registerEntry(resolvedP, index); return index; @@ -10142,9 +10231,7 @@ class ZipFS extends BasePortableFakeFS { throw EEXIST(`symlink '${target}' -> '${p}'`); const index = this.setFileSource(resolvedP, target); this.registerEntry(resolvedP, index); - const rc = this.libzip.file.setExternalAttributes(this.zip, index, 0, 0, this.libzip.ZIP_OPSYS_UNIX, (fs.constants.S_IFLNK | 511) << 16); - if (rc === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); + this.zipImpl.setExternalAttributes(index, ZIP_UNIX, (fs.constants.S_IFLNK | 511) << 16); this.symlinkCount += 1; } async readFilePromise(p, encoding) { @@ -10299,10 +10386,13 @@ class ZipFS extends BasePortableFakeFS { } }; const interval = setInterval(() => { }, 24 * 60 * 60 * 1e3); - return { on: () => { - }, close: () => { - clearInterval(interval); - } }; + return { + on: () => { + }, + close: () => { + clearInterval(interval); + } + }; } watchFile(p, a, b) { const resolvedP = ppath.resolve(PortablePath.root, p); @@ -10314,6 +10404,201 @@ class ZipFS extends BasePortableFakeFS { } } +const SIGNATURE = { + CENTRAL_DIRECTORY: 33639248, + END_OF_CENTRAL_DIRECTORY: 101010256 +}; +const noCommentCDSize = 22; +class JsZipImpl { + fd; + baseFs; + entries; + filesShouldBeCached = false; + constructor(opts) { + if (`buffer` in opts) + throw new Error(`Buffer based zip archives are not supported`); + if (!opts.readOnly) + throw new Error(`Writable zip archives are not supported`); + this.baseFs = opts.baseFs; + this.fd = this.baseFs.openSync(opts.path, `r`); + try { + this.entries = JsZipImpl.readZipSync(this.fd, this.baseFs, opts.size); + } catch (error) { + this.baseFs.closeSync(this.fd); + this.fd = `closed`; + throw error; + } + } + static readZipSync(fd, baseFs, fileSize) { + if (fileSize < noCommentCDSize) + throw new Error(`Invalid ZIP file: EOCD not found`); + let eocdOffset = -1; + let eocdBuffer = Buffer.alloc(noCommentCDSize); + baseFs.readSync( + fd, + eocdBuffer, + 0, + noCommentCDSize, + fileSize - noCommentCDSize + ); + if (eocdBuffer.readUInt32LE(0) === SIGNATURE.END_OF_CENTRAL_DIRECTORY) { + eocdOffset = 0; + } else { + const bufferSize = Math.min(65557, fileSize); + eocdBuffer = Buffer.alloc(bufferSize); + baseFs.readSync( + fd, + eocdBuffer, + 0, + bufferSize, + Math.max(0, fileSize - bufferSize) + ); + for (let i = eocdBuffer.length - 4; i >= 0; i--) { + if (eocdBuffer.readUInt32LE(i) === SIGNATURE.END_OF_CENTRAL_DIRECTORY) { + eocdOffset = i; + break; + } + } + if (eocdOffset === -1) { + throw new Error(`Not a zip archive`); + } + } + const totalEntries = eocdBuffer.readUInt16LE(eocdOffset + 10); + const centralDirSize = eocdBuffer.readUInt32LE(eocdOffset + 12); + const centralDirOffset = eocdBuffer.readUInt32LE(eocdOffset + 16); + const commentLength = eocdBuffer.readUInt16LE(eocdOffset + 20); + if (eocdOffset + commentLength + noCommentCDSize > eocdBuffer.length) + throw new Error(`Zip archive inconsistent`); + if (totalEntries == 65535 || centralDirSize == 4294967295 || centralDirOffset == 4294967295) + throw new Error(`Zip 64 is not supported`); + if (centralDirSize > fileSize) + throw new Error(`Zip archive inconsistent`); + if (totalEntries > centralDirSize / 46) + throw new Error(`Zip archive inconsistent`); + const cdBuffer = Buffer.alloc(centralDirSize); + if (baseFs.readSync(fd, cdBuffer, 0, cdBuffer.length, centralDirOffset) !== cdBuffer.length) + throw new Error(`Zip archive inconsistent`); + const entries = []; + let offset = 0; + let index = 0; + let sumCompressedSize = 0; + while (index < totalEntries) { + if (offset + 46 > cdBuffer.length) + throw new Error(`Zip archive inconsistent`); + if (cdBuffer.readUInt32LE(offset) !== SIGNATURE.CENTRAL_DIRECTORY) + throw new Error(`Zip archive inconsistent`); + const versionMadeBy = cdBuffer.readUInt16LE(offset + 4); + const os = versionMadeBy >>> 8; + const flags = cdBuffer.readUInt16LE(offset + 8); + if ((flags & 1) !== 0) + throw new Error(`Encrypted zip files are not supported`); + const compressionMethod = cdBuffer.readUInt16LE(offset + 10); + const crc = cdBuffer.readUInt32LE(offset + 16); + const nameLength = cdBuffer.readUInt16LE(offset + 28); + const extraLength = cdBuffer.readUInt16LE(offset + 30); + const commentLength2 = cdBuffer.readUInt16LE(offset + 32); + const localHeaderOffset = cdBuffer.readUInt32LE(offset + 42); + const name = cdBuffer.toString(`utf8`, offset + 46, offset + 46 + nameLength).replaceAll(`\0`, ` `); + if (name.includes(`\0`)) + throw new Error(`Invalid ZIP file`); + const compressedSize = cdBuffer.readUInt32LE(offset + 20); + const externalAttributes = cdBuffer.readUInt32LE(offset + 38); + entries.push({ + name, + os, + mtime: SAFE_TIME, + //we dont care, + crc, + compressionMethod, + isSymbolicLink: os === ZIP_UNIX && (externalAttributes >>> 16 & S_IFMT) === S_IFLNK, + size: cdBuffer.readUInt32LE(offset + 24), + compressedSize, + externalAttributes, + localHeaderOffset + }); + sumCompressedSize += compressedSize; + index += 1; + offset += 46 + nameLength + extraLength + commentLength2; + } + if (sumCompressedSize > fileSize) + throw new Error(`Zip archive inconsistent`); + if (offset !== cdBuffer.length) + throw new Error(`Zip archive inconsistent`); + return entries; + } + getExternalAttributes(index) { + const entry = this.entries[index]; + return [entry.os, entry.externalAttributes]; + } + getListings() { + return this.entries.map((e) => e.name); + } + getSymlinkCount() { + let count = 0; + for (const entry of this.entries) + if (entry.isSymbolicLink) + count += 1; + return count; + } + stat(index) { + const entry = this.entries[index]; + return { + crc: entry.crc, + mtime: entry.mtime, + size: entry.size + }; + } + locate(name) { + for (let ind = 0; ind < this.entries.length; ind++) + if (this.entries[ind].name === name) + return ind; + return -1; + } + getFileSource(index) { + if (this.fd === `closed`) + throw new Error(`ZIP file is closed`); + const entry = this.entries[index]; + const localHeaderBuf = Buffer.alloc(30); + this.baseFs.readSync( + this.fd, + localHeaderBuf, + 0, + localHeaderBuf.length, + entry.localHeaderOffset + ); + const nameLength = localHeaderBuf.readUInt16LE(26); + const extraLength = localHeaderBuf.readUInt16LE(28); + const buffer = Buffer.alloc(entry.compressedSize); + if (this.baseFs.readSync(this.fd, buffer, 0, entry.compressedSize, entry.localHeaderOffset + 30 + nameLength + extraLength) !== entry.compressedSize) + throw new Error(`Invalid ZIP file`); + return { data: buffer, compressionMethod: entry.compressionMethod }; + } + discard() { + if (this.fd !== `closed`) { + this.baseFs.closeSync(this.fd); + this.fd = `closed`; + } + } + addDirectory(path) { + throw new Error(`Not implemented`); + } + deleteEntry(index) { + throw new Error(`Not implemented`); + } + setMtime(index, mtime) { + throw new Error(`Not implemented`); + } + getBufferAndClose() { + throw new Error(`Not implemented`); + } + setFileSource(target, compression, buffer) { + throw new Error(`Not implemented`); + } + setExternalAttributes(index, opsys, attributes) { + throw new Error(`Not implemented`); + } +} + setFactory(() => { const emZip = createModule(); return makeInterface(emZip); @@ -10664,6 +10949,7 @@ function hydrateRuntimeState(data, { basePath }) { dependencyTreeRoots, enableTopLevelFallback, fallbackExclusionList, + pnpZipBackend: data.pnpZipBackend, fallbackPool, ignorePattern, packageLocatorsByLocations, @@ -12320,8 +12606,10 @@ const localFs = { ...fs__default.default }; const nodeFs = new NodeFS(localFs); const defaultRuntimeState = $$SETUP_STATE(hydrateRuntimeState); const defaultPnpapiResolution = __filename; +const customZipImplementation = defaultRuntimeState.pnpZipBackend === `js` ? JsZipImpl : void 0; const defaultFsLayer = new VirtualFS({ baseFs: new ZipOpenFS({ + customZipImplementation, baseFs: nodeFs, maxOpenFiles: 80, readOnlyArchives: true diff --git a/yarn.lock b/yarn.lock index 2440d9d..55a1566 100644 --- a/yarn.lock +++ b/yarn.lock @@ -904,11 +904,11 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:^22.7.5": - version: 22.14.0 - resolution: "@types/node@npm:22.14.0" + version: 22.15.3 + resolution: "@types/node@npm:22.15.3" dependencies: undici-types: "npm:~6.21.0" - checksum: 10c0/9d79f3fa1af9c2c869514f419c4a4905b34c10e12915582fd1784868ac4e74c6d306cf5eb47ef889b6750ab85a31be96618227b86739c4a3e0b1c15063f384c6 + checksum: 10c0/2879f012d1aeba0bfdb5fed80d165f4f2cb3d1f2e1f98a24b18d4a211b4ace7d64bf2622784c78355982ffc1081ba79d0934efc2fb8353913e5871a63609661f languageName: node linkType: hard