mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Electron wrapper refactor
This commit is contained in:
parent
57678664d4
commit
3e0f00b077
@ -1,6 +1,5 @@
|
||||
artwork/*
|
||||
build/*
|
||||
electron/*
|
||||
gulp/*
|
||||
node_modules/*
|
||||
res/*
|
||||
|
@ -1,48 +1,55 @@
|
||||
/* eslint-disable quotes,no-undef */
|
||||
|
||||
const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell, dialog, session } = require("electron");
|
||||
const { app, BrowserWindow, Menu, ipcMain, shell, session } = require("electron");
|
||||
const { initializeMenu } = require("./src/menu");
|
||||
const { initializeSwitches, isLocal, isDev, shouldHideDevtools } = require("./src/switches");
|
||||
const { initializeFolders } = require("./src/folders");
|
||||
const { initializeCrashLogs } = require("./src/crashlogs");
|
||||
const { initializeFilesystem } = require("./src/filesystem");
|
||||
const { showMissingExternalMods, initializeMods, showModErrors } = require("./src/mods");
|
||||
const { initializeSteam } = require("./src/steam");
|
||||
const path = require("path");
|
||||
const url = require("url");
|
||||
const fs = require("fs");
|
||||
const steam = require("./steam");
|
||||
const asyncLock = require("async-lock");
|
||||
const windowStateKeeper = require("electron-window-state");
|
||||
|
||||
// Disable hardware key handling, i.e. being able to pause/resume the game music
|
||||
// with hardware keys
|
||||
app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling");
|
||||
|
||||
const isDev = app.commandLine.hasSwitch("dev");
|
||||
const isLocal = app.commandLine.hasSwitch("local");
|
||||
const safeMode = app.commandLine.hasSwitch("safe-mode");
|
||||
const externalMod = app.commandLine.getSwitchValue("load-mod");
|
||||
|
||||
const roamingFolder =
|
||||
process.env.APPDATA ||
|
||||
(process.platform == "darwin"
|
||||
? process.env.HOME + "/Library/Preferences"
|
||||
: process.env.HOME + "/.local/share");
|
||||
|
||||
let storePath = path.join(roamingFolder, "shapez.io", "saves");
|
||||
let modsPath = path.join(roamingFolder, "shapez.io", "mods");
|
||||
|
||||
if (!fs.existsSync(storePath)) {
|
||||
// No try-catch by design
|
||||
fs.mkdirSync(storePath, { recursive: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(modsPath)) {
|
||||
fs.mkdirSync(modsPath, { recursive: true });
|
||||
}
|
||||
|
||||
/** @type {BrowserWindow} */
|
||||
let win = null;
|
||||
let menu = null;
|
||||
|
||||
function createWindow() {
|
||||
let faviconExtension = ".png";
|
||||
async function onReady() {
|
||||
// Show external mod errors before we open anything
|
||||
await showMissingExternalMods();
|
||||
|
||||
// Create a new in-memory session
|
||||
const appSession = session.fromPartition("default");
|
||||
win = createWindow(appSession);
|
||||
|
||||
win.once("ready-to-show", () => {
|
||||
win.show();
|
||||
|
||||
if (isDev && !shouldHideDevtools) {
|
||||
// Show developer tools initially
|
||||
win.webContents.toggleDevTools();
|
||||
}
|
||||
});
|
||||
win.on("closed", () => (win = null));
|
||||
|
||||
ipcMain.on("set-fullscreen", (_, flag) => win.setFullScreen(flag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a URL in external browser if it's HTTPS, does
|
||||
* nothing if it isn't.
|
||||
* @param {string} url
|
||||
*/
|
||||
function openSecureURL(url) {
|
||||
if (!url.startsWith("https://")) {
|
||||
return;
|
||||
}
|
||||
|
||||
return shell.openExternal(url);
|
||||
}
|
||||
|
||||
function createWindow(appSession) {
|
||||
let faviconName = "favicon.png";
|
||||
if (process.platform === "win32") {
|
||||
faviconExtension = ".ico";
|
||||
faviconName = "favicon.ico";
|
||||
}
|
||||
|
||||
const mainWindowState = windowStateKeeper({
|
||||
@ -50,169 +57,77 @@ function createWindow() {
|
||||
defaultHeight: 800,
|
||||
});
|
||||
|
||||
win = new BrowserWindow({
|
||||
const window = new BrowserWindow({
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
show: false,
|
||||
backgroundColor: "#222428",
|
||||
useContentSize: false,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
title: "shapez.io Standalone",
|
||||
transparent: false,
|
||||
icon: path.join(__dirname, "favicon" + faviconExtension),
|
||||
// fullscreen: true,
|
||||
icon: path.join(__dirname, faviconName),
|
||||
autoHideMenuBar: !isDev,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
nodeIntegrationInSubFrames: false,
|
||||
contextIsolation: true,
|
||||
enableRemoteModule: false,
|
||||
disableBlinkFeatures: "Auxclick",
|
||||
|
||||
webSecurity: true,
|
||||
sandbox: true,
|
||||
preload: path.join(__dirname, "preload.js"),
|
||||
experimentalFeatures: false,
|
||||
preload: path.join(__dirname, "src/preload.js"),
|
||||
session: appSession,
|
||||
},
|
||||
allowRunningInsecureContent: false,
|
||||
});
|
||||
|
||||
mainWindowState.manage(win);
|
||||
mainWindowState.manage(window);
|
||||
|
||||
if (isLocal) {
|
||||
win.loadURL("http://localhost:3005");
|
||||
window.loadURL("http://localhost:3005");
|
||||
} else {
|
||||
win.loadURL(
|
||||
url.format({
|
||||
pathname: path.join(__dirname, "index.html"),
|
||||
protocol: "file:",
|
||||
slashes: true,
|
||||
})
|
||||
);
|
||||
window.loadFile("index.html");
|
||||
}
|
||||
win.webContents.session.clearCache();
|
||||
win.webContents.session.clearStorageData();
|
||||
|
||||
////// SECURITY
|
||||
|
||||
// Disable permission requests
|
||||
win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
|
||||
callback(false);
|
||||
});
|
||||
session.fromPartition("default").setPermissionRequestHandler((webContents, permission, callback) => {
|
||||
window.webContents.session.setPermissionRequestHandler((_webContents, _permission, callback) => {
|
||||
callback(false);
|
||||
});
|
||||
|
||||
app.on("web-contents-created", (event, contents) => {
|
||||
// Disable vewbiew
|
||||
contents.on("will-attach-webview", (event, webPreferences, params) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
// Disable navigation
|
||||
contents.on("will-navigate", (event, navigationUrl) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
win.webContents.on("will-redirect", (contentsEvent, navigationUrl) => {
|
||||
// Log and prevent the app from redirecting to a new page
|
||||
console.error(
|
||||
`The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.`
|
||||
);
|
||||
contentsEvent.preventDefault();
|
||||
});
|
||||
|
||||
// Filter loading any module via remote;
|
||||
// you shouldn't be using remote at all, though
|
||||
// https://electronjs.org/docs/tutorial/security#16-filter-the-remote-module
|
||||
app.on("remote-require", (event, webContents, moduleName) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// built-ins are modules such as "app"
|
||||
app.on("remote-get-builtin", (event, webContents, moduleName) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
app.on("remote-get-global", (event, webContents, globalName) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
app.on("remote-get-current-window", (event, webContents) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
app.on("remote-get-current-web-contents", (event, webContents) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
//// END SECURITY
|
||||
|
||||
win.webContents.on("new-window", (event, pth) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (pth.startsWith("https://")) {
|
||||
shell.openExternal(pth);
|
||||
window.webContents.on("will-navigate", (event, url) => {
|
||||
if (url == window.webContents.getURL()) {
|
||||
// Allow location.reload()
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
openSecureURL(url);
|
||||
});
|
||||
|
||||
win.on("closed", () => {
|
||||
console.log("Window closed");
|
||||
win = null;
|
||||
window.webContents.on("will-redirect", event => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
window.webContents.setWindowOpenHandler(({ url }) => {
|
||||
openSecureURL(url);
|
||||
return { action: "deny" };
|
||||
});
|
||||
|
||||
if (isDev) {
|
||||
menu = new Menu();
|
||||
|
||||
win.webContents.toggleDevTools();
|
||||
|
||||
const mainItem = new MenuItem({
|
||||
label: "Toggle Dev Tools",
|
||||
click: () => win.webContents.toggleDevTools(),
|
||||
accelerator: "F12",
|
||||
});
|
||||
menu.append(mainItem);
|
||||
|
||||
const reloadItem = new MenuItem({
|
||||
label: "Reload",
|
||||
click: () => win.reload(),
|
||||
accelerator: "F5",
|
||||
});
|
||||
menu.append(reloadItem);
|
||||
|
||||
const fullscreenItem = new MenuItem({
|
||||
label: "Fullscreen",
|
||||
click: () => win.setFullScreen(!win.isFullScreen()),
|
||||
accelerator: "F11",
|
||||
});
|
||||
menu.append(fullscreenItem);
|
||||
|
||||
const mainMenu = new Menu();
|
||||
mainMenu.append(
|
||||
new MenuItem({
|
||||
label: "shapez.io",
|
||||
submenu: menu,
|
||||
})
|
||||
);
|
||||
|
||||
Menu.setApplicationMenu(mainMenu);
|
||||
initializeMenu(window);
|
||||
} else {
|
||||
Menu.setApplicationMenu(null);
|
||||
}
|
||||
|
||||
win.once("ready-to-show", () => {
|
||||
win.show();
|
||||
win.focus();
|
||||
});
|
||||
return window;
|
||||
}
|
||||
|
||||
initializeSwitches();
|
||||
initializeFolders();
|
||||
initializeCrashLogs();
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.exit(0);
|
||||
} else {
|
||||
// Already running
|
||||
app.exit();
|
||||
}
|
||||
|
||||
app.on("ready", onReady);
|
||||
app.on("second-instance", () => {
|
||||
// Someone tried to run a second instance, we should focus
|
||||
if (win) {
|
||||
@ -222,168 +137,13 @@ if (!app.requestSingleInstanceLock()) {
|
||||
win.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
app.on("ready", createWindow);
|
||||
|
||||
app.on("window-all-closed", () => {
|
||||
console.log("All windows closed");
|
||||
app.quit();
|
||||
ipcMain.on("restart-app", () => {
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
});
|
||||
ipcMain.on("exit-app", () => app.quit());
|
||||
|
||||
ipcMain.on("set-fullscreen", (event, flag) => {
|
||||
win.setFullScreen(flag);
|
||||
});
|
||||
|
||||
ipcMain.on("exit-app", () => {
|
||||
win.close();
|
||||
app.quit();
|
||||
});
|
||||
|
||||
let renameCounter = 1;
|
||||
|
||||
const fileLock = new asyncLock({
|
||||
timeout: 30000,
|
||||
maxPending: 1000,
|
||||
});
|
||||
|
||||
function niceFileName(filename) {
|
||||
return filename.replace(storePath, "@");
|
||||
}
|
||||
|
||||
async function writeFileSafe(filename, contents) {
|
||||
++renameCounter;
|
||||
const prefix = "[ " + renameCounter + ":" + niceFileName(filename) + " ] ";
|
||||
const transactionId = String(new Date().getTime()) + "." + renameCounter;
|
||||
|
||||
if (fileLock.isBusy()) {
|
||||
console.warn(prefix, "Concurrent write process on", filename);
|
||||
}
|
||||
|
||||
fileLock.acquire(filename, async () => {
|
||||
console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId);
|
||||
|
||||
if (!fs.existsSync(filename)) {
|
||||
// this one is easy
|
||||
console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename));
|
||||
await fs.promises.writeFile(filename, contents, "utf8");
|
||||
return;
|
||||
}
|
||||
|
||||
// first, write a temporary file (.tmp-XXX)
|
||||
const tempName = filename + ".tmp-" + transactionId;
|
||||
console.log(prefix, "Writing temporary file", niceFileName(tempName));
|
||||
await fs.promises.writeFile(tempName, contents, "utf8");
|
||||
|
||||
// now, rename the original file to (.backup-XXX)
|
||||
const oldTemporaryName = filename + ".backup-" + transactionId;
|
||||
console.log(
|
||||
prefix,
|
||||
"Renaming old file",
|
||||
niceFileName(filename),
|
||||
"to",
|
||||
niceFileName(oldTemporaryName)
|
||||
);
|
||||
await fs.promises.rename(filename, oldTemporaryName);
|
||||
|
||||
// now, rename the temporary file (.tmp-XXX) to the target
|
||||
console.log(
|
||||
prefix,
|
||||
"Renaming the temporary file",
|
||||
niceFileName(tempName),
|
||||
"to the original",
|
||||
niceFileName(filename)
|
||||
);
|
||||
await fs.promises.rename(tempName, filename);
|
||||
|
||||
// we are done now, try to create a backup, but don't fail if the backup fails
|
||||
try {
|
||||
// check if there is an old backup file
|
||||
const backupFileName = filename + ".backup";
|
||||
if (fs.existsSync(backupFileName)) {
|
||||
console.log(prefix, "Deleting old backup file", niceFileName(backupFileName));
|
||||
// delete the old backup
|
||||
await fs.promises.unlink(backupFileName);
|
||||
}
|
||||
|
||||
// rename the old file to the new backup file
|
||||
console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location");
|
||||
await fs.promises.rename(oldTemporaryName, backupFileName);
|
||||
} catch (ex) {
|
||||
console.error(prefix, "Failed to switch backup files:", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ipcMain.handle("fs-job", async (event, job) => {
|
||||
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/gi, "_");
|
||||
const fname = path.join(storePath, filenameSafe);
|
||||
switch (job.type) {
|
||||
case "read": {
|
||||
if (!fs.existsSync(fname)) {
|
||||
// Special FILE_NOT_FOUND error code
|
||||
return { error: "file_not_found" };
|
||||
}
|
||||
return await fs.promises.readFile(fname, "utf8");
|
||||
}
|
||||
case "write": {
|
||||
await writeFileSafe(fname, job.contents);
|
||||
return job.contents;
|
||||
}
|
||||
|
||||
case "delete": {
|
||||
await fs.promises.unlink(fname);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error("Unknown fs job: " + job.type);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("open-mods-folder", async () => {
|
||||
shell.openPath(modsPath);
|
||||
});
|
||||
|
||||
console.log("Loading mods ...");
|
||||
|
||||
function loadMods() {
|
||||
if (safeMode) {
|
||||
console.log("Safe Mode enabled for mods, skipping mod search");
|
||||
}
|
||||
console.log("Loading mods from", modsPath);
|
||||
let modFiles = safeMode
|
||||
? []
|
||||
: fs
|
||||
.readdirSync(modsPath)
|
||||
.filter(filename => filename.endsWith(".js"))
|
||||
.map(filename => path.join(modsPath, filename));
|
||||
|
||||
if (externalMod) {
|
||||
console.log("Adding external mod source:", externalMod);
|
||||
const externalModPaths = externalMod.split(",");
|
||||
modFiles = modFiles.concat(externalModPaths);
|
||||
}
|
||||
|
||||
return modFiles.map(filename => fs.readFileSync(filename, "utf8"));
|
||||
}
|
||||
|
||||
let mods = [];
|
||||
try {
|
||||
mods = loadMods();
|
||||
console.log("Loaded", mods.length, "mods");
|
||||
} catch (ex) {
|
||||
console.error("Failed to load mods");
|
||||
dialog.showErrorBox("Failed to load mods:", ex);
|
||||
}
|
||||
|
||||
ipcMain.handle("get-mods", async () => {
|
||||
return mods;
|
||||
});
|
||||
|
||||
steam.init(isDev);
|
||||
|
||||
// Only allow achievements and puzzle DLC if no mods are loaded
|
||||
if (mods.length === 0) {
|
||||
steam.listen();
|
||||
}
|
||||
initializeFilesystem();
|
||||
initializeMods();
|
||||
initializeSteam();
|
||||
|
7
electron/jsconfig.json
Normal file
7
electron/jsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"module": "CommonJS"
|
||||
},
|
||||
"include": ["./src/**/*.js", "index.js"]
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
Here you can place mods. Every mod should be a single file ending with ".js".
|
||||
|
||||
--- WARNING ---
|
||||
Mods can potentially access to your filesystem.
|
||||
Please only install mods from trusted sources and developers.
|
||||
--- WARNING ---
|
@ -9,13 +9,14 @@
|
||||
"startDevGpu": "electron --enable-gpu-rasterization --enable-accelerated-2d-canvas --num-raster-threads=8 --enable-zero-copy . --dev --local",
|
||||
"start": "electron --disable-direct-composition --in-process-gpu ."
|
||||
},
|
||||
"devDependencies": {},
|
||||
"devDependencies": {
|
||||
"electron": "^18.3.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"shapez.io-private-artifacts": "github:tobspr/shapez.io-private-artifacts#abi-v99"
|
||||
},
|
||||
"dependencies": {
|
||||
"async-lock": "^1.2.8",
|
||||
"electron": "16.0.7",
|
||||
"electron-window-state": "^5.0.3"
|
||||
}
|
||||
}
|
||||
|
57
electron/src/crashlogs.js
Normal file
57
electron/src/crashlogs.js
Normal file
@ -0,0 +1,57 @@
|
||||
const { app, ipcMain, shell } = require("electron");
|
||||
const { writeFile } = require("fs/promises");
|
||||
const path = require("path");
|
||||
const { crashLogsDir } = require("./folders");
|
||||
|
||||
/**
|
||||
* Writes a crash log and reveals it in file manager.
|
||||
* @param {string} errorStack Stacktrace from renderer
|
||||
*/
|
||||
async function writeCrashLog(errorStack) {
|
||||
const separator = `\n${"=".repeat(20)}\n\n`;
|
||||
let contents = errorStack + separator;
|
||||
|
||||
contents += "GPU Features:\n";
|
||||
|
||||
const gpuFeatures = Object.entries(app.getGPUFeatureStatus());
|
||||
for (const [feature, status] of gpuFeatures) {
|
||||
contents += `${feature}:\t${status}`;
|
||||
}
|
||||
|
||||
const gpuInfo = await app.getGPUInfo("basic");
|
||||
contents += separator;
|
||||
|
||||
for (const gpu of gpuInfo.gpuDevice) {
|
||||
contents += "GPU Active: " + (gpu.active ? "Yes" : "No");
|
||||
contents += "Vendor: 0x" + gpu.vendorId.toString(16);
|
||||
contents += "Device: 0x" + gpu.deviceId.toString(16);
|
||||
contents += "\n";
|
||||
}
|
||||
|
||||
const date = new Date().toISOString();
|
||||
contents += separator + "Date: " + date;
|
||||
|
||||
const target = path.join(crashLogsDir, date + ".log");
|
||||
await writeFile(target, contents, "utf-8");
|
||||
console.log("Wrote crash log to", path.basename(target));
|
||||
|
||||
shell.showItemInFolder(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup an IPC handler to write and reveal crash logs as soon
|
||||
* as an error occurs.
|
||||
*/
|
||||
function initializeCrashLogs() {
|
||||
ipcMain.on("write-crash-log", (_, stack) => {
|
||||
// Write crash logs if we've been told to
|
||||
writeCrashLog(stack);
|
||||
});
|
||||
|
||||
// Also attempt to catch main process errors
|
||||
process.on("uncaughtException", error => {
|
||||
writeCrashLog(error.stack);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { initializeCrashLogs };
|
107
electron/src/filesystem.js
Normal file
107
electron/src/filesystem.js
Normal file
@ -0,0 +1,107 @@
|
||||
const AsyncLock = require("async-lock");
|
||||
const { ipcMain, shell } = require("electron");
|
||||
const { existsSync } = require("fs");
|
||||
const { unlink, readFile, writeFile, rename } = require("fs/promises");
|
||||
const path = require("path");
|
||||
const { savesDir } = require("./folders");
|
||||
|
||||
let renameCounter = 1;
|
||||
|
||||
const fileLock = new AsyncLock({
|
||||
timeout: 30000,
|
||||
maxPending: 1000,
|
||||
});
|
||||
|
||||
/**
|
||||
* Generic handler for FS jobs.
|
||||
* @param {{ type: "read"|"write"|"delete"|"reveal", filename: string, contents?: any }} job
|
||||
*/
|
||||
async function onFilesystemJob(_, job) {
|
||||
const safeFileName = sanitizeFileName(job.filename);
|
||||
const filePath = path.join(savesDir, safeFileName);
|
||||
|
||||
switch (job.type) {
|
||||
case "read":
|
||||
if (!existsSync(filePath)) {
|
||||
// Notify the renderer
|
||||
return { error: "file_not_found" };
|
||||
}
|
||||
|
||||
return await readFile(filePath, "utf-8");
|
||||
case "write":
|
||||
await writeFileSafe(filePath, job.contents);
|
||||
return job.contents;
|
||||
case "delete":
|
||||
await unlink(filePath);
|
||||
return;
|
||||
case "reveal":
|
||||
shell.showItemInFolder(filePath);
|
||||
return;
|
||||
default:
|
||||
throw new Error("Unknown FS job: " + job.type);
|
||||
}
|
||||
}
|
||||
|
||||
async function writeFileSafe(file, contents) {
|
||||
renameCounter++;
|
||||
const prefix = `[ ${renameCounter}:${path.basename(file)} ] `;
|
||||
const transactionId = Date.now() + "." + renameCounter;
|
||||
|
||||
if (fileLock.isBusy()) {
|
||||
console.warn(prefix, "Concurrent write process on", file);
|
||||
}
|
||||
|
||||
fileLock.acquire(file, async () => {
|
||||
console.log(prefix, "Starting write in transaction", transactionId);
|
||||
|
||||
if (!existsSync(file)) {
|
||||
// This one is easy - write directly
|
||||
console.log(prefix, "Creating a new file");
|
||||
await writeFile(file, contents, "utf-8");
|
||||
return;
|
||||
}
|
||||
|
||||
// First, write a temporary file (.tmp-XXX)
|
||||
const tempName = file + ".tmp-" + transactionId;
|
||||
console.log(prefix, "Writing temporary file", path.basename(tempName));
|
||||
await writeFile(tempName, contents, "utf-8");
|
||||
|
||||
// Now, rename the original file to (.backup-XXX)
|
||||
const oldTemporaryName = file + ".backup-" + transactionId;
|
||||
console.log(prefix, "Renaming old file to", path.basename(oldTemporaryName));
|
||||
await rename(file, oldTemporaryName);
|
||||
|
||||
// Now, rename the temporary file (.tmp-XXX) to the target
|
||||
console.log(prefix, "Renaming the temporary file", path.basename(tempName), "to the original file");
|
||||
await rename(tempName, file);
|
||||
|
||||
// We are done now, try to create a backup, but don't fail if the backup fails
|
||||
try {
|
||||
// Check if there is an old backup file
|
||||
const backupFileName = file + ".backup";
|
||||
if (existsSync(backupFileName)) {
|
||||
console.log(prefix, "Deleting old backup file", path.basename(backupFileName));
|
||||
await unlink(backupFileName);
|
||||
}
|
||||
|
||||
// Rename the old file to the new backup file
|
||||
console.log(prefix, "Moving", path.basename(oldTemporaryName), "to the backup file location");
|
||||
await rename(oldTemporaryName, backupFileName);
|
||||
} catch (err) {
|
||||
console.error(prefix, "Failed to swap backup files:", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sanitizeFileName(filename) {
|
||||
return filename.replace(/[^a-z.\-_0-9]/gi, "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers IPC handler for filesystem-related tasks.
|
||||
*/
|
||||
function initializeFilesystem() {
|
||||
ipcMain.handle("fs-job", onFilesystemJob);
|
||||
}
|
||||
|
||||
module.exports = { initializeFilesystem };
|
51
electron/src/folders.js
Normal file
51
electron/src/folders.js
Normal file
@ -0,0 +1,51 @@
|
||||
const { app, ipcMain, shell } = require("electron");
|
||||
const { mkdirSync } = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// Need this to avoid migrating savegames and mods
|
||||
const userHome = app.getPath("home");
|
||||
const platformStorageRoots = {
|
||||
win32: app.getPath("appData"),
|
||||
linux: process.env.XDG_DATA_HOME ?? path.join(userHome, ".local/share"),
|
||||
darwin: path.join(userHome, "Library/Preferences"),
|
||||
};
|
||||
|
||||
app.setPath("appData", platformStorageRoots[process.platform]);
|
||||
|
||||
const appData = path.join(app.getPath("appData"), "shapez.io");
|
||||
const savesDir = path.join(appData, "saves");
|
||||
const modsDir = path.join(appData, "mods");
|
||||
const crashLogsDir = path.join(appData, "crashes");
|
||||
|
||||
// Here, { recursive: true } permits omitting existsSync check
|
||||
mkdirSync(savesDir, { recursive: true });
|
||||
mkdirSync(modsDir, { recursive: true });
|
||||
mkdirSync(crashLogsDir, { recursive: true });
|
||||
|
||||
// Folders need to exist before it is possible to set them
|
||||
app.setPath("userData", appData);
|
||||
|
||||
/**
|
||||
* Sets IPC handler to open various folders.
|
||||
*/
|
||||
function initializeFolders() {
|
||||
ipcMain.handle("open-folder", (_, folder) => {
|
||||
const folderPath = {
|
||||
saves: savesDir,
|
||||
mods: modsDir,
|
||||
}[folder];
|
||||
|
||||
if (folderPath === undefined) {
|
||||
// Asked to open unknown folder
|
||||
return;
|
||||
}
|
||||
return shell.openPath(folderPath);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initializeFolders,
|
||||
savesDir,
|
||||
modsDir,
|
||||
crashLogsDir,
|
||||
};
|
71
electron/src/menu.js
Normal file
71
electron/src/menu.js
Normal file
@ -0,0 +1,71 @@
|
||||
const { Menu, MenuItem, app } = require("electron");
|
||||
|
||||
/**
|
||||
* Returns menu items for the specified window.
|
||||
* @param {Electron.BrowserWindow} window The window to use for actions
|
||||
*/
|
||||
function createMenuItems(window) {
|
||||
/**
|
||||
* Specifying options directly for simplicity.
|
||||
* @type {Electron.MenuItemConstructorOptions[]}
|
||||
*/
|
||||
const itemOptions = [];
|
||||
|
||||
itemOptions.push({
|
||||
label: "Developer Tools",
|
||||
accelerator: "F12",
|
||||
click: () => window.webContents.toggleDevTools(),
|
||||
});
|
||||
|
||||
itemOptions.push({
|
||||
label: "Reload",
|
||||
accelerator: "F5",
|
||||
click: () => window.reload(),
|
||||
});
|
||||
|
||||
itemOptions.push({
|
||||
label: "Restart",
|
||||
accelerator: "F5",
|
||||
click: () => {
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
},
|
||||
});
|
||||
|
||||
itemOptions.push({
|
||||
label: "Full Screen",
|
||||
accelerator: "F11",
|
||||
click: () => window.setFullScreen(!window.fullScreen),
|
||||
});
|
||||
|
||||
return itemOptions.map(options => new MenuItem(options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and set a menu for quick access to development tasks.
|
||||
* @param {Electron.BrowserWindow} window The window to set menu on
|
||||
*/
|
||||
function initializeMenu(window) {
|
||||
const menu = new Menu();
|
||||
for (const item of createMenuItems(window)) {
|
||||
menu.append(item);
|
||||
}
|
||||
|
||||
if (process.platform == "darwin") {
|
||||
// We're on macOS, so a root menu is needed
|
||||
const rootMenu = new Menu();
|
||||
rootMenu.append(
|
||||
new MenuItem({
|
||||
label: "shapez.io",
|
||||
submenu: menu,
|
||||
})
|
||||
);
|
||||
|
||||
window.setMenu(rootMenu);
|
||||
}
|
||||
|
||||
// Items can be directly used on Windows/Linux
|
||||
Menu.setApplicationMenu(menu);
|
||||
}
|
||||
|
||||
module.exports = { initializeMenu };
|
136
electron/src/mods.js
Normal file
136
electron/src/mods.js
Normal file
@ -0,0 +1,136 @@
|
||||
const { dialog, ipcMain } = require("electron");
|
||||
const { readdirSync, readFileSync } = require("fs");
|
||||
const { basename, join } = require("path");
|
||||
const { modsDir } = require("./folders");
|
||||
const { externalModList, isSafeMode, isDev } = require("./switches");
|
||||
|
||||
/**
|
||||
* Map of mod files to source code, populated when initializing mods.
|
||||
* @type {Map<string, string>}
|
||||
* @todo Leverage this to implement disalbing mods
|
||||
*/
|
||||
const modSources = new Map();
|
||||
|
||||
/**
|
||||
* Stores all mod loading errors to report them later.
|
||||
* @type {Map<string, Error>}
|
||||
*/
|
||||
const modErrors = new Map();
|
||||
|
||||
/**
|
||||
* Returns an array of all mod files found in mods/ directory,
|
||||
* skipping the search if safe mode is turned on.
|
||||
* @return {string[]}
|
||||
*/
|
||||
function getModFiles() {
|
||||
if (isSafeMode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = readdirSync(modsDir).filter(file => file.endsWith(".js"));
|
||||
return files.map(file => join(modsDir, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to read all mod files and store their source code in a map,
|
||||
* and registers an IPC handler to return mod source code.
|
||||
*/
|
||||
function initializeMods() {
|
||||
// Not checking whether external mods exist, it's done later
|
||||
const loadOrder = [...getModFiles(), ...externalModList];
|
||||
|
||||
for (const file of loadOrder) {
|
||||
// Each mod has own try/catch block so a single mod won't
|
||||
// break everything else
|
||||
try {
|
||||
const code = readFileSync(file, "utf-8");
|
||||
modSources.set(file, code);
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
// Ensure only Error objects get there
|
||||
modErrors.set(basename(file), err);
|
||||
} else {
|
||||
// Otherwise, silently log them - the mod is throwing
|
||||
// random stuff
|
||||
console.error("A mod reported unknown error:", err);
|
||||
}
|
||||
|
||||
console.warn("Failed to load a mod:", file);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(modSources.size, "mod files found");
|
||||
|
||||
ipcMain.handle("get-mods", () => {
|
||||
// Renderer only needs relative file names
|
||||
/** @type {{ filename: string, source: string }[]} */
|
||||
const mods = [];
|
||||
for (const [file, source] of modSources.entries()) {
|
||||
// Note: duplicates are possible, so we're not using a map here
|
||||
mods.push({ filename: basename(file), source });
|
||||
}
|
||||
|
||||
return mods;
|
||||
});
|
||||
|
||||
ipcMain.on("mod-error", (_, filename, error) => {
|
||||
// A single mod can't have more than one error,
|
||||
// so just set filename -> error
|
||||
modErrors.set(filename, error);
|
||||
});
|
||||
|
||||
ipcMain.on("show-mod-errors", () => showModErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a warning about missing external mod files.
|
||||
* Make sure to call this after "ready" app event.
|
||||
*/
|
||||
function showMissingExternalMods() {
|
||||
const missing = externalModList.filter(mod => !existsSync(mod));
|
||||
if (missing.length == 0) {
|
||||
// None missing, or none were specified
|
||||
return;
|
||||
}
|
||||
|
||||
const message = missing.map(mod => ` - ${mod}`).join("\n");
|
||||
return dialog.showMessageBox({
|
||||
title: "External Mod Errors",
|
||||
message: "These mod files could not be found:",
|
||||
detail: message,
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog with collected mod loading errors, if
|
||||
* there were any. Can only be called once the app is ready.
|
||||
*/
|
||||
function showModErrors() {
|
||||
if (modErrors.size == 0) {
|
||||
// We're lucky - no errors reported
|
||||
return;
|
||||
}
|
||||
|
||||
let errorText = "";
|
||||
for (const [mod, error] of modErrors.entries()) {
|
||||
// Show full errors with --dev
|
||||
errorText += `${mod}: ${isDev ? "\n" + error.stack : error.message}\n`;
|
||||
}
|
||||
|
||||
return dialog.showMessageBox({
|
||||
title: "Mod Errors",
|
||||
message: "Failed to load some mods:",
|
||||
detail: errorText,
|
||||
});
|
||||
}
|
||||
|
||||
function anyModLoaded() {
|
||||
return modSources.size > 0;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initializeMods,
|
||||
showMissingExternalMods,
|
||||
anyModLoaded,
|
||||
};
|
113
electron/src/steam.js
Normal file
113
electron/src/steam.js
Normal file
@ -0,0 +1,113 @@
|
||||
const { readFileSync } = require("fs");
|
||||
const { join } = require("path");
|
||||
const { ipcMain, app, dialog } = require("electron");
|
||||
const { isDev } = require("./switches");
|
||||
const { anyModLoaded } = require("./mods");
|
||||
|
||||
let greenworks = null;
|
||||
let appId = null;
|
||||
let isInitialized = false;
|
||||
|
||||
try {
|
||||
greenworks = require("shapez.io-private-artifacts/steam/greenworks");
|
||||
appId = parseInt(readFileSync("steam_appid.txt", "utf-8"));
|
||||
} catch (err) {
|
||||
console.warn("Failed to load Steam API:", err.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts the game immediately if Steam cannot be initialized
|
||||
* without restarting (game launched outside of Steam). Doesn't
|
||||
* restart if shapez.io was launched with --dev.
|
||||
*/
|
||||
function restartIfNeeded() {
|
||||
if (isDev) {
|
||||
// Skip restart when in development mode
|
||||
return;
|
||||
}
|
||||
|
||||
if (greenworks.restartAppIfNecessary(appId)) {
|
||||
console.log("Restarting with Steam...");
|
||||
app.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes Steam API (if possible) and registers relevant
|
||||
* IPC handlers for achievements and DLC. Can be safely run
|
||||
* before "ready" event.
|
||||
*/
|
||||
function initializeSteam() {
|
||||
ipcMain.handle("steam:is-initialized", () => isInitialized);
|
||||
ipcMain.handle("steam:activate-achievement", (_, id) => activateAchievement(id));
|
||||
ipcMain.handle("steam:check-app-ownership", (_, id) => checkAppOwnership(id));
|
||||
ipcMain.handle("steam:get-ticket", () => getTicket());
|
||||
|
||||
if (greenworks === null || anyModLoaded()) {
|
||||
// Skip initialization - we won't need it anyway
|
||||
return;
|
||||
}
|
||||
|
||||
restartIfNeeded();
|
||||
|
||||
try {
|
||||
isInitialized = greenworks.init();
|
||||
} catch (err) {
|
||||
// This mostly happens when Steam glitches occur
|
||||
dialog.showErrorBox("Steam API Error", err.message);
|
||||
app.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates an achievement by ID, if the game isn't running
|
||||
* with mods.
|
||||
* @param {string} id ID of achievement to activate
|
||||
*/
|
||||
function activateAchievement(id) {
|
||||
if (!isInitialized) {
|
||||
// Either missing greenworks or running with mods
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
greenworks.activateAchievement(
|
||||
id,
|
||||
() => resolve(),
|
||||
err => reject(err)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the user owns specified app, used for DLC.
|
||||
* @param {number} id ID of application to check ownership of
|
||||
*/
|
||||
function checkAppOwnership(id) {
|
||||
if (!isInitialized) {
|
||||
// No Steam access, therefore it's impossible to check
|
||||
return false;
|
||||
}
|
||||
|
||||
return greenworks.isDLCInstalled(id);
|
||||
}
|
||||
|
||||
function getTicket() {
|
||||
if (!isInitialized) {
|
||||
// Just fail because there's nothing to do
|
||||
return Promise.reject(new Error("Steam API is not initialized."));
|
||||
}
|
||||
|
||||
console.log("Requesting Steam ticket...");
|
||||
return new Promise((resolve, reject) => {
|
||||
greenworks.getAuthSessionTicket(
|
||||
success => resolve(success.ticket.toString("hex")),
|
||||
error => {
|
||||
console.error("Failed to get steam ticket:", error);
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { initializeSteam };
|
85
electron/src/switches.js
Normal file
85
electron/src/switches.js
Normal file
@ -0,0 +1,85 @@
|
||||
const { app, dialog } = require("electron");
|
||||
const { readFileSync } = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
/**
|
||||
* List of mods to load manually
|
||||
* @type {string[]}
|
||||
*/
|
||||
const externalModList = [];
|
||||
|
||||
/**
|
||||
* Retrieves external mod list and sets up the application,
|
||||
* should be called only before the app is ready.
|
||||
*/
|
||||
function initializeSwitches() {
|
||||
if (!app.commandLine.hasSwitch("disable-features")) {
|
||||
// Disable Chromium's media keys handler to avoid interfering with
|
||||
// media controls and other apps (such as pausing in-game music)
|
||||
app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling");
|
||||
}
|
||||
|
||||
// First load mod list, then other mods
|
||||
externalModList.push(...parseExternalModList());
|
||||
externalModList.push(...parseCommandLineMods());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of absolute file paths to mods loaded using
|
||||
* --load-mod=path/mod1.js,path/mod2.js argument, or none if the
|
||||
* argument is missing.
|
||||
*/
|
||||
function parseCommandLineMods() {
|
||||
const loadModList = app.commandLine.getSwitchValue("load-mod");
|
||||
if (!loadModList) {
|
||||
// Empty or missing
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = loadModList.split(",");
|
||||
return resolveAllFiles(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of absolute file paths to mods loaded using
|
||||
* --mod-list=path/to/mods.json argument (JSON array), or none
|
||||
* if the file wasn't specified or found.
|
||||
*/
|
||||
function parseExternalModList() {
|
||||
const modListPath = app.commandLine.getSwitchValue("mod-list");
|
||||
if (!modListPath) {
|
||||
// None requested, let's just skip that
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
// Read the file and return resolved mod file paths
|
||||
const json = readFileSync(modListPath, "utf-8");
|
||||
return resolveAllFiles(JSON.parse(json));
|
||||
} catch (err) {
|
||||
// Something went wrong - notify and continue
|
||||
dialog.showErrorBox("Failed to load external mod list!", err.stack);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Small utility to resolve all file paths in an array.
|
||||
* @param {string[]} files
|
||||
*/
|
||||
function resolveAllFiles(files) {
|
||||
return files.map(file => path.resolve(file));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initializeSwitches,
|
||||
// Shows a menu on the window with useful actions
|
||||
isDev: app.commandLine.hasSwitch("dev"),
|
||||
// Instructs the renderer to use bundle hosted on localhost
|
||||
isLocal: app.commandLine.hasSwitch("local"),
|
||||
// Disables all mods except manually loaded ones
|
||||
isSafeMode: app.commandLine.hasSwitch("safe-mode"),
|
||||
// Suppresses initial toggle of developer tools
|
||||
shouldHideDevtools: app.commandLine.hasSwitch("hide-devtools"),
|
||||
externalModList,
|
||||
};
|
@ -1,112 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { ipcMain } = require("electron");
|
||||
|
||||
let greenworks = null;
|
||||
let appId = null;
|
||||
let initialized = false;
|
||||
|
||||
try {
|
||||
greenworks = require("shapez.io-private-artifacts/steam/greenworks");
|
||||
appId = parseInt(fs.readFileSync(path.join(__dirname, "steam_appid.txt"), "utf8"));
|
||||
} catch (err) {
|
||||
// greenworks is not installed
|
||||
console.warn("Failed to load steam api:", err);
|
||||
}
|
||||
|
||||
console.log("App ID:", appId);
|
||||
|
||||
function init(isDev) {
|
||||
if (!greenworks) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDev) {
|
||||
if (greenworks.restartAppIfNecessary(appId)) {
|
||||
console.log("Restarting ...");
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!greenworks.init()) {
|
||||
console.log("Failed to initialize greenworks");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
function listen() {
|
||||
ipcMain.handle("steam:is-initialized", isInitialized);
|
||||
|
||||
if (!initialized) {
|
||||
console.warn("Steam not initialized, won't be able to listen");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!greenworks) {
|
||||
console.warn("Greenworks not loaded, won't be able to listen");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Adding listeners");
|
||||
|
||||
ipcMain.handle("steam:get-achievement-names", getAchievementNames);
|
||||
ipcMain.handle("steam:activate-achievement", activateAchievement);
|
||||
|
||||
function bufferToHex(buffer) {
|
||||
return Array.from(new Uint8Array(buffer))
|
||||
.map(b => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
ipcMain.handle("steam:get-ticket", (event, arg) => {
|
||||
console.log("Requested steam ticket ...");
|
||||
return new Promise((resolve, reject) => {
|
||||
greenworks.getAuthSessionTicket(
|
||||
success => {
|
||||
const ticketHex = bufferToHex(success.ticket);
|
||||
resolve(ticketHex);
|
||||
},
|
||||
error => {
|
||||
console.error("Failed to get steam ticket:", error);
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle("steam:check-app-ownership", (event, appId) => {
|
||||
return Promise.resolve(greenworks.isDLCInstalled(appId));
|
||||
});
|
||||
}
|
||||
|
||||
function isInitialized(event) {
|
||||
return Promise.resolve(initialized);
|
||||
}
|
||||
|
||||
function getAchievementNames(event) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const achievements = greenworks.getAchievementNames();
|
||||
resolve(achievements);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function activateAchievement(event, id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
greenworks.activateAchievement(
|
||||
id,
|
||||
() => resolve(),
|
||||
err => reject(err)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init,
|
||||
listen,
|
||||
};
|
@ -3,9 +3,9 @@
|
||||
|
||||
|
||||
"@electron/get@^1.13.0":
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.1.tgz#42a0aa62fd1189638bd966e23effaebb16108368"
|
||||
integrity sha512-U5vkXDZ9DwXtkPqlB45tfYnnYBN8PePp1z/XDCupnSpdrxT8/ThCv9WCwPLf9oqiSGZTkH6dx2jDUPuoXpjkcA==
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.14.1.tgz#16ba75f02dffb74c23965e72d617adc721d27f40"
|
||||
integrity sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
env-paths "^2.2.0"
|
||||
@ -30,10 +30,10 @@
|
||||
dependencies:
|
||||
defer-to-connect "^1.0.1"
|
||||
|
||||
"@types/node@^14.6.2":
|
||||
version "14.18.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.5.tgz#0dd636fe7b2c6055cbed0d4ca3b7fb540f130a96"
|
||||
integrity sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==
|
||||
"@types/node@^16.11.26":
|
||||
version "16.11.36"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.36.tgz#9ab9f8276987132ed2b225cace2218ba794fc751"
|
||||
integrity sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==
|
||||
|
||||
async-lock@^1.2.8:
|
||||
version "1.2.8"
|
||||
@ -149,13 +149,13 @@ electron-window-state@^5.0.3:
|
||||
jsonfile "^4.0.0"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
electron@16.0.7:
|
||||
version "16.0.7"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-16.0.7.tgz#87eaccd05ab61563d3c17dfbad2949bba7ead162"
|
||||
integrity sha512-/IMwpBf2svhA1X/7Q58RV+Nn0fvUJsHniG4TizaO7q4iKFYSQ6hBvsLz+cylcZ8hRMKmVy5G1XaMNJID2ah23w==
|
||||
electron@^18.3.1:
|
||||
version "18.3.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-18.3.1.tgz#822ca54bd0a32098712377376617cfdd230846d6"
|
||||
integrity sha512-46lH3iEdvbbDSa0s2JiOysGruQlJwGUae0UrEfZ4NgHZUnHbglkvezfKSoRSOEob3c9rENZlvgEv9gCbhYx5Yw==
|
||||
dependencies:
|
||||
"@electron/get" "^1.13.0"
|
||||
"@types/node" "^14.6.2"
|
||||
"@types/node" "^16.11.26"
|
||||
extract-zip "^1.0.3"
|
||||
|
||||
encodeurl@^1.0.2:
|
||||
|
@ -117,7 +117,7 @@ function gulptasksStandalone($, gulp) {
|
||||
});
|
||||
|
||||
gulp.task(taskPrefix + "standalone.prepare.minifyCode", () => {
|
||||
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
|
||||
return gulp.src(path.join(electronBaseDir, "**/*.js")).pipe(gulp.dest(tempDestBuildDir));
|
||||
});
|
||||
|
||||
gulp.task(taskPrefix + "standalone.prepare.copyGamefiles", () => {
|
||||
|
@ -85,6 +85,7 @@
|
||||
"babel-plugin-danger-remove-unused-import": "^1.1.2",
|
||||
"css-mqpacker": "^7.0.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"electron": "^18",
|
||||
"eslint-config-prettier": "6.11.0",
|
||||
"eslint-plugin-prettier": "3.1.3",
|
||||
"faster.js": "^1.1.0",
|
||||
|
@ -120,6 +120,8 @@ function catchErrors(message, source, lineno, colno, error) {
|
||||
}, 200);
|
||||
}
|
||||
|
||||
ipcRenderer.send("write-crash-log", (error && error.stack) || message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,8 @@ const LOG = createLogger("mods");
|
||||
* id: string;
|
||||
* minimumGameVersion?: string;
|
||||
* settings: [];
|
||||
* doesNotAffectSavegame?: boolean
|
||||
* doesNotAffectSavegame?: boolean;
|
||||
* filename: string
|
||||
* }} ModMetadata
|
||||
*/
|
||||
|
||||
@ -150,6 +151,7 @@ export class ModLoader {
|
||||
LOG.log("hook:init", this.app, this.app.storage);
|
||||
this.exposeExports();
|
||||
|
||||
/** @type {{ filename: string, source: string }[]} */
|
||||
let mods = [];
|
||||
if (G_IS_STANDALONE) {
|
||||
mods = await ipcRenderer.invoke("get-mods");
|
||||
@ -168,11 +170,14 @@ export class ModLoader {
|
||||
"Failed to load " + modURLs[i] + ": " + response.status + " " + response.statusText
|
||||
);
|
||||
}
|
||||
mods.push(await response.text());
|
||||
mods.push({
|
||||
filename: modURLs[i],
|
||||
source: await response.text(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.$shapez_registerMod = (modClass, meta) => {
|
||||
const registerMod = (modFile, modClass, meta) => {
|
||||
if (this.initialized) {
|
||||
throw new Error("Can't register mod after modloader is initialized");
|
||||
}
|
||||
@ -180,14 +185,17 @@ export class ModLoader {
|
||||
console.warn("Not registering mod", meta, "since a mod with the same id is already loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
meta.filename = modFile;
|
||||
this.modLoadQueue.push({
|
||||
modClass,
|
||||
meta,
|
||||
});
|
||||
};
|
||||
|
||||
mods.forEach(modCode => {
|
||||
modCode += `
|
||||
mods.forEach(({ filename, source }) => {
|
||||
window.$shapez_registerMod = registerMod.bind(this, filename);
|
||||
source += `
|
||||
if (typeof Mod !== 'undefined') {
|
||||
if (typeof METADATA !== 'object') {
|
||||
throw new Error("No METADATA variable found");
|
||||
@ -196,11 +204,11 @@ export class ModLoader {
|
||||
}
|
||||
`;
|
||||
try {
|
||||
const func = new Function(modCode);
|
||||
const func = new Function(source);
|
||||
func();
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
alert("Failed to parse mod (launch with --dev for more info): \n\n" + ex);
|
||||
ipcRenderer.send("mod-error", filename, ex);
|
||||
}
|
||||
});
|
||||
|
||||
@ -213,19 +221,16 @@ export class ModLoader {
|
||||
if (meta.minimumGameVersion) {
|
||||
const minimumGameVersion = meta.minimumGameVersion;
|
||||
if (!semverValidRange(minimumGameVersion)) {
|
||||
alert("Mod " + meta.id + " has invalid minimumGameVersion: " + minimumGameVersion);
|
||||
const error = new Error(`Invalid minimumGameVersion specified: ${minimumGameVersion}`);
|
||||
ipcRenderer.send("mod-error", meta.filename, error);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!semverSatisifies(G_BUILD_VERSION, minimumGameVersion)) {
|
||||
alert(
|
||||
"Mod '" +
|
||||
meta.id +
|
||||
"' is incompatible with this version of the game: \n\n" +
|
||||
"Mod requires version " +
|
||||
minimumGameVersion +
|
||||
" but this game has version " +
|
||||
G_BUILD_VERSION
|
||||
const error = new Error(
|
||||
`This game version (${G_BUILD_VERSION}) is not supported, ${minimumGameVersion} is required`
|
||||
);
|
||||
ipcRenderer.send("mod-error", meta.filename, error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -258,10 +263,12 @@ export class ModLoader {
|
||||
this.mods.push(mod);
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
alert("Failed to initialize mods (launch with --dev for more info): \n\n" + ex);
|
||||
ipcRenderer.send("mod-error", meta.filename, ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Once initialization of all mods is done, show all errors
|
||||
ipcRenderer.send("show-mod-errors");
|
||||
this.modLoadQueue = [];
|
||||
this.initialized = true;
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ export class ModsState extends TextualGameState {
|
||||
this.dialogs.showWarning(T.global.error, T.mods.folderOnlyStandalone);
|
||||
return;
|
||||
}
|
||||
ipcRenderer.invoke("open-mods-folder");
|
||||
ipcRenderer.invoke("open-folder", "mods");
|
||||
}
|
||||
|
||||
openBrowseMods() {
|
||||
|
306
yarn.lock
306
yarn.lock
@ -748,6 +748,22 @@
|
||||
resolved "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz"
|
||||
integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==
|
||||
|
||||
"@electron/get@^1.13.0":
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.14.1.tgz#16ba75f02dffb74c23965e72d617adc721d27f40"
|
||||
integrity sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
env-paths "^2.2.0"
|
||||
fs-extra "^8.1.0"
|
||||
got "^9.6.0"
|
||||
progress "^2.0.3"
|
||||
semver "^6.2.0"
|
||||
sumchecker "^3.0.1"
|
||||
optionalDependencies:
|
||||
global-agent "^3.0.0"
|
||||
global-tunnel-ng "^2.7.1"
|
||||
|
||||
"@jimp/bmp@^0.6.8":
|
||||
version "0.6.8"
|
||||
resolved "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.6.8.tgz"
|
||||
@ -1097,11 +1113,23 @@
|
||||
dependencies:
|
||||
"@types/node" ">= 8"
|
||||
|
||||
"@sindresorhus/is@^0.14.0":
|
||||
version "0.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
||||
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
|
||||
|
||||
"@sindresorhus/is@^0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz"
|
||||
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
|
||||
|
||||
"@szmarczak/http-timer@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
|
||||
integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
|
||||
dependencies:
|
||||
defer-to-connect "^1.0.1"
|
||||
|
||||
"@types/color-name@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz"
|
||||
@ -1139,6 +1167,11 @@
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz"
|
||||
integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==
|
||||
|
||||
"@types/node@^16.11.26":
|
||||
version "16.11.36"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.36.tgz#9ab9f8276987132ed2b225cace2218ba794fc751"
|
||||
integrity sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==
|
||||
|
||||
"@types/q@^1.5.1":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz"
|
||||
@ -1935,6 +1968,11 @@ boolbase@^1.0.0, boolbase@~1.0.0:
|
||||
resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
boolean@^3.0.1:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b"
|
||||
integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
|
||||
@ -2156,6 +2194,19 @@ cacheable-request@^2.1.1:
|
||||
normalize-url "2.0.1"
|
||||
responselike "1.0.2"
|
||||
|
||||
cacheable-request@^6.0.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
|
||||
integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
|
||||
dependencies:
|
||||
clone-response "^1.0.2"
|
||||
get-stream "^5.1.0"
|
||||
http-cache-semantics "^4.0.0"
|
||||
keyv "^3.0.0"
|
||||
lowercase-keys "^2.0.0"
|
||||
normalize-url "^4.1.0"
|
||||
responselike "^1.0.2"
|
||||
|
||||
calipers-gif@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/calipers-gif/-/calipers-gif-2.0.0.tgz"
|
||||
@ -2435,7 +2486,7 @@ cliui@^6.0.0:
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^6.2.0"
|
||||
|
||||
clone-response@1.0.2:
|
||||
clone-response@1.0.2, clone-response@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz"
|
||||
integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
|
||||
@ -2546,7 +2597,7 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
concat-stream@^1.5.0:
|
||||
concat-stream@^1.5.0, concat-stream@^1.6.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz"
|
||||
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
||||
@ -3051,6 +3102,11 @@ deep-scope-analyser@^1.7.0:
|
||||
esrecurse "^4.2.1"
|
||||
estraverse "^4.2.0"
|
||||
|
||||
defer-to-connect@^1.0.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
|
||||
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
|
||||
|
||||
define-properties@^1.1.2, define-properties@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz"
|
||||
@ -3115,6 +3171,11 @@ detect-indent@^4.0.0:
|
||||
dependencies:
|
||||
repeating "^2.0.0"
|
||||
|
||||
detect-node@^2.0.4:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
|
||||
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
|
||||
|
||||
diffie-hellman@^5.0.0:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz"
|
||||
@ -3244,6 +3305,15 @@ electron-to-chromium@^1.3.390:
|
||||
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.403.tgz"
|
||||
integrity sha512-JaoxV4RzdBAZOnsF4dAlZ2ijJW72MbqO5lNfOBHUWiBQl3Rwe+mk2RCUMrRI3rSClLJ8HSNQNqcry12H+0ZjFw==
|
||||
|
||||
electron@^18:
|
||||
version "18.3.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-18.3.1.tgz#822ca54bd0a32098712377376617cfdd230846d6"
|
||||
integrity sha512-46lH3iEdvbbDSa0s2JiOysGruQlJwGUae0UrEfZ4NgHZUnHbglkvezfKSoRSOEob3c9rENZlvgEv9gCbhYx5Yw==
|
||||
dependencies:
|
||||
"@electron/get" "^1.13.0"
|
||||
"@types/node" "^16.11.26"
|
||||
extract-zip "^1.0.3"
|
||||
|
||||
elliptic@^6.0.0, elliptic@^6.5.2:
|
||||
version "6.5.3"
|
||||
resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz"
|
||||
@ -3282,7 +3352,7 @@ emojis-list@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz"
|
||||
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
encodeurl@^1.0.2, encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
|
||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||
@ -3317,6 +3387,11 @@ entities@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz"
|
||||
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
|
||||
|
||||
env-paths@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
|
||||
|
||||
errno@^0.1.3, errno@~0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz"
|
||||
@ -3357,6 +3432,11 @@ es-to-primitive@^1.2.1:
|
||||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
es6-error@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
|
||||
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
|
||||
|
||||
es6-templates@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz"
|
||||
@ -3375,6 +3455,11 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
|
||||
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
escape-string-regexp@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
eslint-config-prettier@6.11.0:
|
||||
version "6.11.0"
|
||||
resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz"
|
||||
@ -3685,6 +3770,16 @@ extglob@^2.0.4:
|
||||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
extract-zip@^1.0.3:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
|
||||
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
|
||||
dependencies:
|
||||
concat-stream "^1.6.2"
|
||||
debug "^2.6.9"
|
||||
mkdirp "^0.5.4"
|
||||
yauzl "^2.10.0"
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
|
||||
@ -3965,6 +4060,15 @@ fs-constants@^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==
|
||||
|
||||
fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-write-stream-atomic@^1.0.8:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz"
|
||||
@ -4043,13 +4147,20 @@ get-stream@^2.2.0:
|
||||
object-assign "^4.0.1"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
get-stream@^4.0.0:
|
||||
get-stream@^4.0.0, get-stream@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz"
|
||||
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
get-stream@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
|
||||
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
get-value@^2.0.3, get-value@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz"
|
||||
@ -4090,6 +4201,18 @@ glob@^7.0.5, glob@^7.0.6, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
global-agent@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
|
||||
integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
|
||||
dependencies:
|
||||
boolean "^3.0.1"
|
||||
es6-error "^4.1.1"
|
||||
matcher "^3.0.0"
|
||||
roarr "^2.15.3"
|
||||
semver "^7.3.2"
|
||||
serialize-error "^7.0.1"
|
||||
|
||||
global-modules@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz"
|
||||
@ -4126,6 +4249,16 @@ global-prefix@^3.0.0:
|
||||
kind-of "^6.0.2"
|
||||
which "^1.3.1"
|
||||
|
||||
global-tunnel-ng@^2.7.1:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f"
|
||||
integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==
|
||||
dependencies:
|
||||
encodeurl "^1.0.2"
|
||||
lodash "^4.17.10"
|
||||
npm-conf "^1.1.3"
|
||||
tunnel "^0.0.6"
|
||||
|
||||
global@~4.3.0:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.npmjs.org/global/-/global-4.3.2.tgz"
|
||||
@ -4151,6 +4284,13 @@ globals@^9.18.0:
|
||||
resolved "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz"
|
||||
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
|
||||
|
||||
globalthis@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf"
|
||||
integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
|
||||
gonzales-pe@^4.2.3:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz"
|
||||
@ -4201,6 +4341,23 @@ got@^8.3.1:
|
||||
url-parse-lax "^3.0.0"
|
||||
url-to-options "^1.0.1"
|
||||
|
||||
got@^9.6.0:
|
||||
version "9.6.0"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
|
||||
integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^0.14.0"
|
||||
"@szmarczak/http-timer" "^1.1.2"
|
||||
cacheable-request "^6.0.0"
|
||||
decompress-response "^3.3.0"
|
||||
duplexer3 "^0.1.4"
|
||||
get-stream "^4.1.0"
|
||||
lowercase-keys "^1.0.1"
|
||||
mimic-response "^1.0.1"
|
||||
p-cancelable "^1.0.0"
|
||||
to-readable-stream "^1.0.0"
|
||||
url-parse-lax "^3.0.0"
|
||||
|
||||
graceful-fs@^4.1.10, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz"
|
||||
@ -4211,6 +4368,11 @@ graceful-fs@^4.1.11:
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
||||
|
||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
version "4.2.10"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz"
|
||||
@ -4411,6 +4573,11 @@ http-cache-semantics@3.8.1:
|
||||
resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz"
|
||||
integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==
|
||||
|
||||
http-cache-semantics@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
|
||||
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
|
||||
|
||||
http-errors@1.7.2, http-errors@~1.7.2:
|
||||
version "1.7.2"
|
||||
resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz"
|
||||
@ -4974,6 +5141,11 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
|
||||
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
||||
|
||||
json-stringify-safe@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
|
||||
|
||||
json5@^0.5.0, json5@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz"
|
||||
@ -4993,6 +5165,13 @@ json5@^2.1.2:
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
keyv@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz"
|
||||
@ -5000,6 +5179,13 @@ keyv@3.0.0:
|
||||
dependencies:
|
||||
json-buffer "3.0.0"
|
||||
|
||||
keyv@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
|
||||
integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
|
||||
dependencies:
|
||||
json-buffer "3.0.0"
|
||||
|
||||
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz"
|
||||
@ -5172,6 +5358,11 @@ lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.
|
||||
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
|
||||
lodash@^4.17.10:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
logalot@^2.0.0, logalot@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz"
|
||||
@ -5215,11 +5406,16 @@ lowercase-keys@1.0.0:
|
||||
resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz"
|
||||
integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=
|
||||
|
||||
lowercase-keys@^1.0.0:
|
||||
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz"
|
||||
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
|
||||
|
||||
lowercase-keys@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
|
||||
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
|
||||
|
||||
lpad-align@^1.0.1:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz"
|
||||
@ -5314,6 +5510,13 @@ match-all@^1.2.5:
|
||||
resolved "https://registry.npmjs.org/match-all/-/match-all-1.2.5.tgz"
|
||||
integrity sha512-KW4trRDMYbVkAKZ1J655vh0931mk3XM1lIJ480TXUL3KBrOsZ6WpryYJELonvtXC1O4erLYB069uHidLkswbjQ==
|
||||
|
||||
matcher@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
|
||||
integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
|
||||
dependencies:
|
||||
escape-string-regexp "^4.0.0"
|
||||
|
||||
md5.js@^1.3.4:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz"
|
||||
@ -5443,7 +5646,7 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0:
|
||||
resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
mimic-response@^1.0.0:
|
||||
mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz"
|
||||
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
|
||||
@ -5482,6 +5685,11 @@ minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
|
||||
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
minimist@^1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
mississippi@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz"
|
||||
@ -5520,6 +5728,13 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
mkdirp@^0.5.4:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
|
||||
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
|
||||
dependencies:
|
||||
minimist "^1.2.6"
|
||||
|
||||
move-concurrently@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz"
|
||||
@ -5695,7 +5910,12 @@ normalize-url@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz"
|
||||
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
|
||||
|
||||
npm-conf@^1.1.0:
|
||||
normalize-url@^4.1.0:
|
||||
version "4.5.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
|
||||
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
|
||||
|
||||
npm-conf@^1.1.0, npm-conf@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz"
|
||||
integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==
|
||||
@ -5879,6 +6099,11 @@ p-cancelable@^0.4.0:
|
||||
resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz"
|
||||
integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==
|
||||
|
||||
p-cancelable@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
|
||||
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
|
||||
|
||||
p-defer@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz"
|
||||
@ -6958,7 +7183,7 @@ process@~0.5.1:
|
||||
resolved "https://registry.npmjs.org/process/-/process-0.5.2.tgz"
|
||||
integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=
|
||||
|
||||
progress@^2.0.0:
|
||||
progress@^2.0.0, progress@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
@ -7340,7 +7565,7 @@ resolve@^1.10.0, resolve@^1.3.2:
|
||||
dependencies:
|
||||
path-parse "^1.0.6"
|
||||
|
||||
responselike@1.0.2:
|
||||
responselike@1.0.2, responselike@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz"
|
||||
integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
|
||||
@ -7385,6 +7610,18 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
hash-base "^3.0.0"
|
||||
inherits "^2.0.1"
|
||||
|
||||
roarr@^2.15.3:
|
||||
version "2.15.4"
|
||||
resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
|
||||
integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
|
||||
dependencies:
|
||||
boolean "^3.0.1"
|
||||
detect-node "^2.0.4"
|
||||
globalthis "^1.0.1"
|
||||
json-stringify-safe "^5.0.1"
|
||||
semver-compare "^1.0.0"
|
||||
sprintf-js "^1.1.2"
|
||||
|
||||
run-async@^2.4.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz"
|
||||
@ -7476,6 +7713,11 @@ seek-bzip@^1.0.5:
|
||||
dependencies:
|
||||
commander "~2.8.1"
|
||||
|
||||
semver-compare@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
|
||||
|
||||
semver-regex@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz"
|
||||
@ -7498,6 +7740,11 @@ semver@7.0.0:
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz"
|
||||
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
||||
|
||||
semver@^6.2.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.2.1, semver@^7.3.2:
|
||||
version "7.3.2"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz"
|
||||
@ -7534,6 +7781,13 @@ serialize-error@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-3.0.0.tgz"
|
||||
integrity sha512-+y3nkkG/go1Vdw+2f/+XUXM1DXX1XcxTl99FfiD/OEPUNw4uo0i6FKABfTAN5ZcgGtjTRZcEbxcE/jtXbEY19A==
|
||||
|
||||
serialize-error@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
|
||||
integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
|
||||
dependencies:
|
||||
type-fest "^0.13.1"
|
||||
|
||||
serialize-javascript@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz"
|
||||
@ -7774,6 +8028,11 @@ split-string@^3.0.1, split-string@^3.0.2:
|
||||
dependencies:
|
||||
extend-shallow "^3.0.0"
|
||||
|
||||
sprintf-js@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
|
||||
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
|
||||
@ -8006,6 +8265,13 @@ stylehacks@^4.0.0:
|
||||
postcss "^7.0.0"
|
||||
postcss-selector-parser "^3.0.0"
|
||||
|
||||
sumchecker@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
|
||||
integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
|
||||
dependencies:
|
||||
debug "^4.1.0"
|
||||
|
||||
supports-color@6.1.0, supports-color@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz"
|
||||
@ -8202,6 +8468,11 @@ to-object-path@^0.3.0:
|
||||
dependencies:
|
||||
kind-of "^3.0.2"
|
||||
|
||||
to-readable-stream@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
|
||||
integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
|
||||
|
||||
to-regex-range@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz"
|
||||
@ -8283,6 +8554,11 @@ tunnel-agent@^0.6.0:
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
tunnel@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
|
||||
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
|
||||
|
||||
type-check@^0.4.0, type-check@~0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz"
|
||||
@ -8295,6 +8571,11 @@ type-fest@^0.11.0:
|
||||
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz"
|
||||
integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
|
||||
|
||||
type-fest@^0.13.1:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
|
||||
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
|
||||
|
||||
type-fest@^0.5.1:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz"
|
||||
@ -8406,6 +8687,11 @@ universal-user-agent@^6.0.0:
|
||||
resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz"
|
||||
integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
|
||||
@ -8884,7 +9170,7 @@ yarn@^1.22.4:
|
||||
resolved "https://registry.npmjs.org/yarn/-/yarn-1.22.4.tgz"
|
||||
integrity sha512-oYM7hi/lIWm9bCoDMEWgffW8aiNZXCWeZ1/tGy0DWrN6vmzjCXIKu2Y21o8DYVBUtiktwKcNoxyGl/2iKLUNGA==
|
||||
|
||||
yauzl@^2.4.2:
|
||||
yauzl@^2.10.0, yauzl@^2.4.2:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz"
|
||||
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||
|
Loading…
Reference in New Issue
Block a user