From a46e7565c57ec92f41f3b38092c04da43c9aeeb2 Mon Sep 17 00:00:00 2001 From: skarab42 Date: Fri, 12 Feb 2021 15:18:40 +0100 Subject: [PATCH] feat: Import/Export Panels and widgets (#150) --- app/package.json | 2 + app/server/api/panels.js | 11 ++ app/server/libs/panels.js | 186 +++++++++++++++++- app/static/locales/en/app.json | 4 +- app/static/locales/es/app.json | 4 +- app/static/locales/fr/app.json | 4 +- app/yarn.lock | 39 +++- front-src/client/api/panels.js | 3 + .../client/components/App/ContextMenu.svelte | 44 +++++ .../App/ContextMenu/ImportWidgetItem.svelte | 10 + .../components/Panels/Panel/Widget.svelte | 31 ++- .../client/components/Panels/Selector.svelte | 16 ++ front-src/client/libs/panels.js | 12 ++ front-src/client/stores/panels.js | 7 +- package.json | 2 + yarn.lock | 37 ++++ 16 files changed, 390 insertions(+), 22 deletions(-) create mode 100644 front-src/client/components/App/ContextMenu/ImportWidgetItem.svelte diff --git a/app/package.json b/app/package.json index 1b8826c1..1bc1d33d 100644 --- a/app/package.json +++ b/app/package.json @@ -19,8 +19,10 @@ "env-paths": "^2.2.0", "fs-extra": "^9.0.1", "get-system-fonts": "^2.0.2", + "human-id": "^2.0.1", "i18next": "^19.8.3", "i18next-fs-backend": "^1.0.7", + "jszip": "^3.6.0", "mime": "^2.4.6", "obs-websocket-js": "^4.0.2", "open": "^7.3.0", diff --git a/app/server/api/panels.js b/app/server/api/panels.js index 6adbc721..e815847c 100644 --- a/app/server/api/panels.js +++ b/app/server/api/panels.js @@ -51,4 +51,15 @@ module.exports = { this.notify("panels.update", payload.panel); return payload; }, + exportWidget(panel, widget) { + return panels.exportWidget(panel, widget); + }, + exportPanel(panel) { + return panels.exportPanel(panel); + }, + async importArchive(panel, widget) { + const payload = await panels.importArchive(panel, widget); + this.notify(`panels.${payload.event}`, payload.panel); + return payload; + }, }; diff --git a/app/server/libs/panels.js b/app/server/libs/panels.js index be594140..3aa4507f 100644 --- a/app/server/libs/panels.js +++ b/app/server/libs/panels.js @@ -1,8 +1,13 @@ const { panels: store } = require("../../stores"); +const { filesPath } = require("../../utils"); const cloneDeep = require("clone-deep"); +const { humanId } = require("human-id"); const actions = require("./actions"); const { v4: uuid } = require("uuid"); const { _ } = require("./i18next"); +const JSZip = require("jszip"); +const path = require("path"); +const fs = require("fs"); let panels = getAll(); @@ -14,17 +19,19 @@ function name(id) { return `${_("sentences.powers-group")} #${id.slice(0, 4)}`; } -function create() { +function create(panel = {}) { const id = uuid(); return { - id, name: name(id), widgets: [], grid: [], + ...panel, + id, }; } -function createWidget() { +function createWidget(widget = {}) { + widget && delete widget.id; return { id: uuid(), component: null, @@ -40,11 +47,12 @@ function createWidget() { backgroundColor: "#553C9A", backgroundImage: null, borders: "rounded", + ...widget, }; } -function add() { - let panel = create(); +function add(panel = {}) { + panel = create(panel); panels.push(panel); store.set("panels", panels); return panel; @@ -90,10 +98,10 @@ function findWidgetById(panel, id) { return panel.widgets.find((w) => w.id === id); } -function addWidget(panel, item) { - let widget = createWidget(); +function addWidget(panel, item, widget = {}) { + widget = createWidget(widget); const oldPanel = findPanelById(panel.id); - oldPanel.grid.push({ id: widget.id, ...item }); + oldPanel.grid.push({ ...item, id: widget.id }); oldPanel.widgets.push(widget); return { panel: update(oldPanel), widget, item }; } @@ -157,6 +165,165 @@ function removeWidget(panel, widget) { return { panel: update(oldPanel), widget }; } +function readAssetFile(filename) { + return fs.readFileSync(path.join(filesPath, filename)); +} + +async function exportArchive(type, store, files) { + const zip = new JSZip(); + const filename = `${humanId()}.marv-${type}`; + + zip.file("store.json", JSON.stringify(store)); + + files.forEach((filename) => + zip.file(`files/${filename}`, readAssetFile(filename)) + ); + + const buffer = await zip.generateAsync({ type: "nodebuffer" }); + return { filename, buffer }; +} + +async function exportPanel(panel) { + let files = []; + let panelActions = {}; + + panel.widgets.forEach((widget) => { + const action = actions.get(widget.id); + action && (panelActions[widget.id] = action); + files = [...files, ...getWidgetFiles({ widget, action })]; + }); + + return await exportArchive("panel", { panel, actions: panelActions }, files); +} + +function getWidgetFiles({ widget, action }) { + const files = []; + + if (widget.backgroundImage) { + files.push(widget.backgroundImage); + } + + action && + action.items.forEach(({ target }) => { + files.push(target.filename); + }); + + return files; +} + +async function exportWidget(panel, widget) { + const bbox = panel.grid.find((item) => item.id === widget.id); + const action = actions.get(widget.id); + const store = { widget, bbox, action }; + const files = getWidgetFiles(store); + + return exportArchive("widget", store, files); +} + +async function saveWidgetAsset(relativePath, file) { + let filepath = path.join(filesPath, relativePath); + const buffer = await file.async("nodebuffer"); + const renamed = fs.existsSync(filepath); + const oldPath = relativePath; + if (renamed) { + relativePath = `${humanId()}${path.extname(relativePath)}`; + filepath = path.join(filesPath, relativePath); + } + fs.writeFileSync(filepath, buffer); + return { renamed, oldPath, newPath: relativePath }; +} + +async function saveZipFiles(files) { + const promises = []; + + files.forEach((relativePath, file) => { + promises.push(saveWidgetAsset(relativePath, file)); + }); + + return await Promise.all(promises); +} + +async function loadArchive(buffer) { + const jszip = new JSZip(); + const zip = await jszip.loadAsync(buffer); + const store = await zip.file("store.json").async("string"); + const files = await zip.folder("files"); + + return { store: JSON.parse(store), files }; +} + +function renameWidgetFiles(results, { widget, action }) { + results.forEach(({ renamed, newPath, oldPath }) => { + if (!renamed) return; + if (widget.backgroundImage === oldPath) { + widget.backgroundImage = newPath; + } + if (action) { + action.items = action.items.map((item) => { + if (item.target.filename === oldPath) { + item.target.filename = newPath; + } + item.keyframes = item.keyframes.map((keyframe) => ({ + ...keyframe, + id: uuid(), + })); + return { ...item, id: uuid() }; + }); + } + }); +} + +async function importWidget(panel, { buffer, position }) { + const { store, files } = await loadArchive(buffer); + const results = await saveZipFiles(files); + + renameWidgetFiles(results, store); + + const result = addWidget(panel, position, store.widget); + + if (store.action) { + actions.update({ widget: result.widget, anime: store.action }); + } + + return { event: "update", ...result }; +} + +async function importPanel({ buffer }) { + const { store, files } = await loadArchive(buffer); + const results = await saveZipFiles(files); + let panel = add({ name: store.panel.name }); + + store.panel.widgets.forEach((widget) => { + const grid = store.panel.grid.find((item) => item.id === widget.id); + const action = store.actions[widget.id]; + + renameWidgetFiles(results, { widget, action }); + + const { x, y, w, h } = grid; + const result = addWidget(panel, { x, y, w, h }, widget); + panel = result.panel; + + if (action) { + action.id = result.widget.id; + actions.update({ widget: result.widget, anime: action }); + } + }); + + return { event: "add", panel }; +} + +function importArchive(panel, archive) { + if (archive.filename.endsWith(".marv-widget")) { + return importWidget(panel, archive); + } + + if (archive.filename.endsWith(".marv-panel")) { + return importPanel(archive); + } + + return { error: "Unsupported file format" }; +} + module.exports = { add, set, @@ -164,7 +331,10 @@ module.exports = { update, getAll, addWidget, + exportPanel, removeWidget, + importArchive, + exportWidget, duplicateWidget, moveWidgetToPanel, removeWidgetComponent, diff --git a/app/static/locales/en/app.json b/app/static/locales/en/app.json index 5e61b6e5..b9749501 100644 --- a/app/static/locales/en/app.json +++ b/app/static/locales/en/app.json @@ -145,7 +145,9 @@ "move": "move", "duplicate": "duplicate", "close": "close", - "confirm": "confirm" + "confirm": "confirm", + "export": "export", + "import": "import" }, "obs": { "scene-list": "OBS | Scene list", diff --git a/app/static/locales/es/app.json b/app/static/locales/es/app.json index 2b7a7f9c..4cd0a1c3 100644 --- a/app/static/locales/es/app.json +++ b/app/static/locales/es/app.json @@ -144,7 +144,9 @@ "move": "mover", "duplicate": "duplicado", "close": "cerrar", - "confirm": "confirmar" + "confirm": "confirmar", + "export": "exportar", + "import": "importar" }, "obs": { "scene-list": "OBS | Lista de escenas", diff --git a/app/static/locales/fr/app.json b/app/static/locales/fr/app.json index 70448484..3fabdef1 100644 --- a/app/static/locales/fr/app.json +++ b/app/static/locales/fr/app.json @@ -145,7 +145,9 @@ "move": "déplacer", "duplicate": "dupliquer", "close": "fermer", - "confirm": "confirmer" + "confirm": "confirmer", + "export": "exporter", + "import": "importer" }, "obs": { "scene-list": "OBS | Liste des scènes", diff --git a/app/yarn.lock b/app/yarn.lock index d0c3b9c1..350bd060 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -1040,6 +1040,11 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +human-id@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/human-id/-/human-id-2.0.1.tgz#71aadd0f46d577fd982358133cfd43f2a46f1477" + integrity sha512-XWoYbGsEfBB0mtUHiyihsefgf+s1tNQHj7sX1kgDxUM0IEKk8rcZIPTwUpqDdFIQbkViOLejbc0t8jBzz5jL3w== + i18next-fs-backend@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-1.0.7.tgz#00ca4587e306f8948740408389dda73461a5d07f" @@ -1066,6 +1071,11 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" @@ -1329,6 +1339,16 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jszip@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.6.0.tgz#839b72812e3f97819cc13ac4134ffced95dd6af9" + integrity sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + set-immediate-shim "~1.0.1" + kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -1339,6 +1359,13 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -1674,6 +1701,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + parseqs@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" @@ -1764,7 +1796,7 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -readable-stream@^2.0.6: +readable-stream@^2.0.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -1898,6 +1930,11 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-immediate-shim@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" diff --git a/front-src/client/api/panels.js b/front-src/client/api/panels.js index 30532f35..7a565999 100644 --- a/front-src/client/api/panels.js +++ b/front-src/client/api/panels.js @@ -13,4 +13,7 @@ export default { removeWidget: (panel, widget) => emit("panels.removeWidget", panel, widget), removeWidgetComponent: (panel, widget) => emit("panels.removeWidgetComponent", panel, widget), + exportWidget: (panel, widget) => emit("panels.exportWidget", panel, widget), + exportPanel: (panel) => emit("panels.exportPanel", panel), + importArchive: (panel, widget) => emit("panels.importArchive", panel, widget), }; diff --git a/front-src/client/components/App/ContextMenu.svelte b/front-src/client/components/App/ContextMenu.svelte index 64ddecad..39e15dba 100644 --- a/front-src/client/components/App/ContextMenu.svelte +++ b/front-src/client/components/App/ContextMenu.svelte @@ -1,9 +1,43 @@ + + + diff --git a/front-src/client/components/App/ContextMenu/ImportWidgetItem.svelte b/front-src/client/components/App/ContextMenu/ImportWidgetItem.svelte new file mode 100644 index 00000000..29aa7729 --- /dev/null +++ b/front-src/client/components/App/ContextMenu/ImportWidgetItem.svelte @@ -0,0 +1,10 @@ + + +{#if $panels.length} + {_('words.import')} +{/if} diff --git a/front-src/client/components/Panels/Panel/Widget.svelte b/front-src/client/components/Panels/Panel/Widget.svelte index 3f8bc1cc..cbd5a796 100644 --- a/front-src/client/components/Panels/Panel/Widget.svelte +++ b/front-src/client/components/Panels/Panel/Widget.svelte @@ -7,7 +7,10 @@ moveWidgetToPanel, } from "@/stores/panels"; import { _ } from "@/libs/i18next"; + import capitalize from "capitalize"; + import { saveAs } from "file-saver"; import Widget from "./Widget/Button.svelte"; + import { exportWidget } from "@/libs/panels"; import MdEdit from "svelte-icons/md/MdEdit.svelte"; import MdBrush from "svelte-icons/md/MdBrush.svelte"; import RemoveModal from "./Widget/RemoveModal.svelte"; @@ -16,6 +19,7 @@ import EditActionModal from "./Widget/EditActionModal.svelte"; import EditStylesModal from "./Widget/EditStylesModal.svelte"; import ContextMenu from "@/components/App/ContextMenu.svelte"; + import FaFileExport from "svelte-icons/fa/FaFileExport.svelte"; import PanelSelectModal from "./Widget/PanelSelectModal.svelte"; import MdContentCopy from "svelte-icons/md/MdContentCopy.svelte"; import MdArrowForward from "svelte-icons/md/MdArrowForward.svelte"; @@ -61,27 +65,36 @@ function stopPropagation(e) { e.stopPropagation(); } + + async function exportTo() { + const { filename, buffer } = await exportWidget(panel, widget); + const blob = new Blob([buffer], { type: "application/zip" }); + saveAs(blob, filename); + }
- - {_('sentences.edit-action')} + + {capitalize(_('sentences.edit-action'))} - - {_('sentences.edit-styles')} + + {capitalize(_('sentences.edit-styles'))} - - {_('words.duplicate')} + + {capitalize(_('words.duplicate'))} {#if $panels.length > 1} - {_('sentences.move-to')} + {capitalize(_('sentences.move-to'))} {/if} - - {_('words.remove')} + + {capitalize(_('words.export'))} + + + {capitalize(_('words.remove'))}
diff --git a/front-src/client/components/Panels/Selector.svelte b/front-src/client/components/Panels/Selector.svelte index 01ff1510..69ab85da 100644 --- a/front-src/client/components/Panels/Selector.svelte +++ b/front-src/client/components/Panels/Selector.svelte @@ -1,6 +1,8 @@ {#if $currentPanel} @@ -65,6 +74,13 @@ > {_('words.remove')} + + {_('words.export')} +
diff --git a/front-src/client/libs/panels.js b/front-src/client/libs/panels.js index 2ad96308..164cbccd 100644 --- a/front-src/client/libs/panels.js +++ b/front-src/client/libs/panels.js @@ -11,3 +11,15 @@ export function update(panel) { export function removeWidgetComponent(panel, widget) { return api.removeWidgetComponent(panel, widget); } + +export function exportWidget(panel, widget) { + return api.exportWidget(panel, widget); +} + +export function importArchive(panel, widget) { + return api.importArchive(panel, widget); +} + +export function exportPanel(panel) { + return api.exportPanel(panel); +} diff --git a/front-src/client/stores/panels.js b/front-src/client/stores/panels.js index 14349092..0c551581 100644 --- a/front-src/client/stores/panels.js +++ b/front-src/client/stores/panels.js @@ -47,7 +47,7 @@ export function setCurrentPanel(panel) { localStorage.setItem("currentPanel", panel && panel.id); } -function findSpaceForWidget(panel, props = {}) { +export function findSpaceForWidget(panel, props = {}) { const cols = get(gridOptions).cols; const item = { ...gridHelper.item(get(itemOptions)), ...props }; const pos = gridHelper.findSpaceForItem(item, panel.grid, cols); @@ -79,6 +79,9 @@ export async function moveWidgetToPanel({ panel, targetPanel, item }) { } function onAdd(panel, { owner }) { + if (panel.grid.length) { + makeGrid(panel); + } panels.update((state) => [...state, panel]); if (owner || !get(currentPanel)) { setCurrentPanel(panel); @@ -113,6 +116,8 @@ function makeGrid(panel) { } function onUpdate(panel) { + if (!panel) return; + const cp = get(currentPanel); makeGrid(panel); diff --git a/package.json b/package.json index e8e2c06f..9bb9c88e 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,10 @@ "electron-rebuild": "^2.3.2", "eslint": "^7.12.0", "eslint-plugin-svelte3": "^2.7.3", + "file-saver": "^2.0.5", "i18next": "^19.8.3", "i18next-http-backend": "^1.0.21", + "jszip": "^3.6.0", "prettier": "^2.1.2", "prettier-plugin-svelte": "^1.4.1", "rollup": "^2.32.1", diff --git a/yarn.lock b/yarn.lock index b7d46368..7c4102e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2185,6 +2185,11 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" +file-saver@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38" + integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA== + filelist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.1.tgz#f10d1a3ae86c1694808e8f20906f43d4c9132dbb" @@ -2791,6 +2796,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" @@ -3272,6 +3282,16 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jszip@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.6.0.tgz#839b72812e3f97819cc13ac4134ffced95dd6af9" + integrity sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + set-immediate-shim "~1.0.1" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -3330,6 +3350,13 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -4142,6 +4169,11 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -5001,6 +5033,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-immediate-shim@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"