mirror of
https://github.com/WowUp/WowUp.git
synced 2026-04-22 15:00:38 -04:00
Start working on the backup system
Feature flag for account functions for now disabled
This commit is contained in:
@@ -1,11 +1,34 @@
|
||||
import { exec } from 'child_process';
|
||||
import * as log from 'electron-log';
|
||||
import * as fsp from 'fs/promises';
|
||||
import { max, sumBy } from 'lodash';
|
||||
import * as path from 'path';
|
||||
import { exec } from "child_process";
|
||||
import * as log from "electron-log";
|
||||
import * as fsp from "fs/promises";
|
||||
import { max, sumBy } from "lodash";
|
||||
import * as path from "path";
|
||||
import * as crypto from "crypto";
|
||||
import * as AdmZip from "adm-zip";
|
||||
|
||||
import { TreeNode } from '../src/common/models/ipc-events';
|
||||
import { isWin } from './platform';
|
||||
import { TreeNode } from "../src/common/models/ipc-events";
|
||||
import { GetDirectoryTreeOptions } from "../src/common/models/ipc-request";
|
||||
import { isWin } from "./platform";
|
||||
|
||||
export function zipFile(srcPath: string, outPath: string): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const zip = new AdmZip();
|
||||
zip.addLocalFolder(srcPath);
|
||||
|
||||
zip.writeZip(outPath, (e) => {
|
||||
return e ? reject(e) : resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function readFileInZip(zipPath: string, filePath: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const zip = new AdmZip(zipPath);
|
||||
zip.readAsTextAsync(filePath, (data, err) => {
|
||||
return err ? reject(err) : resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function exists(path: string): Promise<boolean> {
|
||||
try {
|
||||
@@ -95,7 +118,7 @@ export async function readDirRecursive(sourcePath: string): Promise<string[]> {
|
||||
return dirFiles;
|
||||
}
|
||||
|
||||
export async function getDirTree(sourcePath: string): Promise<TreeNode> {
|
||||
export async function getDirTree(sourcePath: string, opts?: GetDirectoryTreeOptions): Promise<TreeNode> {
|
||||
const files = await fsp.readdir(sourcePath, { withFileTypes: true });
|
||||
|
||||
const node: TreeNode = {
|
||||
@@ -109,10 +132,18 @@ export async function getDirTree(sourcePath: string): Promise<TreeNode> {
|
||||
for (const file of files) {
|
||||
const filePath = path.join(sourcePath, file.name);
|
||||
if (file.isDirectory()) {
|
||||
const nestedNode = await getDirTree(filePath);
|
||||
const nestedNode = await getDirTree(filePath, opts);
|
||||
node.children.push(nestedNode);
|
||||
node.size = sumBy(node.children, (n) => n.size);
|
||||
if (opts?.includeHash) {
|
||||
node.hash = hashString(node.children.map((n) => n.hash).join(""), "sha256");
|
||||
}
|
||||
} else {
|
||||
let hash = "";
|
||||
if (opts?.includeHash) {
|
||||
hash = await hashFile(filePath, "sha256");
|
||||
}
|
||||
|
||||
const stats = await fsp.stat(filePath);
|
||||
node.size += stats.size;
|
||||
node.children.push({
|
||||
@@ -121,10 +152,15 @@ export async function getDirTree(sourcePath: string): Promise<TreeNode> {
|
||||
children: [],
|
||||
isDirectory: false,
|
||||
size: stats.size,
|
||||
hash,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (opts?.includeHash) {
|
||||
node.hash = hashString(node.children.map((n) => n.hash).join(""), "sha256");
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -139,3 +175,14 @@ export async function getLastModifiedFileDate(sourcePath: string): Promise<numbe
|
||||
const latest = max(dates);
|
||||
return latest;
|
||||
}
|
||||
|
||||
export function hashString(str: string | crypto.BinaryLike, alg = "md5") {
|
||||
const md5 = crypto.createHash(alg);
|
||||
md5.update(str);
|
||||
return md5.digest("hex");
|
||||
}
|
||||
|
||||
export async function hashFile(filePath: string, alg = "md5"): Promise<string> {
|
||||
const text = await fsp.readFile(filePath);
|
||||
return hashString(text, alg);
|
||||
}
|
||||
|
||||
@@ -89,11 +89,28 @@ import { createAppMenu } from "./app-menu";
|
||||
import { CurseFolderScanner } from "./curse-folder-scanner";
|
||||
import * as fsp from "fs/promises";
|
||||
|
||||
import { chmodDir, copyDir, getDirTree, getLastModifiedFileDate, readDirRecursive, remove } from "./file.utils";
|
||||
import {
|
||||
chmodDir,
|
||||
copyDir,
|
||||
exists,
|
||||
getDirTree,
|
||||
getLastModifiedFileDate,
|
||||
readDirRecursive,
|
||||
readFileInZip,
|
||||
remove,
|
||||
zipFile,
|
||||
} from "./file.utils";
|
||||
import { addonStore } from "./stores";
|
||||
import { createTray, restoreWindow } from "./system-tray";
|
||||
import { WowUpFolderScanner } from "./wowup-folder-scanner";
|
||||
import * as push from "./push";
|
||||
import { WowInstallation } from "../src/common/warcraft/wow-installation";
|
||||
import {
|
||||
BackupCreateRequest,
|
||||
BackupGetExistingRequest,
|
||||
GetDirectoryTreeRequest,
|
||||
} from "../src/common/models/ipc-request";
|
||||
import { BackupCreateResponse, BackupGetExistingResponse } from "../src/common/models/ipc-response";
|
||||
|
||||
let PENDING_OPEN_URLS: string[] = [];
|
||||
|
||||
@@ -310,6 +327,11 @@ export function initializeIpcHandlers(window: BrowserWindow, userAgent: string):
|
||||
});
|
||||
|
||||
handle(IPC_LIST_FILES_CHANNEL, async (evt, sourcePath: string, filter: string) => {
|
||||
const pathExists = await exists(sourcePath);
|
||||
if (!pathExists) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const globFilter = globrex(filter);
|
||||
const results = await fsp.readdir(sourcePath, { withFileTypes: true });
|
||||
const matches = _.filter(results, (entry) => globFilter.regex.test(entry.name));
|
||||
@@ -363,6 +385,14 @@ export function initializeIpcHandlers(window: BrowserWindow, userAgent: string):
|
||||
return arg.outputFolder;
|
||||
});
|
||||
|
||||
handle("zip-file", async (evt, srcPath: string, destPath: string) => {
|
||||
return await zipFile(srcPath, destPath);
|
||||
});
|
||||
|
||||
handle("zip-read-file", async (evt, zipPath: string, filePath: string) => {
|
||||
return await readFileInZip(zipPath, filePath);
|
||||
});
|
||||
|
||||
handle(IPC_COPY_FILE_CHANNEL, async (evt, arg: CopyFileRequest): Promise<boolean> => {
|
||||
log.info(`[FileCopy] '${arg.sourceFilePath}' -> '${arg.destinationFilePath}'`);
|
||||
const stat = await fsp.lstat(arg.sourceFilePath);
|
||||
@@ -409,8 +439,9 @@ export function initializeIpcHandlers(window: BrowserWindow, userAgent: string):
|
||||
return readDirRecursive(dirPath);
|
||||
});
|
||||
|
||||
handle(IPC_GET_DIRECTORY_TREE, (evt, dirPath: string): Promise<TreeNode> => {
|
||||
return getDirTree(dirPath);
|
||||
handle(IPC_GET_DIRECTORY_TREE, (evt, args: GetDirectoryTreeRequest): Promise<TreeNode> => {
|
||||
log.debug(IPC_GET_DIRECTORY_TREE, args);
|
||||
return getDirTree(args.dirPath, args.opts);
|
||||
});
|
||||
|
||||
handle(IPC_MINIMIZE_WINDOW, () => {
|
||||
@@ -484,6 +515,37 @@ export function initializeIpcHandlers(window: BrowserWindow, userAgent: string):
|
||||
return await push.subscribeToChannel(channel);
|
||||
});
|
||||
|
||||
handle("backup-get-existing", async (evt, req: BackupGetExistingRequest) => {
|
||||
const response: BackupGetExistingResponse = {
|
||||
exists: false,
|
||||
};
|
||||
|
||||
log.debug("backup-get-existing", req);
|
||||
|
||||
const backupFolder = path.join(req.backupPath, req.installation.id);
|
||||
response.exists = await exists(backupFolder);
|
||||
if (!response.exists) {
|
||||
return response;
|
||||
}
|
||||
|
||||
return response;
|
||||
});
|
||||
|
||||
handle("backup-create", async (evt, req: BackupCreateRequest) => {
|
||||
const response: BackupCreateResponse = {};
|
||||
|
||||
log.debug("backup-get-existing", req);
|
||||
|
||||
const backupFolder = path.join(req.backupPath, req.installation.id);
|
||||
await fsp.mkdir(backupFolder, { recursive: true });
|
||||
|
||||
// TODO create backup tree
|
||||
|
||||
// TODO copy backup contents
|
||||
|
||||
return response;
|
||||
});
|
||||
|
||||
ipcMain.on(IPC_DOWNLOAD_FILE_CHANNEL, (evt, arg: DownloadRequest) => {
|
||||
handleDownloadFile(arg).catch((e) => console.error(e.toString()));
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as path from "path";
|
||||
import * as pLimit from "p-limit";
|
||||
import * as log from "electron-log";
|
||||
import { WowUpScanResult } from "../src/common/wowup/models";
|
||||
import { exists, readDirRecursive } from "./file.utils";
|
||||
import { exists, readDirRecursive, hashFile, hashString } from "./file.utils";
|
||||
import * as fsp from "fs/promises";
|
||||
|
||||
const INVALID_PATH_CHARS = [
|
||||
@@ -86,14 +86,14 @@ export class WowUpFolderScanner {
|
||||
const limit = pLimit(4);
|
||||
const tasks = _.map(matchingFiles, (file) =>
|
||||
limit(async () => {
|
||||
return { hash: await this.hashFile(file), file };
|
||||
return { hash: await hashFile(file), file };
|
||||
})
|
||||
);
|
||||
const fileFingerprints = await Promise.all(tasks);
|
||||
|
||||
const fingerprintList = _.map(fileFingerprints, (ff) => ff.hash);
|
||||
const hashConcat = _.orderBy(fingerprintList).join("");
|
||||
const fingerprint = this.hashString(hashConcat);
|
||||
const fingerprint = hashString(hashConcat);
|
||||
|
||||
const result: WowUpScanResult = {
|
||||
fileFingerprints: fingerprintList,
|
||||
@@ -204,17 +204,6 @@ export class WowUpFolderScanner {
|
||||
return matches;
|
||||
}
|
||||
|
||||
private hashString(str: string | crypto.BinaryLike) {
|
||||
const md5 = crypto.createHash("md5");
|
||||
md5.update(str);
|
||||
return md5.digest("hex");
|
||||
}
|
||||
|
||||
private async hashFile(filePath: string): Promise<string> {
|
||||
const text = await fsp.readFile(filePath);
|
||||
return this.hashString(text);
|
||||
}
|
||||
|
||||
private getRealPath(filePath: string) {
|
||||
const lowerPath = filePath.toLowerCase();
|
||||
const matchedPath = this._fileMap[lowerPath];
|
||||
|
||||
237
wowup-electron/package-lock.json
generated
237
wowup-electron/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "wowup",
|
||||
"version": "2.5.0-beta.20",
|
||||
"version": "2.5.0-beta.21",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wowup",
|
||||
"version": "2.5.0-beta.20",
|
||||
"version": "2.5.0-beta.21",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "12.2.10",
|
||||
@@ -29,8 +29,10 @@
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@microsoft/applicationinsights-web": "2.7.0",
|
||||
"adm-zip": "0.5.9",
|
||||
"ag-grid-angular": "26.1.0",
|
||||
"ag-grid-community": "26.1.0",
|
||||
"archiver": "5.3.0",
|
||||
"auto-launch": "5.0.5",
|
||||
"compare-versions": "3.6.0",
|
||||
"electron-log": "4.4.1",
|
||||
@@ -73,6 +75,8 @@
|
||||
"@angular/cli": "12.2.10",
|
||||
"@ngx-translate/core": "13.0.0",
|
||||
"@ngx-translate/http-loader": "6.0.0",
|
||||
"@types/adm-zip": "0.4.34",
|
||||
"@types/archiver": "5.1.1",
|
||||
"@types/globrex": "0.1.1",
|
||||
"@types/jasmine": "3.10.0",
|
||||
"@types/jasminewd2": "2.0.10",
|
||||
@@ -3562,6 +3566,24 @@
|
||||
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/adm-zip": {
|
||||
"version": "0.4.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz",
|
||||
"integrity": "sha512-8ToYLLAYhkRfcmmljrKi22gT2pqu7hGMDtORP1emwIEGmgUTZOsaDjzWFzW5N2frcFRz/50CWt4zA1CxJ73pmQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/archiver": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.1.1.tgz",
|
||||
"integrity": "sha512-heuaCk0YH5m274NOLSi66H1zX6GtZoMsdE6TYFcpFFjBjg0FoU4i4/M/a/kNlgNg26Xk3g364mNOYe1JaiEPOQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/glob": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cacheable-request": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
|
||||
@@ -4795,12 +4817,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/adm-zip": {
|
||||
"version": "0.4.16",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
|
||||
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
|
||||
"dev": true,
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz",
|
||||
"integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==",
|
||||
"engines": {
|
||||
"node": ">=0.3.0"
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ag-grid-angular": {
|
||||
@@ -5094,7 +5115,6 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz",
|
||||
"integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"archiver-utils": "^2.1.0",
|
||||
"async": "^3.2.0",
|
||||
@@ -5112,7 +5132,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
|
||||
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.4",
|
||||
"graceful-fs": "^4.2.0",
|
||||
@@ -5132,14 +5151,12 @@
|
||||
"node_modules/archiver/node_modules/async": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz",
|
||||
"integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg=="
|
||||
},
|
||||
"node_modules/archiver/node_modules/readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -5620,8 +5637,7 @@
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/base": {
|
||||
"version": "0.11.2",
|
||||
@@ -5666,7 +5682,6 @@
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -5737,7 +5752,6 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
@@ -5748,7 +5762,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -5991,7 +6004,6 @@
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -6040,7 +6052,6 @@
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -6955,7 +6966,6 @@
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz",
|
||||
"integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer-crc32": "^0.2.13",
|
||||
"crc32-stream": "^4.0.2",
|
||||
@@ -6970,7 +6980,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -7037,8 +7046,7 @@
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "1.6.2",
|
||||
@@ -7651,8 +7659,7 @@
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
@@ -7697,7 +7704,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
|
||||
"integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"exit-on-epipe": "~1.0.1",
|
||||
"printj": "~1.1.0"
|
||||
@@ -7713,7 +7719,6 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz",
|
||||
"integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"crc-32": "^1.2.0",
|
||||
"readable-stream": "^3.4.0"
|
||||
@@ -7726,7 +7731,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -9544,7 +9548,6 @@
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
@@ -10675,7 +10678,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
|
||||
"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
@@ -11407,8 +11409,7 @@
|
||||
"node_modules/fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "9.1.0",
|
||||
@@ -11446,8 +11447,7 @@
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
@@ -11788,7 +11788,6 @@
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@@ -12882,7 +12881,6 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -13068,7 +13066,6 @@
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
@@ -13077,8 +13074,7 @@
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ini": {
|
||||
"version": "2.0.0",
|
||||
@@ -13828,8 +13824,7 @@
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"node_modules/isbinaryfile": {
|
||||
"version": "4.0.8",
|
||||
@@ -14574,7 +14569,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
|
||||
"integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"readable-stream": "^2.0.5"
|
||||
},
|
||||
@@ -14878,14 +14872,12 @@
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
|
||||
},
|
||||
"node_modules/lodash.difference": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
||||
"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
|
||||
},
|
||||
"node_modules/lodash.escaperegexp": {
|
||||
"version": "4.1.2",
|
||||
@@ -14895,8 +14887,7 @@
|
||||
"node_modules/lodash.flatten": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
@@ -14918,8 +14909,7 @@
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
|
||||
"dev": true
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "3.0.4",
|
||||
@@ -14942,8 +14932,7 @@
|
||||
"node_modules/lodash.union": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=",
|
||||
"dev": true
|
||||
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
|
||||
},
|
||||
"node_modules/lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
@@ -15747,7 +15736,6 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@@ -17009,7 +16997,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -19675,7 +19662,6 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
|
||||
"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"printj": "bin/printj.njs"
|
||||
},
|
||||
@@ -19686,8 +19672,7 @@
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
@@ -21621,7 +21606,6 @@
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@@ -21636,7 +21620,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz",
|
||||
"integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimatch": "^3.0.4"
|
||||
}
|
||||
@@ -23660,7 +23643,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@@ -24112,7 +24094,6 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
@@ -24128,7 +24109,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -25255,8 +25235,7 @@
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
@@ -25455,6 +25434,15 @@
|
||||
"node": ">=6.9.x"
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/adm-zip": {
|
||||
"version": "0.4.16",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
|
||||
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
@@ -27042,8 +27030,7 @@
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"node_modules/write-file-atomic": {
|
||||
"version": "3.0.3",
|
||||
@@ -27286,7 +27273,6 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz",
|
||||
"integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"archiver-utils": "^2.1.0",
|
||||
"compress-commons": "^4.1.0",
|
||||
@@ -27300,7 +27286,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -29810,6 +29795,24 @@
|
||||
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/adm-zip": {
|
||||
"version": "0.4.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz",
|
||||
"integrity": "sha512-8ToYLLAYhkRfcmmljrKi22gT2pqu7hGMDtORP1emwIEGmgUTZOsaDjzWFzW5N2frcFRz/50CWt4zA1CxJ73pmQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/archiver": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.1.1.tgz",
|
||||
"integrity": "sha512-heuaCk0YH5m274NOLSi66H1zX6GtZoMsdE6TYFcpFFjBjg0FoU4i4/M/a/kNlgNg26Xk3g364mNOYe1JaiEPOQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/glob": "*"
|
||||
}
|
||||
},
|
||||
"@types/cacheable-request": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
|
||||
@@ -30789,10 +30792,9 @@
|
||||
}
|
||||
},
|
||||
"adm-zip": {
|
||||
"version": "0.4.16",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
|
||||
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
|
||||
"dev": true
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz",
|
||||
"integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg=="
|
||||
},
|
||||
"ag-grid-angular": {
|
||||
"version": "26.1.0",
|
||||
@@ -31023,7 +31025,6 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz",
|
||||
"integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"archiver-utils": "^2.1.0",
|
||||
"async": "^3.2.0",
|
||||
@@ -31037,14 +31038,12 @@
|
||||
"async": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz",
|
||||
"integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -31057,7 +31056,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
|
||||
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.4",
|
||||
"graceful-fs": "^4.2.0",
|
||||
@@ -31429,8 +31427,7 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"base": {
|
||||
"version": "0.11.2",
|
||||
@@ -31467,8 +31464,7 @@
|
||||
"base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||
},
|
||||
"base64id": {
|
||||
"version": "2.0.0",
|
||||
@@ -31516,7 +31512,6 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
@@ -31527,7 +31522,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -31722,7 +31716,6 @@
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -31758,7 +31751,6 @@
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
@@ -32483,7 +32475,6 @@
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz",
|
||||
"integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-crc32": "^0.2.13",
|
||||
"crc32-stream": "^4.0.2",
|
||||
@@ -32495,7 +32486,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -32554,8 +32544,7 @@
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"concat-stream": {
|
||||
"version": "1.6.2",
|
||||
@@ -33029,8 +33018,7 @@
|
||||
"core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"cors": {
|
||||
"version": "2.8.5",
|
||||
@@ -33069,7 +33057,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
|
||||
"integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"exit-on-epipe": "~1.0.1",
|
||||
"printj": "~1.1.0"
|
||||
@@ -33079,7 +33066,6 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz",
|
||||
"integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"crc-32": "^1.2.0",
|
||||
"readable-stream": "^3.4.0"
|
||||
@@ -33089,7 +33075,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -34481,7 +34466,6 @@
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
@@ -35310,8 +35294,7 @@
|
||||
"exit-on-epipe": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
|
||||
"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw=="
|
||||
},
|
||||
"expand-brackets": {
|
||||
"version": "2.1.4",
|
||||
@@ -35902,8 +35885,7 @@
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.1.0",
|
||||
@@ -35935,8 +35917,7 @@
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
@@ -36200,7 +36181,6 @@
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@@ -37084,8 +37064,7 @@
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.1.8",
|
||||
@@ -37211,7 +37190,6 @@
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
@@ -37220,8 +37198,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ini": {
|
||||
"version": "2.0.0",
|
||||
@@ -37764,8 +37741,7 @@
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"isbinaryfile": {
|
||||
"version": "4.0.8",
|
||||
@@ -38361,7 +38337,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
|
||||
"integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"readable-stream": "^2.0.5"
|
||||
}
|
||||
@@ -38599,14 +38574,12 @@
|
||||
"lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
|
||||
},
|
||||
"lodash.difference": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
||||
"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
|
||||
},
|
||||
"lodash.escaperegexp": {
|
||||
"version": "4.1.2",
|
||||
@@ -38616,8 +38589,7 @@
|
||||
"lodash.flatten": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
|
||||
},
|
||||
"lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
@@ -38639,8 +38611,7 @@
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
|
||||
"dev": true
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "3.0.4",
|
||||
@@ -38663,8 +38634,7 @@
|
||||
"lodash.union": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=",
|
||||
"dev": true
|
||||
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
|
||||
},
|
||||
"lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
@@ -39278,7 +39248,6 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -40240,7 +40209,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -42161,14 +42129,12 @@
|
||||
"printj": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
|
||||
"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"progress": {
|
||||
"version": "2.0.3",
|
||||
@@ -43996,7 +43962,6 @@
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@@ -44011,7 +43976,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz",
|
||||
"integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimatch": "^3.0.4"
|
||||
}
|
||||
@@ -45622,7 +45586,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@@ -45964,7 +45927,6 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
@@ -45977,7 +45939,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -46844,8 +46805,7 @@
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
@@ -47134,6 +47094,12 @@
|
||||
"xml2js": "^0.4.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"adm-zip": {
|
||||
"version": "0.4.16",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
|
||||
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
@@ -48240,8 +48206,7 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"write-file-atomic": {
|
||||
"version": "3.0.3",
|
||||
@@ -48413,7 +48378,6 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz",
|
||||
"integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"archiver-utils": "^2.1.0",
|
||||
"compress-commons": "^4.1.0",
|
||||
@@ -48424,7 +48388,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
"@angular/cli": "12.2.10",
|
||||
"@ngx-translate/core": "13.0.0",
|
||||
"@ngx-translate/http-loader": "6.0.0",
|
||||
"@types/adm-zip": "0.4.34",
|
||||
"@types/archiver": "5.1.1",
|
||||
"@types/globrex": "0.1.1",
|
||||
"@types/jasmine": "3.10.0",
|
||||
"@types/jasminewd2": "2.0.10",
|
||||
@@ -131,8 +133,10 @@
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@microsoft/applicationinsights-web": "2.7.0",
|
||||
"adm-zip": "0.5.9",
|
||||
"ag-grid-angular": "26.1.0",
|
||||
"ag-grid-community": "26.1.0",
|
||||
"archiver": "5.3.0",
|
||||
"auto-launch": "5.0.5",
|
||||
"compare-versions": "3.6.0",
|
||||
"electron-log": "4.4.1",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
import { Observable, of } from "rxjs";
|
||||
|
||||
import { Addon } from "../../common/entities/addon";
|
||||
|
||||
@@ -34,7 +34,7 @@ import { AddonSearchResult } from "../models/wowup/addon-search-result";
|
||||
import { AddonSearchResultDependency } from "../models/wowup/addon-search-result-dependency";
|
||||
import { AddonSearchResultFile } from "../models/wowup/addon-search-result-file";
|
||||
import { ProtocolSearchResult } from "../models/wowup/protocol-search-result";
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
import { ElectronService } from "../services";
|
||||
import { CachingService } from "../services/caching/caching-service";
|
||||
import { CircuitBreakerWrapper, NetworkService } from "../services/network/network.service";
|
||||
|
||||
@@ -22,7 +22,7 @@ import { AddonChannelType } from "../../common/wowup/models";
|
||||
import { AddonSearchResult } from "../models/wowup/addon-search-result";
|
||||
import { AddonSearchResultFile } from "../models/wowup/addon-search-result-file";
|
||||
import { AddonProvider, GetAllResult } from "./addon-provider";
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
import { convertMarkdown } from "../utils/markdown.utlils";
|
||||
import { strictFilterBy } from "../utils/array.utils";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
import * as _ from "lodash";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { TukUiAddon } from "../models/tukui/tukui-addon";
|
||||
import { AddonFolder } from "../models/wowup/addon-folder";
|
||||
import { AddonSearchResult } from "../models/wowup/addon-search-result";
|
||||
import { AddonSearchResultFile } from "../models/wowup/addon-search-result-file";
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
import { CachingService } from "../services/caching/caching-service";
|
||||
import { CircuitBreakerWrapper, NetworkService } from "../services/network/network.service";
|
||||
import { getGameVersion } from "../utils/addon.utils";
|
||||
|
||||
@@ -13,7 +13,7 @@ import { AddonDetailsResponse } from "../models/wow-interface/addon-details-resp
|
||||
import { AddonFolder } from "../models/wowup/addon-folder";
|
||||
import { AddonSearchResult } from "../models/wowup/addon-search-result";
|
||||
import { AddonSearchResultFile } from "../models/wowup/addon-search-result-file";
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
import { CachingService } from "../services/caching/caching-service";
|
||||
import { CircuitBreakerWrapper, NetworkService } from "../services/network/network.service";
|
||||
import { getGameVersion } from "../utils/addon.utils";
|
||||
|
||||
@@ -23,7 +23,7 @@ import { AddonFolder } from "../models/wowup/addon-folder";
|
||||
import { AddonSearchResult } from "../models/wowup/addon-search-result";
|
||||
import { AddonSearchResultFile } from "../models/wowup/addon-search-result-file";
|
||||
import { AppWowUpScanResult } from "../models/wowup/app-wowup-scan-result";
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
import { ElectronService } from "../services";
|
||||
import { CachingService } from "../services/caching/caching-service";
|
||||
import { CircuitBreakerWrapper, NetworkService } from "../services/network/network.service";
|
||||
|
||||
@@ -4,7 +4,7 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { ADDON_PROVIDER_WOWUP_COMPANION, WOWUP_DATA_ADDON_FOLDER_NAME } from "../../common/constants";
|
||||
import { AddonChannelType } from "../../common/wowup/models";
|
||||
import { AddonFolder } from "../models/wowup/addon-folder";
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
import { FileService } from "../services/files/file.service";
|
||||
import { TocService } from "../services/toc/toc.service";
|
||||
import { getGameVersion } from "../utils/addon.utils";
|
||||
|
||||
@@ -15,7 +15,7 @@ import { FileService } from "../services/files/file.service";
|
||||
import { TocService } from "../services/toc/toc.service";
|
||||
import { WarcraftService } from "../services/warcraft/warcraft.service";
|
||||
import { AddonProvider } from "./addon-provider";
|
||||
import { WowInstallation } from "../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../common/warcraft/wow-installation";
|
||||
|
||||
const VALID_ZIP_CONTENT_TYPES = ["application/zip", "application/x-zip-compressed", "application/octet-stream"];
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { BehaviorSubject, Subscription } from "rxjs";
|
||||
import { map } from "rxjs/operators";
|
||||
import { AddonInstallState } from "../../../models/wowup/addon-install-state";
|
||||
import { WowInstallation } from "../../../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../../../common/warcraft/wow-installation";
|
||||
import {
|
||||
AddonBrokerService,
|
||||
ExportPayload,
|
||||
|
||||
@@ -7,10 +7,10 @@ import { FormControl } from "@angular/forms";
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
|
||||
|
||||
import { ProtocolSearchResult } from "../../../models/wowup/protocol-search-result";
|
||||
import { WowInstallation } from "../../../models/wowup/wow-installation";
|
||||
import { AddonService } from "../../../services/addons/addon.service";
|
||||
import { SessionService } from "../../../services/session/session.service";
|
||||
import { WarcraftInstallationService } from "../../../services/warcraft/warcraft-installation.service";
|
||||
import { WowInstallation } from "../../../../common/warcraft/wow-installation";
|
||||
|
||||
export interface InstallFromProtocolDialogComponentData {
|
||||
protocol: string;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<div mat-dialog-title class="wtf-backup-dialog">
|
||||
<div class="row align-items-center">
|
||||
<h2 class="flex-grow-1 m-0">
|
||||
{{ "WTF_BACKUP.DIALOG_TITLE" | translate: { clientType: selectedInstallation.label } }}
|
||||
</h2>
|
||||
<button mat-icon-button [mat-dialog-close]="true" color="accent" [disabled]="busy$ | async">
|
||||
<mat-icon svgIcon="fas:times"> </mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<mat-dialog-content>
|
||||
<div *ngIf="(hasBackups$ | async) === false">
|
||||
<h4 class="mb-0">No backups were found at:</h4>
|
||||
<p class="text-2">{{ backupPath }}</p>
|
||||
</div>
|
||||
<div *ngIf="(hasBackups$ | async) === true">
|
||||
<p class="text-2">
|
||||
{{ "WTF_BACKUP.BACKUP_COUNT_TEXT" | translate: { count: backupCt$ | async } }}
|
||||
</p>
|
||||
<ul class="backup-list rounded">
|
||||
<li *ngFor="let backup of backups$ | async" class="backup-list-item">
|
||||
<div class="row align-items-center">
|
||||
<div class="flex-grow-1">
|
||||
<div class="row">
|
||||
<div class="title ml-1 flex-grow-1" [ngClass]="{ 'text-warning': backup.error }">{{ backup.title }}</div>
|
||||
</div>
|
||||
<div class="row text-2">
|
||||
<div class="mr-3">{{ backup.date | localeDate: "medium" }}</div>
|
||||
<div class="mr-3">{{ backup.size }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ACTIONS -->
|
||||
<div *ngIf="backup.error" class="flex-shrink-0">{{ "WTF_BACKUP.ERROR." + backup.error | translate }}</div>
|
||||
<div *ngIf="backup.error === undefined" class="flex-shrink-0">
|
||||
<button mat-icon-button [matTooltip]="'WTF_BACKUP.TOOL_TIP.APPLY_BUTTON' | translate">
|
||||
<mat-icon svgIcon="fas:history"> </mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" [matTooltip]="'WTF_BACKUP.TOOL_TIP.DELETE_BUTTON' | translate">
|
||||
<mat-icon svgIcon="fas:trash"> </mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button color="primary" [disabled]="busy$ | async" (click)="onShowFolder()">
|
||||
{{ "WTF_BACKUP.SHOW_FOLDER_BUTTON" | translate }}
|
||||
</button>
|
||||
<div class="flex-grow-1"></div>
|
||||
<button mat-flat-button color="primary" [disabled]="busy$ | async" (click)="onCreateBackup()">
|
||||
{{ "WTF_BACKUP.CREATE_BACKUP_BUTTON" | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,28 @@
|
||||
.wtf-backup-dialog {
|
||||
min-width: 600px;
|
||||
}
|
||||
|
||||
.backup-list {
|
||||
background-color: var(--background-secondary-2);
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
padding: 0 1em;
|
||||
|
||||
.backup-list-item:not(:last-child) {
|
||||
border-bottom: 1px solid var(--background-secondary-1);
|
||||
}
|
||||
|
||||
.backup-list-item {
|
||||
padding: 1em 0;
|
||||
list-style: none;
|
||||
|
||||
.status-badge {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WtfBackupComponent } from './wtf-backup.component';
|
||||
|
||||
describe('WtfBackupComponent', () => {
|
||||
let component: WtfBackupComponent;
|
||||
let fixture: ComponentFixture<WtfBackupComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ WtfBackupComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WtfBackupComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { map } from "rxjs/operators";
|
||||
import { WowInstallation } from "../../../../common/warcraft/wow-installation";
|
||||
import { ElectronService } from "../../../services";
|
||||
import { SessionService } from "../../../services/session/session.service";
|
||||
import { WtfBackup, WtfService } from "../../../services/wtf/wtf.service";
|
||||
import { formatSize } from "../../../utils/number.utils";
|
||||
|
||||
interface WtfBackupViewModel {
|
||||
title: string;
|
||||
size: string;
|
||||
date: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-wtf-backup",
|
||||
templateUrl: "./wtf-backup.component.html",
|
||||
styleUrls: ["./wtf-backup.component.scss"],
|
||||
})
|
||||
export class WtfBackupComponent implements OnInit {
|
||||
public readonly busy$ = new BehaviorSubject<boolean>(false);
|
||||
public readonly backups$ = new BehaviorSubject<WtfBackupViewModel[]>([]);
|
||||
|
||||
public readonly selectedInstallation: WowInstallation;
|
||||
public readonly hasBackups$ = this.backups$.pipe(map((backups) => backups.length > 0));
|
||||
public readonly backupCt$ = this.backups$.pipe(map((backups) => backups.length));
|
||||
public readonly backupPath: string;
|
||||
|
||||
constructor(
|
||||
private _electronService: ElectronService,
|
||||
private _sessionService: SessionService,
|
||||
private _wtfService: WtfService
|
||||
) {
|
||||
this.selectedInstallation = this._sessionService.getSelectedWowInstallation();
|
||||
this.backupPath = this._wtfService.getBackupPath(this.selectedInstallation);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadBackups();
|
||||
}
|
||||
|
||||
async onShowFolder(): Promise<void> {
|
||||
const backupPath = this._wtfService.getBackupPath(this.selectedInstallation);
|
||||
await this._electronService.openPath(backupPath);
|
||||
}
|
||||
|
||||
async onCreateBackup(): Promise<void> {
|
||||
this.busy$.next(true);
|
||||
try {
|
||||
await this._wtfService.createBackup(this.selectedInstallation);
|
||||
await this.loadBackups();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.busy$.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async loadBackups() {
|
||||
this.busy$.next(true);
|
||||
try {
|
||||
const backups = await this._wtfService.getBackupList(this.selectedInstallation);
|
||||
console.debug(backups);
|
||||
|
||||
const viewModels = backups.map((b) => this.toViewModel(b));
|
||||
console.debug(viewModels);
|
||||
this.backups$.next(viewModels);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.busy$.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
private toViewModel(backup: WtfBackup): WtfBackupViewModel {
|
||||
return {
|
||||
title: backup.fileName,
|
||||
size: formatSize(backup.size),
|
||||
date: backup.metadata?.createdAt ?? backup.birthtimeMs,
|
||||
error: backup.error,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,6 @@
|
||||
matTooltip="{{ 'PAGES.MY_ADDONS.PAGE_CONTEXT_FOOTER.JOIN_DISCORD' | translate }}">
|
||||
<mat-icon class="tab-icon tab-icon-inactive" svgIcon="fab:discord"></mat-icon>
|
||||
</a>
|
||||
<!-- <div *ngIf="(sessionService.wowUpAuthenticated$ | async) === false">
|
||||
<div class="bnet-btn bnet-btn-sm ml-2" (click)="onClickAccount()">Login</div>
|
||||
</div>
|
||||
<div *ngIf="(sessionService.wowUpAuthenticated$ | async) === true" class="btn" (click)="onClickAccount()"
|
||||
matTooltip="{{ 'You\'re logged in!' | translate }}">
|
||||
{{accountDisplayName$ | async}}
|
||||
</div> -->
|
||||
<p class="text-1">{{ sessionService.statusText$ | async }}</p>
|
||||
<div class="flex-grow-1"></div>
|
||||
<p class="mr-3">{{ sessionService.pageContextText$ | async }}</p>
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
<div class="tab-strip bg-primary text-1" [ngClass]="{
|
||||
mac: electronService.isMac,
|
||||
windows: electronService.isWin,
|
||||
linux: electronService.isLinux
|
||||
}">
|
||||
<div *ngFor="let tab of tabsTop" class="tab"
|
||||
[ngClass]="{ selected: tab.isSelected$ | async, disabled: tab.isDisabled$ | async }" (click)="tab.onClick(tab)">
|
||||
<mat-icon class="tab-icon tab-icon-inactive"
|
||||
<div
|
||||
class="tab-strip bg-primary text-1"
|
||||
[ngClass]="{
|
||||
mac: electronService.isMac,
|
||||
windows: electronService.isWin,
|
||||
linux: electronService.isLinux
|
||||
}"
|
||||
>
|
||||
<div
|
||||
*ngFor="let tab of tabsTop"
|
||||
class="tab"
|
||||
[ngClass]="{ selected: tab.isSelected$ | async, disabled: tab.isDisabled$ | async }"
|
||||
(click)="tab.onClick(tab)"
|
||||
>
|
||||
<mat-icon
|
||||
class="tab-icon tab-icon-inactive"
|
||||
[ngClass]="{ 'tab-icon-active': tab.isSelected$ | async, 'tab-icon-disabled': tab.isDisabled$ | async }"
|
||||
[svgIcon]="tab.icon">
|
||||
[svgIcon]="tab.icon"
|
||||
>
|
||||
</mat-icon>
|
||||
<div class="tab-title" *ngIf="tab.titleKey">{{ tab.titleKey | translate }}</div>
|
||||
<!-- <mat-icon *ngIf="tab.badge === true" class="tab-icon-inactive z-badge clear-badge" matBadge="1" matBadgeColor="accent"
|
||||
@@ -15,40 +24,49 @@
|
||||
</mat-icon> -->
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<a appExternalLink class="text-1 tab hover-bg-secondary-2" [href]="wowUpWebsiteUrl + '/guide'"
|
||||
matTooltip="{{ 'PAGES.MY_ADDONS.PAGE_CONTEXT_FOOTER.VIEW_GUIDE' | translate }}">
|
||||
<a
|
||||
appExternalLink
|
||||
class="text-1 tab hover-bg-secondary-2"
|
||||
[href]="wowUpWebsiteUrl + '/guide'"
|
||||
matTooltip="{{ 'PAGES.MY_ADDONS.PAGE_CONTEXT_FOOTER.VIEW_GUIDE' | translate }}"
|
||||
>
|
||||
<mat-icon class="tab-icon tab-icon-inactive" svgIcon="far:question-circle"></mat-icon>
|
||||
<div class="tab-title">{{ 'PAGES.HOME.GUIDE_TAB_TITLE' | translate }}</div>
|
||||
<div class="tab-title">{{ "PAGES.HOME.GUIDE_TAB_TITLE" | translate }}</div>
|
||||
</a>
|
||||
<!-- <a appExternalLink class="patreon-link tab hover-bg-secondary-2" href="https://www.patreon.com/jliddev"
|
||||
matTooltip="{{ 'PAGES.MY_ADDONS.PAGE_CONTEXT_FOOTER.PATREON_SUPPORT' | translate }}">
|
||||
<img class="tab-icon patron-img" src="assets/images/patreon_logo_small.png" />
|
||||
</a> -->
|
||||
<!-- <a appExternalLink class="text-1 tab hover-bg-secondary-2" href="https://discord.gg/rk4F5aD"
|
||||
matTooltip="{{ 'PAGES.MY_ADDONS.PAGE_CONTEXT_FOOTER.JOIN_DISCORD' | translate }}">
|
||||
<mat-icon class="tab-icon tab-icon-inactive" svgIcon="fab:discord"></mat-icon>
|
||||
</a> -->
|
||||
<div class="tab hover-bg-secondary-2" [ngClass]="{ selected: isAccountSelected$ | async }"
|
||||
(click)="onClickTab(TAB_INDEX_ACCOUNT)">
|
||||
<!-- <div class="tab-icon"> -->
|
||||
<mat-icon class="tab-icon tab-icon-inactive" svgIcon="fas:user-circle"
|
||||
[ngClass]="{ 'tab-icon-active': isAccountSelected$ | async }">
|
||||
|
||||
<!-- ACCOUNT TAB -->
|
||||
<div
|
||||
*ngIf="FEATURE_ACCOUNTS_ENABLED"
|
||||
class="tab hover-bg-secondary-2"
|
||||
[ngClass]="{ selected: isAccountSelected$ | async }"
|
||||
(click)="onClickTab(TAB_INDEX_ACCOUNT)"
|
||||
>
|
||||
<mat-icon
|
||||
class="tab-icon tab-icon-inactive"
|
||||
svgIcon="fas:user-circle"
|
||||
[ngClass]="{ 'tab-icon-active': isAccountSelected$ | async }"
|
||||
>
|
||||
</mat-icon>
|
||||
<!-- </div> -->
|
||||
<div>
|
||||
<div class="tab-title">{{ 'PAGES.HOME.ACCOUNT_TAB_TITLE' | translate }}
|
||||
</div>
|
||||
<div class="tab-title">{{ "PAGES.HOME.ACCOUNT_TAB_TITLE" | translate }}</div>
|
||||
<div *ngIf="(sessionService.wowUpAuthenticated$ | async) === true" class="tab-subtitle">Logged in</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let tab of tabsBottom" class="tab hover-bg-secondary-2"
|
||||
[ngClass]="{ selected: tab.isSelected$ | async, disabled: tab.isDisabled$ | async }" (click)="tab.onClick(tab)">
|
||||
<div
|
||||
*ngFor="let tab of tabsBottom"
|
||||
class="tab hover-bg-secondary-2"
|
||||
[ngClass]="{ selected: tab.isSelected$ | async, disabled: tab.isDisabled$ | async }"
|
||||
(click)="tab.onClick(tab)"
|
||||
>
|
||||
<!-- <div class="tab-icon"> -->
|
||||
<mat-icon class="tab-icon tab-icon-inactive" [svgIcon]="tab.icon"
|
||||
[ngClass]="{ 'tab-icon-active': tab.isSelected$ | async, 'tab-icon-disabled': tab.isDisabled$ | async }">
|
||||
<mat-icon
|
||||
class="tab-icon tab-icon-inactive"
|
||||
[svgIcon]="tab.icon"
|
||||
[ngClass]="{ 'tab-icon-active': tab.isSelected$ | async, 'tab-icon-disabled': tab.isDisabled$ | async }"
|
||||
>
|
||||
</mat-icon>
|
||||
<!-- </div> -->
|
||||
<div class="tab-title" *ngIf="tab.titleKey">{{ tab.titleKey | translate }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { map } from "rxjs/operators";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import {
|
||||
FEATURE_ACCOUNTS_ENABLED,
|
||||
TAB_INDEX_ABOUT,
|
||||
TAB_INDEX_GET_ADDONS,
|
||||
TAB_INDEX_MY_ADDONS,
|
||||
@@ -33,6 +34,7 @@ interface Tab {
|
||||
export class HorizontalTabsComponent implements OnInit {
|
||||
public wowUpWebsiteUrl = AppConfig.wowUpWebsiteUrl;
|
||||
public TAB_INDEX_ACCOUNT = TAB_INDEX_ABOUT;
|
||||
public FEATURE_ACCOUNTS_ENABLED = FEATURE_ACCOUNTS_ENABLED;
|
||||
|
||||
public isAccountSelected$ = this.sessionService.selectedHomeTab$.pipe(map((result) => result === TAB_INDEX_ABOUT));
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ import { MatDialog } from "@angular/material/dialog";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
|
||||
import { WowClientType } from "../../../../common/warcraft/wow-client-type";
|
||||
import { WowInstallation } from "../../../models/wowup/wow-installation";
|
||||
import { WowUpReleaseChannelType } from "../../../models/wowup/wowup-release-channel-type";
|
||||
import { WarcraftInstallationService } from "../../../services/warcraft/warcraft-installation.service";
|
||||
import { WarcraftService } from "../../../services/warcraft/warcraft.service";
|
||||
import { WowUpService } from "../../../services/wowup/wowup.service";
|
||||
import { getEnumList, getEnumName } from "../../../utils/enum.utils";
|
||||
import { AlertDialogComponent } from "../../common/alert-dialog/alert-dialog.component";
|
||||
import { WowInstallation } from "../../../../common/warcraft/wow-installation";
|
||||
|
||||
@Component({
|
||||
selector: "app-options-wow-section",
|
||||
|
||||
@@ -9,12 +9,12 @@ import { TranslateService } from "@ngx-translate/core";
|
||||
|
||||
import { WowClientType } from "../../../../common/warcraft/wow-client-type";
|
||||
import { AddonChannelType } from "../../../../common/wowup/models";
|
||||
import { WowInstallation } from "../../../models/wowup/wow-installation";
|
||||
import { WarcraftInstallationService } from "../../../services/warcraft/warcraft-installation.service";
|
||||
import { getEnumList, getEnumName } from "../../../utils/enum.utils";
|
||||
import { ConfirmDialogComponent } from "../../common/confirm-dialog/confirm-dialog.component";
|
||||
import { WarcraftService } from "../../../services/warcraft/warcraft.service";
|
||||
import { SessionService } from "../../../services/session/session.service";
|
||||
import { WowInstallation } from "../../../../common/warcraft/wow-installation";
|
||||
|
||||
@Component({
|
||||
selector: "app-wow-client-options",
|
||||
|
||||
@@ -9,9 +9,9 @@ import { WarcraftService } from "../../../services/warcraft/warcraft.service";
|
||||
import { WtfNode, WtfService } from "../../../services/wtf/wtf.service";
|
||||
import { removeExtension } from "../../../utils/string.utils";
|
||||
import { AddonFolder } from "../../../models/wowup/addon-folder";
|
||||
import { WowInstallation } from "../../../models/wowup/wow-installation";
|
||||
import { WarcraftInstallationService } from "../../../services/warcraft/warcraft-installation.service";
|
||||
import { formatSize } from "../../../utils/number.utils";
|
||||
import { WowInstallation } from "../../../../common/warcraft/wow-installation";
|
||||
|
||||
interface SavedVariable {
|
||||
name: string;
|
||||
|
||||
@@ -21,6 +21,7 @@ import { InstallFromUrlDialogComponent } from "../components/addons/install-from
|
||||
import { DirectiveModule } from "./directive.module";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { AddonManageDialogComponent } from "../components/addons/addon-manage-dialog/addon-manage-dialog.component";
|
||||
import { WtfBackupComponent } from "../components/addons/wtf-backup/wtf-backup.component";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -38,6 +39,7 @@ import { AddonManageDialogComponent } from "../components/addons/addon-manage-di
|
||||
MyAddonsAddonCellComponent,
|
||||
InstallFromUrlDialogComponent,
|
||||
AddonManageDialogComponent,
|
||||
WtfBackupComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@@ -65,6 +67,7 @@ import { AddonManageDialogComponent } from "../components/addons/addon-manage-di
|
||||
MyAddonsAddonCellComponent,
|
||||
InstallFromUrlDialogComponent,
|
||||
AddonManageDialogComponent,
|
||||
WtfBackupComponent,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DatePipe } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { DownloadCountPipe } from "../pipes/download-count.pipe";
|
||||
import { GetAddonListItemFilePropPipe } from "../pipes/get-addon-list-item-file-prop.pipe";
|
||||
@@ -29,6 +30,6 @@ import { TrustHtmlPipe } from "../pipes/trust-html.pipe";
|
||||
InterfaceFormatPipe,
|
||||
InvertBoolPipe,
|
||||
],
|
||||
providers: [RelativeDurationPipe, GetAddonListItemFilePropPipe, DownloadCountPipe],
|
||||
providers: [RelativeDurationPipe, GetAddonListItemFilePropPipe, DownloadCountPipe, DatePipe],
|
||||
})
|
||||
export class PipesModule {}
|
||||
|
||||
@@ -33,7 +33,6 @@ import { TableContextHeaderCellComponent } from "../../components/addons/table-c
|
||||
import { GenericProviderError } from "../../errors";
|
||||
import { AddonSearchResult } from "../../models/wowup/addon-search-result";
|
||||
import { ColumnState } from "../../models/wowup/column-state";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { DownloadCountPipe } from "../../pipes/download-count.pipe";
|
||||
import { RelativeDurationPipe } from "../../pipes/relative-duration-pipe";
|
||||
import { ElectronService } from "../../services";
|
||||
@@ -46,6 +45,7 @@ import { WarcraftService } from "../../services/warcraft/warcraft.service";
|
||||
import { WowUpService } from "../../services/wowup/wowup.service";
|
||||
import { getEnumKeys } from "../../utils/enum.utils";
|
||||
import { camelToSnakeCase } from "../../utils/string.utils";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
|
||||
interface CategoryItem {
|
||||
category: AddonCategory;
|
||||
|
||||
@@ -22,7 +22,6 @@ import { PatchNotesDialogComponent } from "../../components/common/patch-notes-d
|
||||
import { AddonScanError } from "../../errors";
|
||||
import { AddonInstallState } from "../../models/wowup/addon-install-state";
|
||||
import { AddonUpdateEvent } from "../../models/wowup/addon-update-event";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { ElectronService } from "../../services";
|
||||
import { AddonService, ScanUpdate, ScanUpdateType } from "../../services/addons/addon.service";
|
||||
import { DialogFactory } from "../../services/dialog/dialog.factory";
|
||||
@@ -31,6 +30,7 @@ import { SnackbarService } from "../../services/snackbar/snackbar.service";
|
||||
import { WarcraftInstallationService } from "../../services/warcraft/warcraft-installation.service";
|
||||
import { WowUpService } from "../../services/wowup/wowup.service";
|
||||
import { getProtocol } from "../../utils/string.utils";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
|
||||
@Component({
|
||||
selector: "app-home",
|
||||
|
||||
@@ -297,6 +297,9 @@
|
||||
<button mat-menu-item (click)="onClickImportExport()">
|
||||
<span>Import/Export</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="onClickCreateBackup()">
|
||||
<span>Create Backup</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<!-- MULTI ADDON CONTEXT MENU -->
|
||||
|
||||
@@ -44,7 +44,7 @@ import { TableContextHeaderCellComponent } from "../../components/addons/table-c
|
||||
import { AddonInstallState } from "../../models/wowup/addon-install-state";
|
||||
import { AddonUpdateEvent } from "../../models/wowup/addon-update-event";
|
||||
import { ColumnState } from "../../models/wowup/column-state";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
import { RelativeDurationPipe } from "../../pipes/relative-duration-pipe";
|
||||
import { ElectronService } from "../../services";
|
||||
import { AddonService } from "../../services/addons/addon.service";
|
||||
@@ -61,6 +61,8 @@ import { SortOrder } from "../../models/wowup/sort-order";
|
||||
import { PushService } from "../../services/push/push.service";
|
||||
import { AddonUiService } from "../../services/addons/addon-ui.service";
|
||||
import { AddonManageDialogComponent } from "../../components/addons/addon-manage-dialog/addon-manage-dialog.component";
|
||||
import { WtfService } from "../../services/wtf/wtf.service";
|
||||
import { WtfBackupComponent } from "../../components/addons/wtf-backup/wtf-backup.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-my-addons",
|
||||
@@ -241,6 +243,7 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
private _snackbarService: SnackbarService,
|
||||
private _pushService: PushService,
|
||||
private _addonUiService: AddonUiService,
|
||||
private _wtfService: WtfService,
|
||||
public addonService: AddonService,
|
||||
public electronService: ElectronService,
|
||||
public overlay: Overlay,
|
||||
@@ -670,6 +673,15 @@ export class MyAddonsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
return data.addon.id;
|
||||
};
|
||||
|
||||
public onClickCreateBackup(): void {
|
||||
const dialogRef = this._dialogFactory.getDialog(WtfBackupComponent, {
|
||||
disableClose: true,
|
||||
data: {},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().pipe(first()).subscribe();
|
||||
}
|
||||
|
||||
public onClickImportExport(): void {
|
||||
const dialogRef = this._dialogFactory.getDialog(AddonManageDialogComponent, {
|
||||
disableClose: true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DatePipe } from "@angular/common";
|
||||
import { Pipe, PipeTransform } from "@angular/core";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
|
||||
@@ -5,9 +6,9 @@ import { TranslateService } from "@ngx-translate/core";
|
||||
name: "localeDate",
|
||||
})
|
||||
export class NgxDatePipe implements PipeTransform {
|
||||
public constructor(private translateService: TranslateService) {}
|
||||
public constructor(private translateService: TranslateService, private datePipe: DatePipe) {}
|
||||
|
||||
public transform(value: string | number): any {
|
||||
return new Date(value).toLocaleString(this.translateService.currentLang);
|
||||
public transform(value: string | number, pattern: string = "short"): any {
|
||||
return this.datePipe.transform(value, pattern, undefined, this.translateService.currentLang);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { nanoid } from "nanoid";
|
||||
|
||||
import { Addon } from "../../../common/entities/addon";
|
||||
import { WowClientType } from "../../../common/warcraft/wow-client-type";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
import { getEnumName } from "../../utils/enum.utils";
|
||||
import { AddonStorageService } from "../storage/addon-storage.service";
|
||||
import { WarcraftService } from "../warcraft/warcraft.service";
|
||||
@@ -129,28 +129,53 @@ export class AddonBrokerService {
|
||||
}
|
||||
|
||||
public async installImportSummary(importSummary: ImportSummary, installation: WowInstallation): Promise<void> {
|
||||
for (const comp of importSummary.comparisons) {
|
||||
if (comp.state !== "added") {
|
||||
continue;
|
||||
}
|
||||
const comps = importSummary.comparisons.filter((comp) => comp.state === "added");
|
||||
|
||||
try {
|
||||
await this._addonService.installBaseAddon(
|
||||
comp.imported.id,
|
||||
comp.imported.provider_name,
|
||||
installation,
|
||||
(installState, progress) => {
|
||||
this._addonInstallSrc.next({
|
||||
comparisonId: comp.id,
|
||||
installState,
|
||||
progress,
|
||||
});
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(`Failed to install imported addon`, e);
|
||||
}
|
||||
}
|
||||
const tasks = comps.map((comp) => {
|
||||
return (async (c) => {
|
||||
try {
|
||||
await this._addonService.installBaseAddon(
|
||||
c.imported.id,
|
||||
c.imported.provider_name,
|
||||
installation,
|
||||
(installState, progress) => {
|
||||
this._addonInstallSrc.next({
|
||||
comparisonId: c.id,
|
||||
installState,
|
||||
progress,
|
||||
});
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(`Failed to install imported addon`, e);
|
||||
}
|
||||
})(comp);
|
||||
});
|
||||
|
||||
await Promise.all(tasks);
|
||||
|
||||
// for (const comp of importSummary.comparisons) {
|
||||
// if (comp.state !== "added") {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// try {
|
||||
// await this._addonService.installBaseAddon(
|
||||
// comp.imported.id,
|
||||
// comp.imported.provider_name,
|
||||
// installation,
|
||||
// (installState, progress) => {
|
||||
// this._addonInstallSrc.next({
|
||||
// comparisonId: comp.id,
|
||||
// installState,
|
||||
// progress,
|
||||
// });
|
||||
// }
|
||||
// );
|
||||
// } catch (e) {
|
||||
// console.error(`Failed to install imported addon`, e);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public getImportSummary(exportPayload: ExportPayload, installation: WowInstallation): ImportSummary {
|
||||
|
||||
@@ -46,7 +46,7 @@ import { AddonSearchResultFile } from "../../models/wowup/addon-search-result-fi
|
||||
import { AddonUpdateEvent } from "../../models/wowup/addon-update-event";
|
||||
import { ProtocolSearchResult } from "../../models/wowup/protocol-search-result";
|
||||
import { Toc } from "../../models/wowup/toc";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
import * as AddonUtils from "../../utils/addon.utils";
|
||||
import { getEnumName } from "../../utils/enum.utils";
|
||||
import * as SearchResults from "../../utils/search-result.utils";
|
||||
|
||||
@@ -25,6 +25,7 @@ import { CopyFileRequest } from "../../../common/models/copy-file-request";
|
||||
import { UnzipRequest } from "../../../common/models/unzip-request";
|
||||
import { FsDirent, FsStats, TreeNode } from "../../../common/models/ipc-events";
|
||||
import { ElectronService } from "../electron/electron.service";
|
||||
import { GetDirectoryTreeOptions, GetDirectoryTreeRequest } from "../../../common/models/ipc-request";
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
@@ -127,8 +128,12 @@ export class FileService {
|
||||
return await this._electronService.invoke(IPC_LIST_DIR_RECURSIVE, dirPath);
|
||||
}
|
||||
|
||||
public async getDirectoryTree(dirPath: string): Promise<TreeNode> {
|
||||
return await this._electronService.invoke(IPC_GET_DIRECTORY_TREE, dirPath);
|
||||
public async getDirectoryTree(dirPath: string, opts?: GetDirectoryTreeOptions): Promise<TreeNode> {
|
||||
const request: GetDirectoryTreeRequest = {
|
||||
dirPath,
|
||||
opts,
|
||||
};
|
||||
return await this._electronService.invoke(IPC_GET_DIRECTORY_TREE, request);
|
||||
}
|
||||
|
||||
public async writeFile(sourcePath: string, contents: string): Promise<string> {
|
||||
@@ -155,6 +160,10 @@ export class FileService {
|
||||
return this._electronService.invoke<string[]>(IPC_LIST_FILES_CHANNEL, sourcePath, filter);
|
||||
}
|
||||
|
||||
public readFileInZip(zipPath: string, filePath: string) {
|
||||
return this._electronService.invoke<any>("zip-read-file", zipPath, filePath);
|
||||
}
|
||||
|
||||
public async unzipFile(zipFilePath: string, outputFolder: string): Promise<string> {
|
||||
console.log("unzipFile", zipFilePath);
|
||||
|
||||
@@ -166,4 +175,8 @@ export class FileService {
|
||||
|
||||
return await this._electronService.invoke(IPC_UNZIP_FILE_CHANNEL, request);
|
||||
}
|
||||
|
||||
public async zipFile(srcPath: string, destPath: string) {
|
||||
await this._electronService.invoke("zip-file", srcPath, destPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
faInfoCircle,
|
||||
faCodeBranch,
|
||||
faCaretDown,
|
||||
faExclamation,
|
||||
faExclamationTriangle,
|
||||
faCode,
|
||||
faCoins,
|
||||
@@ -31,6 +32,8 @@ import {
|
||||
faUserCircle,
|
||||
faEllipsisV,
|
||||
faCopy,
|
||||
faTrash,
|
||||
faHistory,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faQuestionCircle, faClock, faCheckCircle as farCheckCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
import { faDiscord, faGithub, faPatreon } from "@fortawesome/free-brands-svg-icons";
|
||||
@@ -75,6 +78,9 @@ export class IconService {
|
||||
this.addSvg(faEllipsisV);
|
||||
this.addSvg(faCopy);
|
||||
this.addSvg(farCheckCircle);
|
||||
this.addSvg(faExclamation);
|
||||
this.addSvg(faTrash);
|
||||
this.addSvg(faHistory);
|
||||
}
|
||||
|
||||
private addSvg(icon: IconDefinition): void {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { BehaviorSubject, Subject } from "rxjs";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { SELECTED_DETAILS_TAB_KEY, TAB_INDEX_SETTINGS } from "../../../common/constants";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
import { PreferenceStorageService } from "../storage/preference-storage.service";
|
||||
import { WarcraftInstallationService } from "../warcraft/warcraft-installation.service";
|
||||
import { ColumnState } from "../../models/wowup/column-state";
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from "../../../common/constants";
|
||||
import { WowClientType } from "../../../common/warcraft/wow-client-type";
|
||||
import { AddonChannelType } from "../../../common/wowup/models";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
import { getEnumName } from "../../utils/enum.utils";
|
||||
import { ElectronService } from "../electron/electron.service";
|
||||
import { FileService } from "../files/file.service";
|
||||
|
||||
@@ -11,7 +11,7 @@ import { InstalledProduct } from "../../models/warcraft/installed-product";
|
||||
import { ProductDb } from "../../models/warcraft/product-db";
|
||||
import { AddonFolder } from "../../models/wowup/addon-folder";
|
||||
import { SelectItem } from "../../models/wowup/select-item";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
import { getEnumList, getEnumName } from "../../utils/enum.utils";
|
||||
import { FileService } from "../files/file.service";
|
||||
import { PreferenceStorageService } from "../storage/preference-storage.service";
|
||||
|
||||
@@ -25,11 +25,11 @@ const CHANGELOGS: ChangeLog[] = [
|
||||
<li>Added the ability to remove addons from the details dialog on Get Addons tab (tellier-dev)</li>
|
||||
<li>
|
||||
<div>The first stage of a set of cloud based features you've been asking for</div>
|
||||
<img style="width: 80%;" loading="lazy" src="/assets/images/patch/account-beta.png">
|
||||
<img style="width: 70%; border: 1px solid #666666; border-radius: 4px;" loading="lazy" src="assets/images/patch/account-beta.png">
|
||||
</li>
|
||||
<li>
|
||||
<div>Added the ability to import/export a list of addons for a WoW client. The first step in backing up your list of addons or sharing them with your friends!</div>
|
||||
<img style="width: 80%;" loading="lazy" src="/assets/images/patch/import-export-preview.png">
|
||||
<img style="width: 70%; border: 1px solid #666666; border-radius: 4px;" loading="lazy" src="assets/images/patch/import-export-preview.png">
|
||||
</li>
|
||||
<li>Added WowUpHub category support</li>
|
||||
<li>Added WowUpHub preview support</li>
|
||||
@@ -79,7 +79,7 @@ const CHANGELOGS: ChangeLog[] = [
|
||||
<div>
|
||||
<h4 style="margin-top: 1em;">Fixes</h4>
|
||||
<ul>
|
||||
<li>Update Electron to 13.5.1 to fix the <a appExternalLink href="https://github.com/electron/electron/issues/31212">TukUI download issue</a></li>
|
||||
<li>Update Electron to 13.5.1 to fix the <a appExternalLink style="" href="https://github.com/electron/electron/issues/31212">TukUI download issue</a></li>
|
||||
</ul>
|
||||
</div>`,
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
ACCT_FEATURE_KEYS,
|
||||
ACCT_PUSH_ENABLED_KEY,
|
||||
APP_PROTOCOL_NAME,
|
||||
FEATURE_ACCOUNTS_ENABLED,
|
||||
IPC_PUSH_INIT,
|
||||
IPC_PUSH_REGISTER,
|
||||
IPC_PUSH_SUBSCRIBE,
|
||||
@@ -52,6 +53,10 @@ export class WowUpAccountService {
|
||||
private _preferenceStorageService: PreferenceStorageService,
|
||||
private _wowUpApiService: WowUpApiService
|
||||
) {
|
||||
if (!FEATURE_ACCOUNTS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.wowUpAuthTokenSrc
|
||||
.pipe(
|
||||
filter((token) => !!token && token.length > 10),
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import { Addon } from "../../../common/entities/addon";
|
||||
import { AddonChannelType } from "../../../common/wowup/models";
|
||||
import { AddonInstallState } from "../../models/wowup/addon-install-state";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
import { toInterfaceVersion } from "../../utils/addon.utils";
|
||||
import { AddonProviderFactory } from "../addons/addon.provider.factory";
|
||||
import { AddonService } from "../addons/addon.service";
|
||||
|
||||
@@ -61,6 +61,7 @@ export class WowUpService {
|
||||
public readonly applicationLogsFolderPath: string = window.logPath;
|
||||
public readonly applicationDownloadsFolderPath: string = join(this.applicationFolderPath, "downloads");
|
||||
public readonly applicationUpdaterPath: string = join(this.applicationFolderPath, this.updaterName);
|
||||
public readonly wtfBackupFolder: string = join(this.applicationFolderPath, "wtf_backups");
|
||||
|
||||
public readonly preferenceChange$ = this._preferenceChangeSrc.asObservable();
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import * as path from "path";
|
||||
import { ElectronService } from "..";
|
||||
|
||||
import { Addon } from "../../../common/entities/addon";
|
||||
import { FsStats, TreeNode } from "../../../common/models/ipc-events";
|
||||
import { WowInstallation } from "../../models/wowup/wow-installation";
|
||||
import { BackupGetExistingRequest } from "../../../common/models/ipc-request";
|
||||
import { BackupGetExistingResponse } from "../../../common/models/ipc-response";
|
||||
import { WowInstallation } from "../../../common/warcraft/wow-installation";
|
||||
import { FileService } from "../files/file.service";
|
||||
import { WowUpService } from "../wowup/wowup.service";
|
||||
|
||||
const WTF_FOLDER = "WTF";
|
||||
const ACCOUNT_FOLDER = "Account";
|
||||
const SAVED_VARIABLES_FOLDER = "SavedVariables";
|
||||
const BACKUP_META_FILENAME = "wowup-meta.json";
|
||||
|
||||
export interface FileStats {
|
||||
name: string;
|
||||
@@ -27,15 +32,42 @@ export interface WtfNode extends TreeNode {
|
||||
children: WtfNode[];
|
||||
}
|
||||
|
||||
export interface WtfBackupMetadataFile {
|
||||
createdBy: string;
|
||||
createdAt: number;
|
||||
contents: WtfBackupMeta[];
|
||||
}
|
||||
|
||||
export interface WtfBackupMeta {
|
||||
path: string;
|
||||
isDirectory: boolean;
|
||||
size: number;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export interface WtfBackup {
|
||||
location: string;
|
||||
fileName: string;
|
||||
size: number;
|
||||
birthtimeMs: number;
|
||||
error?: string;
|
||||
metadata?: WtfBackupMetadataFile;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
})
|
||||
export class WtfService {
|
||||
public constructor(private _fileService: FileService) {}
|
||||
public constructor(
|
||||
private _fileService: FileService,
|
||||
private _electronService: ElectronService,
|
||||
private _wowUpService: WowUpService
|
||||
) {}
|
||||
|
||||
public async getWtfContents(installation: WowInstallation): Promise<WtfNode> {
|
||||
// including the hash will make this operation much slower
|
||||
public async getWtfContents(installation: WowInstallation, includeHash = false): Promise<WtfNode> {
|
||||
const wtfPath = this.getWtfPath(installation);
|
||||
const tree = await this._fileService.getDirectoryTree(wtfPath);
|
||||
const tree = await this._fileService.getDirectoryTree(wtfPath, { includeHash });
|
||||
return this.getWtfNode(tree);
|
||||
}
|
||||
|
||||
@@ -136,4 +168,139 @@ export class WtfService {
|
||||
|
||||
return fsStats;
|
||||
}
|
||||
|
||||
public async getBackupList(installation: WowInstallation): Promise<WtfBackup[]> {
|
||||
const wtfBackups: WtfBackup[] = [];
|
||||
|
||||
const backupZipFiles = await this.listBackupFiles(installation);
|
||||
const fsStats = await this._fileService.statFiles(backupZipFiles);
|
||||
|
||||
for (let i = 0; i < backupZipFiles.length; i++) {
|
||||
const zipFile = backupZipFiles[i];
|
||||
const stat = fsStats[zipFile];
|
||||
|
||||
const wtfBackup: WtfBackup = {
|
||||
location: zipFile,
|
||||
fileName: path.basename(zipFile),
|
||||
size: stat.size,
|
||||
birthtimeMs: stat.birthtimeMs,
|
||||
};
|
||||
|
||||
try {
|
||||
const zipMetaTxt = await this._fileService.readFileInZip(zipFile, BACKUP_META_FILENAME);
|
||||
const zipMetaData: WtfBackupMetadataFile = JSON.parse(zipMetaTxt);
|
||||
|
||||
if (!Array.isArray(zipMetaData.contents)) {
|
||||
wtfBackup.error = "INVALID_CONTENTS";
|
||||
} else if (typeof zipMetaData.createdAt !== "number") {
|
||||
wtfBackup.error = "INVALID_CREATED_AT";
|
||||
} else if (typeof zipMetaData.createdBy !== "string") {
|
||||
wtfBackup.error = "INVALID_CREATED_BY";
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to process backup metadata", zipFile, e);
|
||||
wtfBackup.error = "GENERIC_ERROR";
|
||||
} finally {
|
||||
wtfBackups.push(wtfBackup);
|
||||
}
|
||||
}
|
||||
|
||||
return wtfBackups;
|
||||
}
|
||||
|
||||
public async createBackup(installation: WowInstallation): Promise<void> {
|
||||
await this.createBackupDirectory(installation);
|
||||
|
||||
const metadataFilePath = await this.createBackupMetadataFile(installation);
|
||||
|
||||
try {
|
||||
await this.createBackupZip(installation);
|
||||
} finally {
|
||||
// always delete the metadata file
|
||||
await this._fileService.remove(metadataFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
private async createBackupZip(installation: WowInstallation): Promise<void> {
|
||||
const wtfPath = this.getWtfPath(installation);
|
||||
const zipPath = path.join(this.getBackupPath(installation), `wtf_${Date.now()}.zip`);
|
||||
this._fileService.zipFile(wtfPath, zipPath);
|
||||
}
|
||||
|
||||
private async createBackupDirectory(installation: WowInstallation): Promise<void> {
|
||||
const backupPath = this.getBackupPath(installation);
|
||||
await this._fileService.createDirectory(backupPath);
|
||||
}
|
||||
|
||||
private async createBackupMetadataFile(installation: WowInstallation): Promise<string> {
|
||||
const wtfTree = await this.getWtfContents(installation, true);
|
||||
const wtfList = this.flattenTree([wtfTree]);
|
||||
console.debug(wtfList);
|
||||
|
||||
const backupMetadata: WtfBackupMetadataFile = {
|
||||
contents: this.toBackupMeta(wtfList, installation),
|
||||
createdAt: Date.now(),
|
||||
createdBy: "manual",
|
||||
};
|
||||
|
||||
return await this.writeWtfMetadataFile(backupMetadata, installation);
|
||||
}
|
||||
|
||||
private async writeWtfMetadataFile(
|
||||
backupMetadata: WtfBackupMetadataFile,
|
||||
installation: WowInstallation
|
||||
): Promise<string> {
|
||||
const wtfPath = this.getWtfPath(installation);
|
||||
|
||||
const metaPath = path.join(wtfPath, BACKUP_META_FILENAME);
|
||||
await this._fileService.writeFile(metaPath, JSON.stringify(backupMetadata, null, 2));
|
||||
|
||||
return metaPath;
|
||||
}
|
||||
|
||||
private async getBackup(installation: WowInstallation): Promise<BackupGetExistingResponse> {
|
||||
const args: BackupGetExistingRequest = {
|
||||
backupPath: this._wowUpService.wtfBackupFolder,
|
||||
installation,
|
||||
};
|
||||
return await this._electronService.invoke("backup-get-existing", args);
|
||||
}
|
||||
|
||||
private async listBackupFiles(installation: WowInstallation) {
|
||||
const backupPath = this.getBackupPath(installation);
|
||||
const zipFiles = await this._fileService.listFiles(backupPath, "*.zip");
|
||||
return zipFiles.map((f) => path.join(backupPath, f));
|
||||
}
|
||||
|
||||
private async createFullBackup(installation: WowInstallation): Promise<void> {}
|
||||
|
||||
private async backupNode(node: WtfNode, installation: WowInstallation) {
|
||||
const wtfPath = this.getWtfPath(installation);
|
||||
const nodeBase = node.path.replace(wtfPath, "");
|
||||
}
|
||||
|
||||
public getBackupPath(installation: WowInstallation): string {
|
||||
return path.join(this._wowUpService.wtfBackupFolder, installation.id);
|
||||
}
|
||||
|
||||
private flattenTree(nodes: WtfNode[]): WtfNode[] {
|
||||
return Array.prototype.concat.apply(
|
||||
nodes,
|
||||
nodes.map((n) => this.flattenTree(n.children))
|
||||
);
|
||||
}
|
||||
|
||||
private toBackupMeta(nodes: WtfNode[], installation: WowInstallation): WtfBackupMeta[] {
|
||||
const wtfPath = this.getWtfPath(installation);
|
||||
|
||||
return nodes.map((n) => {
|
||||
const nodeBase = n.path.replace(wtfPath, "");
|
||||
return {
|
||||
hash: n.hash,
|
||||
isDirectory: n.isDirectory,
|
||||
path: nodeBase,
|
||||
size: n.size,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,22 @@
|
||||
"RESET_BUTTON": "Reset",
|
||||
"VERSION_MISMATCH": "This addon is already installed, but the versions do not match"
|
||||
},
|
||||
"WTF_BACKUP": {
|
||||
"DIALOG_TITLE": "WTF Settings Backup: {clientType}",
|
||||
"CREATE_BACKUP_BUTTON": "Create Backup",
|
||||
"SHOW_FOLDER_BUTTON": "Show Folder",
|
||||
"BACKUP_COUNT_TEXT": "Found {count} {count, plural, =1{backup} other{backups}}",
|
||||
"TOOL_TIP": {
|
||||
"APPLY_BUTTON": "Apply this backup",
|
||||
"DELETE_BUTTON": "Delete this backup"
|
||||
},
|
||||
"ERROR": {
|
||||
"INVALID_CONTENTS": "There was an issue processing this backup",
|
||||
"INVALID_CREATED_AT": "There was an issue processing this backup",
|
||||
"INVALID_CREATED_BY": "There was an issue processing this backup",
|
||||
"GENERIC_ERROR": "There was an issue processing this backup"
|
||||
}
|
||||
},
|
||||
"APP": {
|
||||
"APP_MENU": {
|
||||
"EDIT": {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
export const APP_USER_MODEL_ID = "io.wowup.jliddev"; // Bundle ID
|
||||
|
||||
// FEATURES
|
||||
export const FEATURE_ACCOUNTS_ENABLED = false;
|
||||
|
||||
export const ADDON_PROVIDER_WOWINTERFACE = "WowInterface";
|
||||
export const ADDON_PROVIDER_CURSEFORGE = "Curse";
|
||||
export const ADDON_PROVIDER_GITHUB = "GitHub";
|
||||
|
||||
@@ -44,4 +44,5 @@ export interface TreeNode {
|
||||
isDirectory: boolean;
|
||||
children: TreeNode[];
|
||||
size: number;
|
||||
hash?: string;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
import { WowInstallation } from "../warcraft/wow-installation";
|
||||
|
||||
export interface IpcRequest {
|
||||
responseKey: string;
|
||||
}
|
||||
|
||||
export interface BackupGetExistingRequest {
|
||||
backupPath: string;
|
||||
installation: WowInstallation;
|
||||
}
|
||||
|
||||
export interface BackupCreateRequest {
|
||||
backupPath: string;
|
||||
installation: WowInstallation;
|
||||
}
|
||||
|
||||
export interface GetDirectoryTreeOptions {
|
||||
includeHash?: boolean;
|
||||
}
|
||||
|
||||
export interface GetDirectoryTreeRequest {
|
||||
dirPath: string;
|
||||
opts?: GetDirectoryTreeOptions;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
export interface IpcResponse {
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export interface BackupGetExistingResponse {
|
||||
exists: boolean;
|
||||
}
|
||||
|
||||
export interface BackupCreateResponse {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WowClientType } from "../../../common/warcraft/wow-client-type";
|
||||
import { AddonChannelType } from "../../../common/wowup/models";
|
||||
import { WowClientType } from "./wow-client-type";
|
||||
import { AddonChannelType } from "../wowup/models";
|
||||
|
||||
export interface WowInstallation {
|
||||
id: string;
|
||||
6
wowup-electron/src/common/wowup.d.ts
vendored
6
wowup-electron/src/common/wowup.d.ts
vendored
@@ -38,6 +38,8 @@ declare type RendererChannels =
|
||||
| "curse-get-scan-results"
|
||||
| "wowup-get-scan-results"
|
||||
| "unzip-file"
|
||||
| "zip-file"
|
||||
| "zip-read-file"
|
||||
| "copy-file"
|
||||
| "delete-directory"
|
||||
| "list-disks-win32"
|
||||
@@ -79,7 +81,9 @@ declare type RendererChannels =
|
||||
| "push-init"
|
||||
| "push-register"
|
||||
| "push-unregister"
|
||||
| "push-subscribe";
|
||||
| "push-subscribe"
|
||||
| "backup-get-existing"
|
||||
| "backup-create";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
||||
@@ -535,6 +535,11 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.changelog {
|
||||
a {
|
||||
color: var(--control-color);
|
||||
}
|
||||
}
|
||||
.tab-icon-inactive {
|
||||
svg {
|
||||
transition: fill 0.3s ease 0.1s;
|
||||
|
||||
@@ -162,6 +162,9 @@ img {
|
||||
margin-top: 1em !important;
|
||||
}
|
||||
|
||||
.mb-0 {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.mb-1 {
|
||||
margin-bottom: 0.25em !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user