From 726613bf1dcdb6c3700bfcdb4fb8e63694d98816 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Wed, 15 Nov 2023 19:32:17 -0500 Subject: [PATCH] fix: Remove `unzipper` as it cannot handle zip within zip (#6162) --- package.json | 4 +- server/models/FileOperation.ts | 7 + server/queues/tasks/ImportJSONTask.ts | 15 +- .../tasks/ImportMarkdownZipTask.test.ts | 50 +++++-- server/queues/tasks/ImportNotionTask.test.ts | 24 ++-- server/queues/tasks/ImportTask.ts | 57 +++++--- server/storage/files/BaseStorage.ts | 11 ++ server/storage/files/LocalStorage.ts | 9 ++ server/storage/files/S3Storage.ts | 34 +++++ server/utils/ZipHelper.ts | 75 ++++++++++ yarn.lock | 131 +++++------------- 11 files changed, 272 insertions(+), 145 deletions(-) diff --git a/package.json b/package.json index a410396ef..c918f9bb2 100644 --- a/package.json +++ b/package.json @@ -222,7 +222,6 @@ "tmp": "^0.2.1", "turndown": "^7.1.2", "umzug": "^3.2.1", - "unzipper": "0.10.11", "utf8": "^3.0.0", "utility-types": "^3.10.0", "uuid": "^8.3.2", @@ -233,6 +232,7 @@ "ws": "^7.5.9", "y-indexeddb": "^9.0.11", "y-protocols": "^1.0.5", + "yauzl": "^2.10.0", "yjs": "^13.6.1", "zod": "^3.22.4" }, @@ -302,9 +302,9 @@ "@types/throng": "^5.0.4", "@types/tmp": "^0.2.3", "@types/turndown": "^5.0.1", - "@types/unzipper": "^0.10.9", "@types/utf8": "^3.0.1", "@types/validator": "^13.7.17", + "@types/yauzl": "^2.10.3", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "babel-eslint": "^10.1.0", diff --git a/server/models/FileOperation.ts b/server/models/FileOperation.ts index 257159084..8d945efc8 100644 --- a/server/models/FileOperation.ts +++ b/server/models/FileOperation.ts @@ -83,6 +83,13 @@ class FileOperation extends ParanoidModel { return FileStorage.getFileStream(this.key); } + /** + * The file operation contents as a handle which contains a path and cleanup function. + */ + get handle() { + return FileStorage.getFileHandle(this.key); + } + // hooks @BeforeDestroy diff --git a/server/queues/tasks/ImportJSONTask.ts b/server/queues/tasks/ImportJSONTask.ts index 0cecb1b5a..08dbf6f29 100644 --- a/server/queues/tasks/ImportJSONTask.ts +++ b/server/queues/tasks/ImportJSONTask.ts @@ -53,7 +53,11 @@ export default class ImportJSONTask extends ImportTask { rootPath = path.dirname(node.path); } if (node.path === "metadata.json") { - metadata = JSON.parse(await fs.readFile(node.path, "utf8")); + try { + metadata = JSON.parse(await fs.readFile(node.path, "utf8")); + } catch (err) { + throw new Error(`Could not parse metadata.json. ${err.message}`); + } } } @@ -116,9 +120,12 @@ export default class ImportJSONTask extends ImportTask { continue; } - const item: CollectionJSONExport = JSON.parse( - await fs.readFile(node.path, "utf8") - ); + let item: CollectionJSONExport; + try { + item = JSON.parse(await fs.readFile(node.path, "utf8")); + } catch (err) { + throw new Error(`Could not parse ${node.path}. ${err.message}`); + } const collectionId = uuidv4(); output.collections.push({ diff --git a/server/queues/tasks/ImportMarkdownZipTask.test.ts b/server/queues/tasks/ImportMarkdownZipTask.test.ts index 1b9e6727f..789b42e03 100644 --- a/server/queues/tasks/ImportMarkdownZipTask.test.ts +++ b/server/queues/tasks/ImportMarkdownZipTask.test.ts @@ -1,4 +1,4 @@ -import fs from "fs"; +/* eslint-disable @typescript-eslint/no-empty-function */ import path from "path"; import { FileOperation } from "@server/models"; import { buildFileOperation } from "@server/test/factories"; @@ -7,11 +7,19 @@ import ImportMarkdownZipTask from "./ImportMarkdownZipTask"; describe("ImportMarkdownZipTask", () => { it("should import the documents, attachments", async () => { const fileOperation = await buildFileOperation(); - Object.defineProperty(fileOperation, "stream", { + Object.defineProperty(fileOperation, "handle", { get() { - return fs.createReadStream( - path.resolve(__dirname, "..", "..", "test", "fixtures", "outline.zip") - ); + return { + path: path.resolve( + __dirname, + "..", + "..", + "test", + "fixtures", + "outline.zip" + ), + cleanup: async () => {}, + }; }, }); jest.spyOn(FileOperation, "findByPk").mockResolvedValue(fileOperation); @@ -30,11 +38,19 @@ describe("ImportMarkdownZipTask", () => { it("should throw an error with corrupt zip", async () => { const fileOperation = await buildFileOperation(); - Object.defineProperty(fileOperation, "stream", { + Object.defineProperty(fileOperation, "handle", { get() { - return fs.createReadStream( - path.resolve(__dirname, "..", "..", "test", "fixtures", "corrupt.zip") - ); + return { + path: path.resolve( + __dirname, + "..", + "..", + "test", + "fixtures", + "corrupt.zip" + ), + cleanup: async () => {}, + }; }, }); jest.spyOn(FileOperation, "findByPk").mockResolvedValue(fileOperation); @@ -56,11 +72,19 @@ describe("ImportMarkdownZipTask", () => { it("should throw an error with empty collection in zip", async () => { const fileOperation = await buildFileOperation(); - Object.defineProperty(fileOperation, "stream", { + Object.defineProperty(fileOperation, "handle", { get() { - return fs.createReadStream( - path.resolve(__dirname, "..", "..", "test", "fixtures", "empty.zip") - ); + return { + path: path.resolve( + __dirname, + "..", + "..", + "test", + "fixtures", + "empty.zip" + ), + cleanup: async () => {}, + }; }, }); jest.spyOn(FileOperation, "findByPk").mockResolvedValue(fileOperation); diff --git a/server/queues/tasks/ImportNotionTask.test.ts b/server/queues/tasks/ImportNotionTask.test.ts index 622c37d1f..01181bfa9 100644 --- a/server/queues/tasks/ImportNotionTask.test.ts +++ b/server/queues/tasks/ImportNotionTask.test.ts @@ -1,4 +1,4 @@ -import fs from "fs"; +/* eslint-disable @typescript-eslint/no-empty-function */ import path from "path"; import { FileOperation } from "@server/models"; import { buildFileOperation } from "@server/test/factories"; @@ -7,18 +7,19 @@ import ImportNotionTask from "./ImportNotionTask"; describe("ImportNotionTask", () => { it("should import successfully from a Markdown export", async () => { const fileOperation = await buildFileOperation(); - Object.defineProperty(fileOperation, "stream", { + Object.defineProperty(fileOperation, "handle", { get() { - return fs.createReadStream( - path.resolve( + return { + path: path.resolve( __dirname, "..", "..", "test", "fixtures", "notion-markdown.zip" - ) - ); + ), + cleanup: async () => {}, + }; }, }); jest.spyOn(FileOperation, "findByPk").mockResolvedValue(fileOperation); @@ -44,18 +45,19 @@ describe("ImportNotionTask", () => { it("should import successfully from a HTML export", async () => { const fileOperation = await buildFileOperation(); - Object.defineProperty(fileOperation, "stream", { + Object.defineProperty(fileOperation, "handle", { get() { - return fs.createReadStream( - path.resolve( + return { + path: path.resolve( __dirname, "..", "..", "test", "fixtures", "notion-html.zip" - ) - ); + ), + cleanup: async () => {}, + }; }, }); jest.spyOn(FileOperation, "findByPk").mockResolvedValue(fileOperation); diff --git a/server/queues/tasks/ImportTask.ts b/server/queues/tasks/ImportTask.ts index cadd28f40..3f31757a5 100644 --- a/server/queues/tasks/ImportTask.ts +++ b/server/queues/tasks/ImportTask.ts @@ -2,7 +2,6 @@ import path from "path"; import { rm } from "fs-extra"; import truncate from "lodash/truncate"; import tmp from "tmp"; -import unzipper from "unzipper"; import { AttachmentPreset, CollectionPermission, @@ -13,7 +12,6 @@ import { CollectionValidation } from "@shared/validations"; import attachmentCreator from "@server/commands/attachmentCreator"; import documentCreator from "@server/commands/documentCreator"; import { serializer } from "@server/editor"; -import env from "@server/env"; import { InternalError, ValidationError } from "@server/errors"; import Logger from "@server/logging/Logger"; import { @@ -25,6 +23,7 @@ import { Attachment, } from "@server/models"; import { sequelize } from "@server/storage/database"; +import ZipHelper from "@server/utils/ZipHelper"; import BaseTask, { TaskPriority } from "./BaseTask"; type Props = { @@ -198,30 +197,44 @@ export default abstract class ImportTask extends BaseTask { protected async fetchAndExtractData( fileOperation: FileOperation ): Promise { - return new Promise((resolve, reject) => { - const stream = fileOperation.stream; - if (!stream) { - return reject(new Error("No stream available")); - } + let cleanup; + let filePath: string; - tmp.dir((err, path) => { - if (err) { - return reject(err); - } + try { + const res = await fileOperation.handle; + filePath = res.path; + cleanup = res.cleanup; - const dest = unzipper - .Extract({ path, verbose: env.isDevelopment }) - .on("error", reject) - .on("close", () => resolve(path)); + const path = await new Promise((resolve, reject) => { + tmp.dir((err, tmpDir) => { + if (err) { + Logger.error("Could not create temporary directory", err); + return reject(err); + } - stream - .on("error", (err) => { - dest.end(); - reject(err); - }) - .pipe(dest); + Logger.debug( + "task", + `ImportTask extracting data for ${fileOperation.id}` + ); + + void ZipHelper.extract(filePath, tmpDir) + .then(() => resolve(tmpDir)) + .catch((err) => { + Logger.error("Could not extract zip file", err); + reject(err); + }); + }); }); - }); + + return path; + } finally { + Logger.debug( + "task", + `ImportTask cleaning up temporary data for ${fileOperation.id}` + ); + + await cleanup?.(); + } } /** diff --git a/server/storage/files/BaseStorage.ts b/server/storage/files/BaseStorage.ts index d713d9d9d..e15c6e3ca 100644 --- a/server/storage/files/BaseStorage.ts +++ b/server/storage/files/BaseStorage.ts @@ -84,6 +84,17 @@ export default abstract class BaseStorage { acl?: string; }): Promise; + /** + * Returns a file handle for a file from the storage provider. + * + * @param key The path to the file + * @returns The file path and a cleanup function + */ + public abstract getFileHandle(key: string): Promise<{ + path: string; + cleanup: () => Promise; + }>; + /** * Returns a buffer of a file from the storage provider. * diff --git a/server/storage/files/LocalStorage.ts b/server/storage/files/LocalStorage.ts index 9b9e15959..3e3128d40 100644 --- a/server/storage/files/LocalStorage.ts +++ b/server/storage/files/LocalStorage.ts @@ -128,6 +128,15 @@ export default class LocalStorage extends BaseStorage { return Promise.resolve(`${env.URL}/api/files.get?sig=${sig}`); }; + public async getFileHandle(key: string) { + return { + path: this.getFilePath(key), + cleanup: async () => { + // no-op, as we're reading the canonical file directly + }, + }; + } + public getFileStream(key: string) { return createReadStream(this.getFilePath(key)); } diff --git a/server/storage/files/S3Storage.ts b/server/storage/files/S3Storage.ts index d1ba71386..1e6105b4c 100644 --- a/server/storage/files/S3Storage.ts +++ b/server/storage/files/S3Storage.ts @@ -1,7 +1,10 @@ +import path from "path"; import util from "util"; import AWS, { S3 } from "aws-sdk"; +import { createWriteStream, remove } from "fs-extra"; import invariant from "invariant"; import compact from "lodash/compact"; +import tmp from "tmp"; import env from "@server/env"; import Logger from "@server/logging/Logger"; import BaseStorage from "./BaseStorage"; @@ -159,6 +162,37 @@ export default class S3Storage extends BaseStorage { return url; }; + public getFileHandle(key: string): Promise<{ + path: string; + cleanup: () => Promise; + }> { + return new Promise((resolve, reject) => { + tmp.dir((err, tmpDir) => { + if (err) { + return reject(err); + } + const tmpFile = path.join(tmpDir, "tmp"); + const dest = createWriteStream(tmpFile); + dest.on("error", reject); + dest.on("finish", () => + resolve({ path: tmpFile, cleanup: () => remove(tmpFile) }) + ); + + const stream = this.getFileStream(key); + if (!stream) { + return reject(new Error("No stream available")); + } + + stream + .on("error", (err) => { + dest.end(); + reject(err); + }) + .pipe(dest); + }); + }); + } + public getFileStream(key: string) { invariant( env.AWS_S3_UPLOAD_BUCKET_NAME, diff --git a/server/utils/ZipHelper.ts b/server/utils/ZipHelper.ts index ee2febf74..4f92ad9c3 100644 --- a/server/utils/ZipHelper.ts +++ b/server/utils/ZipHelper.ts @@ -1,6 +1,9 @@ import fs from "fs"; +import path from "path"; +import { mkdirp } from "fs-extra"; import JSZip from "jszip"; import tmp from "tmp"; +import yauzl from "yauzl"; import { bytesToHumanReadable } from "@shared/utils/files"; import Logger from "@server/logging/Logger"; import { trace } from "@server/logging/tracing"; @@ -20,6 +23,7 @@ export default class ZipHelper { /** * Write a zip file to a temporary disk location * + * @deprecated Use `extract` instead * @param zip JSZip object * @returns pathname of the temporary file where the zip was written to disk */ @@ -87,4 +91,75 @@ export default class ZipHelper { ); }); } + + /** + * Write a zip file to a disk location + * + * @param filePath The file path where the zip is located + * @param outputDir The directory where the zip should be extracted + */ + public static extract(filePath: string, outputDir: string): Promise { + return new Promise((resolve, reject) => { + Logger.debug("utils", "Opening zip file", { filePath }); + + yauzl.open( + filePath, + { lazyEntries: true, autoClose: true }, + function (err, zipfile) { + if (err) { + return reject(err); + } + try { + zipfile.readEntry(); + zipfile.on("entry", function (entry) { + Logger.debug("utils", "Extracting zip entry", entry); + if (/\/$/.test(entry.fileName)) { + // directory file names end with '/' + mkdirp( + path.join(outputDir, entry.fileName), + function (err: Error) { + if (err) { + throw err; + } + zipfile.readEntry(); + } + ); + } else { + // file entry + zipfile.openReadStream(entry, function (err, readStream) { + if (err) { + throw err; + } + // ensure parent directory exists + mkdirp( + path.join(outputDir, path.dirname(entry.fileName)), + function (err) { + if (err) { + throw err; + } + readStream.pipe( + fs.createWriteStream( + path.join(outputDir, entry.fileName) + ) + ); + readStream.on("end", function () { + zipfile.readEntry(); + }); + readStream.on("error", (err) => { + throw err; + }); + } + ); + }); + } + }); + zipfile.on("close", resolve); + zipfile.on("error", reject); + } catch (err) { + reject(err); + } + } + ); + }); + } } diff --git a/yarn.lock b/yarn.lock index 46d8d85c4..f8bb78f1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3540,13 +3540,6 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== -"@types/unzipper@^0.10.9": - version "0.10.9" - resolved "https://registry.yarnpkg.com/@types/unzipper/-/unzipper-0.10.9.tgz#ccbc393ecd1ec013dbe9bc6f13332dad0aa00a0f" - integrity sha512-vHbmFZAw8emNAOVkHVbS3qBnbr0x/qHQZ+ei1HE7Oy6Tyrptl+jpqnOX+BF5owcu/HZLOV0nJK+K9sjs1Ox2JA== - dependencies: - "@types/node" "*" - "@types/utf8@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/utf8/-/utf8-3.0.1.tgz#bf081663d4fff05ee63b41f377a35f8b189f7e5b" @@ -3581,6 +3574,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yauzl@^2.10.3": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -4354,30 +4354,17 @@ batch@^0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== -big-integer@^1.6.17: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - binary-extensions@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== -binary@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" - integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg== - dependencies: - buffers "~0.1.1" - chainsaw "~0.1.0" - bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bluebird@~3.4.0, bluebird@~3.4.1: +bluebird@~3.4.0: version "3.4.7" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= @@ -4480,6 +4467,11 @@ btoa@^1.2.1: resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" @@ -4495,11 +4487,6 @@ buffer-from@^1.0.0, buffer-from@^1.1.2: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-indexof-polyfill@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" - integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== - buffer-writer@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" @@ -4514,11 +4501,6 @@ buffer@4.9.2: ieee754 "^1.1.4" isarray "^1.0.0" -buffers@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" - integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== - builtin-modules@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-2.0.0.tgz#60b7ef5ae6546bd7deefa74b08b62a43a232648e" @@ -4597,13 +4579,6 @@ caniuse-lite@^1.0.30001449: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz#2e197c698fc1373d63e1406d6607ea4617c613f1" integrity sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w== -chainsaw@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" - integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ== - dependencies: - traverse ">=0.3.0 <0.4" - chalk@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" @@ -5916,13 +5891,6 @@ duck@^0.1.12: dependencies: underscore "^1.13.1" -duplexer2@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== - dependencies: - readable-stream "^2.0.2" - duplexer@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -6845,6 +6813,13 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + fecha@^4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce" @@ -7137,16 +7112,6 @@ fsevents@^2.3.2, fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -fstream@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - ftp@^0.3.10: version "0.3.10" resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" @@ -7403,7 +7368,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -7834,7 +7799,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -9322,11 +9287,6 @@ list-stylesheets@^2.0.1: cheerio "1.0.0-rc.12" pick-util "^1.1.5" -listenercount@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" - integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ== - listr2@6.6.1: version "6.6.1" resolved "https://registry.yarnpkg.com/listr2/-/listr2-6.6.1.tgz#08b2329e7e8ba6298481464937099f4a2cd7f95d" @@ -9759,13 +9719,6 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== -"mkdirp@>=0.5 0": - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - mktemp@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/mktemp/-/mktemp-0.4.0.tgz#6d0515611c8a8c84e484aa2000129b98e981ff0b" @@ -10525,6 +10478,11 @@ pause@0.0.1: resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10= +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -11374,7 +11332,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -11703,7 +11661,7 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== -rimraf@2, rimraf@^2.5.4, rimraf@^2.6.3: +rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -11982,7 +11940,7 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" -setimmediate@^1.0.5, setimmediate@~1.0.4: +setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== @@ -12881,11 +12839,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== - tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -13200,22 +13153,6 @@ unpipe@1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -unzipper@0.10.11: - version "0.10.11" - resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.11.tgz#0b4991446472cbdb92ee7403909f26c2419c782e" - integrity sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw== - dependencies: - big-integer "^1.6.17" - binary "~0.3.0" - bluebird "~3.4.1" - buffer-indexof-polyfill "~1.0.0" - duplexer2 "~0.1.4" - fstream "^1.0.12" - graceful-fs "^4.2.2" - listenercount "~1.0.1" - readable-stream "~2.3.6" - setimmediate "~1.0.4" - upath@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -13973,6 +13910,14 @@ yarn-deduplicate@^6.0.2: semver "^7.5.0" tslib "^2.5.0" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yjs@^13.6.1: version "13.6.1" resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.6.1.tgz#13c1b26b2215cd39ba7bf9ef8b570561d82fa98e"