mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Remove fs-extra and klaw (#882)
These packages are obsoleted by the promises API to the built-in Node fs module. The fs-extra package is also causing problems with broken APIs that don't behave the same as Node's fs module, and throwing errors without code properties on them.
This commit is contained in:
1 parent
130dca4261
commit
2e48b728dd
74 files changed
+570
-529
No files matched your search
@@ -8,7 +8,7 @@
|
||||
|
||||
"use strict";
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const yargs = require("yargs");
|
||||
|
||||
const npmPackage = require("./package.json");
|
||||
@@ -24,13 +24,15 @@ let DRY = false;
|
||||
* @param {string} filePath The path to remove.
|
||||
*/
|
||||
async function tryRemove(filePath) {
|
||||
if (await fs.exists(filePath)) {
|
||||
// eslint-disable-next-line no-console
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
console.log(filePath);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DRY) {
|
||||
await fs.remove(filePath);
|
||||
await fs.rm(filePath, { force: true, recursive: true, maxRetries: 10 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"express": "^5.1.0",
|
||||
"form-data": "^4.0.4",
|
||||
"fs-extra": "^11.3.0",
|
||||
"globals": "^16.3.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import path from "path";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
import crypto from "crypto";
|
||||
import setBlocking from "set-blocking";
|
||||
|
||||
@@ -22,40 +22,38 @@
|
||||
"dependencies": {
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@sinclair/typebox": "catalog:",
|
||||
"set-blocking": "catalog:",
|
||||
"jsonwebtoken": "catalog:",
|
||||
"express": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"ws": "catalog:",
|
||||
"jszip": "catalog:",
|
||||
"winston": "catalog:",
|
||||
"winston-daily-rotate-file": "catalog:",
|
||||
"busboy": "^1.6.0",
|
||||
"compression": "^1.8.0",
|
||||
"express": "catalog:",
|
||||
"finalhandler": "^1.3.1",
|
||||
"jsonwebtoken": "catalog:",
|
||||
"jszip": "catalog:",
|
||||
"set-blocking": "catalog:",
|
||||
"winston": "catalog:",
|
||||
"winston-daily-rotate-file": "catalog:",
|
||||
"ws": "catalog:",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@types/jsonwebtoken": "catalog:",
|
||||
"@types/set-blocking": "catalog:",
|
||||
"@types/finalhandler": "catalog:",
|
||||
"@swc/core": "catalog:",
|
||||
"@types/busboy": "^1.5.0",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/express": "catalog:",
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@types/finalhandler": "catalog:",
|
||||
"@types/jsonwebtoken": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/set-blocking": "catalog:",
|
||||
"@types/ws": "catalog:",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/busboy": "^1.5.0",
|
||||
"@swc/core": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-router-dom": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"webpack": "catalog:",
|
||||
"webpack-cli": "catalog:",
|
||||
"webpack-merge": "catalog:",
|
||||
"webpack-dev-middleware": "^6.1.1"
|
||||
"webpack-dev-middleware": "^6.1.1",
|
||||
"webpack-merge": "catalog:"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from "path";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
import type Controller from "./Controller";
|
||||
import type WsServerConnector from "./WsServerConnector";
|
||||
@@ -94,10 +94,12 @@ export default class BaseConnection extends lib.Link {
|
||||
async handleModDownloadRequest(request: lib.ModDownloadRequest) {
|
||||
let mod = this.getMod(request);
|
||||
let modPath = path.join(this._controller.config.get("controller.mods_directory"), mod.filename);
|
||||
// Open source file before creating the stream in case it throws.
|
||||
let source = (await fs.open(modPath)).createReadStream();
|
||||
|
||||
let stream = await routes.createProxyStream(this._controller.app);
|
||||
stream.filename = mod.filename;
|
||||
stream.source = fs.createReadStream(modPath);
|
||||
stream.source = source;
|
||||
stream.mime = "application/zip";
|
||||
stream.size = String(mod.size);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import finalhandler from "finalhandler";
|
||||
|
||||
import compression from "compression";
|
||||
import events from "events";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import http from "http";
|
||||
import https from "https";
|
||||
import jwt from "jsonwebtoken";
|
||||
@@ -123,7 +123,7 @@ export default class Controller {
|
||||
|
||||
static async bootstrap(config: lib.ControllerConfig) {
|
||||
let databaseDirectory = config.get("controller.database_directory");
|
||||
await fs.ensureDir(databaseDirectory);
|
||||
await fs.mkdir(databaseDirectory, { recursive: true });
|
||||
|
||||
// Migrate combined user and role file from 2.0.0-alpha.22
|
||||
await UserManager.attemptMigrateLegacyUsersFile(
|
||||
@@ -173,7 +173,7 @@ export default class Controller {
|
||||
).bootstrap());
|
||||
|
||||
let modsDirectory = config.get("controller.mods_directory");
|
||||
await fs.ensureDir(modsDirectory);
|
||||
await fs.mkdir(modsDirectory, { recursive: true });
|
||||
const modStore = await lib.ModStore.fromDirectory(modsDirectory);
|
||||
|
||||
// Add default mod packs
|
||||
@@ -602,7 +602,7 @@ export default class Controller {
|
||||
// First check the clusterio version
|
||||
const runningVersion = this.config.get("controller.version");
|
||||
const packageJsonPath = require.resolve("@clusterio/controller/package.json");
|
||||
const packageJson = await fs.readJSON(packageJsonPath);
|
||||
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
||||
if (runningVersion !== packageJson.version) {
|
||||
this.config.restartRequired = true;
|
||||
return true;
|
||||
@@ -619,7 +619,7 @@ export default class Controller {
|
||||
|
||||
packageName = pluginInfo.npmPackage ?? pluginInfo.name;
|
||||
const pluginPackageJsonPath = require.resolve(path.posix.join(pluginInfo.requirePath, "package.json"));
|
||||
const pluginPackageJson = await fs.readJSON(pluginPackageJsonPath);
|
||||
const pluginPackageJson = JSON.parse(await fs.readFile(pluginPackageJsonPath, "utf8"));
|
||||
if (pluginInfo.version !== pluginPackageJson.version) {
|
||||
this.config.restartRequired = true;
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import * as lib from "@clusterio/lib";
|
||||
|
||||
@@ -4,7 +4,7 @@ import Controller from "./Controller";
|
||||
import busboy from "busboy";
|
||||
import crypto from "crypto";
|
||||
import events from "events";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import JSZip from "jszip";
|
||||
import jwt from "jsonwebtoken";
|
||||
import path from "path";
|
||||
|
||||
+36
-22
@@ -2,7 +2,7 @@
|
||||
|
||||
"use strict";
|
||||
const child_process = require("child_process");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const inquirer = require("inquirer");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
@@ -97,11 +97,24 @@ class LineSplitter extends stream.Transform {
|
||||
|
||||
class InstallError extends Error { }
|
||||
|
||||
async function pathExists(filePath) {
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
} catch (err) {
|
||||
if (err.code === "ENOENT" || err.code === "ENOTDIR") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function safeOutputFile(file, data, options={}) {
|
||||
let { dir, name, ext } = path.parse(file);
|
||||
let temporary = path.join(dir, `${name}.tmp${ext}`);
|
||||
await fs.outputFile(temporary, data, options);
|
||||
if (dir) {
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
}
|
||||
await fs.writeFile(temporary, data, { ...options, flush: true });
|
||||
await fs.rename(temporary, file);
|
||||
}
|
||||
|
||||
@@ -221,7 +234,7 @@ async function migrateRename(args) {
|
||||
}
|
||||
|
||||
async function migrateConfig(source, destination) {
|
||||
if (await fs.pathExists(source) && !await fs.pathExists(destination)) {
|
||||
if (await pathExists(source) && !await pathExists(destination)) {
|
||||
await safeOutputFile(destination, JSON.stringify(
|
||||
renameConfig(JSON.parse(await fs.readFile(source))), null, "\t"
|
||||
));
|
||||
@@ -233,7 +246,7 @@ async function migrateRename(args) {
|
||||
await migrateConfig("config-slave.json", "config-host.json");
|
||||
|
||||
let instancesFile = path.join("database", "instances.json");
|
||||
if (await fs.pathExists(instancesFile)) {
|
||||
if (await pathExists(instancesFile)) {
|
||||
await safeOutputFile(instancesFile, JSON.stringify(
|
||||
JSON.parse(await fs.readFile(instancesFile)).map(renameConfig), null, "\t"
|
||||
));
|
||||
@@ -241,7 +254,7 @@ async function migrateRename(args) {
|
||||
}
|
||||
|
||||
let usersFile = path.join("database", "users.json");
|
||||
if (await fs.pathExists(usersFile)) {
|
||||
if (await pathExists(usersFile)) {
|
||||
let users = JSON.parse(await fs.readFile(usersFile));
|
||||
for (let role of users["roles"]) {
|
||||
role["permissions"] = role["permissions"].map(rename);
|
||||
@@ -258,7 +271,7 @@ async function migrateRename(args) {
|
||||
|
||||
async function migrateLog(inputFile, outputFile) {
|
||||
let lineStream = new LineSplitter({ readableObjectMode: true });
|
||||
let fileStream = fs.createReadStream(inputFile);
|
||||
let fileStream = (await fs.open(inputFile)).createReadStream();
|
||||
fileStream.pipe(lineStream);
|
||||
let lines = [];
|
||||
|
||||
@@ -278,7 +291,7 @@ async function migrateRename(args) {
|
||||
}
|
||||
|
||||
async function migrateLogsDir(inputLogs, outputLogs) {
|
||||
if (await fs.pathExists(inputLogs)) {
|
||||
if (await pathExists(inputLogs)) {
|
||||
logger.info(`Migrating ${inputLogs} to ${outputLogs}`);
|
||||
for (let logFile of await fs.readdir(inputLogs)) {
|
||||
if (!logFile.endsWith(".log")) {
|
||||
@@ -286,7 +299,7 @@ async function migrateRename(args) {
|
||||
}
|
||||
let inputFile = path.join(inputLogs, logFile);
|
||||
let outputFile = path.join(outputLogs, rename(logFile));
|
||||
if (!await fs.pathExists(outputFile)) {
|
||||
if (!await pathExists(outputFile)) {
|
||||
await migrateLog(inputFile, outputFile);
|
||||
logger.verbose(`Migrated ${inputFile} to ${outputFile}`);
|
||||
}
|
||||
@@ -297,16 +310,16 @@ async function migrateRename(args) {
|
||||
await migrateLogsDir(path.join("logs", "master"), path.join("logs", "controller"));
|
||||
await migrateLogsDir(path.join("logs", "slave"), path.join("logs", "host"));
|
||||
if (
|
||||
await fs.pathExists(path.join("logs", "cluster"))
|
||||
&& !await fs.pathExists(path.join("logs", "cluster-prerename"))
|
||||
await pathExists(path.join("logs", "cluster"))
|
||||
&& !await pathExists(path.join("logs", "cluster-prerename"))
|
||||
) {
|
||||
await fs.rename(path.join("logs", "cluster"), path.join("logs", "cluster-prerename"));
|
||||
await migrateLogsDir(path.join("logs", "cluster-prerename"), path.join("logs", "cluster"));
|
||||
}
|
||||
|
||||
if (
|
||||
await fs.pathExists("sharedMods")
|
||||
&& !await fs.pathExists("mods")
|
||||
await pathExists("sharedMods")
|
||||
&& !await pathExists("mods")
|
||||
) {
|
||||
logger.info("Moving sharedMods/ to mods/");
|
||||
await fs.rename("sharedMods", "mods");
|
||||
@@ -328,8 +341,8 @@ async function migrateRename(args) {
|
||||
await execFile(`npm${scriptExt}`, ["update"]);
|
||||
}
|
||||
|
||||
const hasRunMaster = await fs.pathExists("run-master.sh") || await fs.pathExists("run-master.cmd");
|
||||
const hasRunSlave = await fs.pathExists("run-slave.sh") || await fs.pathExists("run-slave.cmd");
|
||||
const hasRunMaster = await pathExists("run-master.sh") || await pathExists("run-master.cmd");
|
||||
const hasRunSlave = await pathExists("run-slave.sh") || await pathExists("run-slave.cmd");
|
||||
if (hasRunMaster || hasRunSlave) {
|
||||
logger.info("Writing run scripts");
|
||||
let mode = "standalone";
|
||||
@@ -354,7 +367,7 @@ async function migrateRename(args) {
|
||||
|
||||
async function installClusterio(mode, plugins) {
|
||||
try {
|
||||
await fs.outputFile("package.json", JSON.stringify({
|
||||
await fs.writeFile("package.json", JSON.stringify({
|
||||
name: "clusterio-install",
|
||||
private: true,
|
||||
}, null, 2), { flag: "wx" });
|
||||
@@ -643,7 +656,7 @@ async function inquirerMissingArgs(args) {
|
||||
let locations = factorioLocations[process.platform] || [];
|
||||
let foundLocations = [];
|
||||
for (let location of locations) {
|
||||
if (await fs.pathExists(path.join(location, "data", "changelog.txt"))) {
|
||||
if (await pathExists(path.join(location, "data", "changelog.txt"))) {
|
||||
foundLocations.push(location);
|
||||
}
|
||||
}
|
||||
@@ -687,7 +700,7 @@ async function inquirerMissingArgs(args) {
|
||||
},
|
||||
], answers);
|
||||
} else if (answers.factorioDir === "factorio") {
|
||||
await fs.ensureDir("factorio");
|
||||
await fs.mkdir("factorio", { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,20 +785,21 @@ async function downloadLinuxServer() {
|
||||
const factorioDir = `factorio/${version}/`;
|
||||
const tmpFactorioDir = tmpDir + version;
|
||||
|
||||
if (await fs.pathExists(factorioDir)) {
|
||||
if (await pathExists(factorioDir)) {
|
||||
logger.warn(`setting downloadDir to ${factorioDir}, but not downloading because already existing`);
|
||||
} else {
|
||||
await fs.emptyDir(tmpDir);
|
||||
await fs.rm(tmpDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(tmpDir, { recursive: true });
|
||||
|
||||
logger.info("Downloading latest Factorio server release. This may take a while.");
|
||||
const writeStream = fs.createWriteStream(tmpArchivePath);
|
||||
const writeStream = (await fs.open(tmpArchivePath, "w")).createWriteStream();
|
||||
stream.Readable.fromWeb(res.body).pipe(writeStream);
|
||||
|
||||
await finished(writeStream);
|
||||
|
||||
await fs.rename(tmpArchivePath, archivePath);
|
||||
try {
|
||||
await fs.ensureDir(tmpFactorioDir);
|
||||
await fs.mkdir(tmpFactorioDir, { recursive: true });
|
||||
await execFile("tar", [
|
||||
"xf", archivePath, "-C", tmpFactorioDir, "--strip-components", "1",
|
||||
]);
|
||||
@@ -794,7 +808,7 @@ async function downloadLinuxServer() {
|
||||
throw e;
|
||||
}
|
||||
|
||||
await fs.ensureDir("factorio");
|
||||
await fs.mkdir("factorio", { recursive: true });
|
||||
await fs.rename(tmpFactorioDir, factorioDir);
|
||||
await fs.unlink(archivePath);
|
||||
await fs.rm(tmpDir);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"inquirer": "^8.2.4",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const { logger } = require("./logging");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const _package = require("./package.json");
|
||||
@@ -61,9 +61,15 @@ async function copyTemplateFile(src, dst, properties) {
|
||||
return include ? content : "";
|
||||
});
|
||||
|
||||
// Create folder for output file
|
||||
const dir = path.parse(dst);
|
||||
if (dir) {
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
}
|
||||
|
||||
// Attempt to write the output file, warn if it already exists
|
||||
try {
|
||||
await fs.outputFile(dst, outputData, { flag: "wx" });
|
||||
await fs.writeFile(dst, outputData, { flag: "wx" });
|
||||
} catch (err) {
|
||||
if (err.code === "EEXIST") {
|
||||
logger.warn(`Could not create file ${dst} because it already exists`);
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* Command line interface for controlling a Clusterio cluster
|
||||
* @module ctl/ctl
|
||||
*/
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import yargs, { type Argv } from "yargs";
|
||||
import path from "path";
|
||||
import { version } from "./package.json";
|
||||
|
||||
@@ -21,17 +21,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"set-blocking": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"winston": "catalog:",
|
||||
"as-table": "^1.0.55",
|
||||
"set-blocking": "catalog:",
|
||||
"winston": "catalog:",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/set-blocking": "catalog:",
|
||||
"@types/yargs": "^17.0.33",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import asTableModule from "as-table";
|
||||
import events from "events";
|
||||
import os from "os";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import asTableModule from "as-table";
|
||||
import events from "events";
|
||||
import os from "os";
|
||||
@@ -87,7 +87,7 @@ hostCommands.add(new lib.Command({
|
||||
} else {
|
||||
logger.info(`Writing ${args.output}`);
|
||||
try {
|
||||
await fs.outputFile(args.output, content, { flag: "wx" });
|
||||
await fs.writeFile(args.output, content, { flag: "wx" });
|
||||
} catch (err: any) {
|
||||
if (err.code === "EEXIST") {
|
||||
throw new lib.CommandError(`File ${args.output} already exists`);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import asTableModule from "as-table";
|
||||
import events from "events";
|
||||
import path from "path";
|
||||
@@ -338,7 +338,7 @@ instanceSaveCommands.add(new lib.Command({
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
body: await fs.createReadStream(args.filepath),
|
||||
body: (await fs.open(args.filepath)).createReadStream(),
|
||||
duplex: "half",
|
||||
headers: {
|
||||
"X-Access-Token": control.config.get("control.controller_token")!,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import asTableModule from "as-table";
|
||||
import events from "events";
|
||||
import path from "path";
|
||||
@@ -151,7 +151,7 @@ modCommands.add(new lib.Command({
|
||||
|
||||
let response = await fetch(url, {
|
||||
method: "POST",
|
||||
body: fs.createReadStream(args.file),
|
||||
body: (await fs.open(args.file)).createReadStream(),
|
||||
duplex: "half",
|
||||
headers: {
|
||||
"X-Access-Token": control.config.get("control.controller_token")!,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import asTableModule from "as-table";
|
||||
import path from "path";
|
||||
|
||||
@@ -224,7 +224,7 @@ async function handleImportOrRestore(args: {
|
||||
|
||||
print(`${restore ? "Restored" : "Imported"} ${importType} from ${args.filepath}`);
|
||||
if (restore && backup && !args.noBackup) {
|
||||
await fs.writeJSON(`${importType}-backup.json`, backup, { spaces: 2 });
|
||||
await fs.writeFile(`${importType}-backup.json`, JSON.stringify(backup, undefined, "\t"));
|
||||
print(`Wrote backup to ${importType}-backup.json`);
|
||||
}
|
||||
}
|
||||
@@ -296,9 +296,9 @@ userCommands.add(new lib.Command({
|
||||
exportType = "whitelist";
|
||||
}
|
||||
|
||||
await fs.writeJSON(args.filepath, await control.send(
|
||||
await fs.writeFile(args.filepath, JSON.stringify(await control.send(
|
||||
new lib.UserBulkExportRequest(exportType)
|
||||
), { spaces: 2 });
|
||||
), undefined, "\t"));
|
||||
print(`Exported ${exportType} to ${args.filepath}`);
|
||||
},
|
||||
}));
|
||||
@@ -12,7 +12,7 @@
|
||||
* @example
|
||||
* npx clusteriohost run
|
||||
*/
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import yargs from "yargs";
|
||||
import setBlocking from "set-blocking";
|
||||
@@ -199,9 +199,9 @@ async function startHost() {
|
||||
await hostConfigLock.acquire();
|
||||
|
||||
// If we get here the command was run
|
||||
await fs.ensureDir(hostConfig.get("host.instances_directory"));
|
||||
await fs.ensureDir(hostConfig.get("host.mods_directory"));
|
||||
await fs.ensureDir("modules");
|
||||
await fs.mkdir(hostConfig.get("host.instances_directory"), { recursive: true });
|
||||
await fs.mkdir(hostConfig.get("host.mods_directory"), { recursive: true });
|
||||
await fs.mkdir("modules", { recursive: true });
|
||||
|
||||
// Set the process title, shows up as the title of the CMD window on windows
|
||||
// and as the process name in ps/top on linux.
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
"dependencies": {
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@sinclair/typebox": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"jimp": "^1.6.0",
|
||||
"jszip": "catalog:",
|
||||
"pidusage": "^3.0.2",
|
||||
@@ -38,7 +37,6 @@
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/pidusage": "^2.0.2",
|
||||
"@types/semver": "^7.5.0",
|
||||
|
||||
+19
-17
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import events from "events";
|
||||
import pidusage from "pidusage";
|
||||
@@ -294,7 +294,7 @@ export default class Host extends lib.Link {
|
||||
|
||||
static async bootstrap(hostConfig: lib.HostConfig) {
|
||||
const modsDirectory = hostConfig.get("host.mods_directory");
|
||||
await fs.ensureDir(modsDirectory);
|
||||
await fs.mkdir(modsDirectory, { recursive: true });
|
||||
return [
|
||||
await lib.ModStore.fromDirectory(modsDirectory),
|
||||
] as const;
|
||||
@@ -847,7 +847,7 @@ export default class Host extends lib.Link {
|
||||
// First check the clusterio version
|
||||
const runningVersion = this.config.get("host.version");
|
||||
const packageJsonPath = require.resolve("@clusterio/host/package.json");
|
||||
const packageJson = await fs.readJSON(packageJsonPath);
|
||||
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
||||
if (runningVersion !== packageJson.version) {
|
||||
this.config.restartRequired = true;
|
||||
return true;
|
||||
@@ -864,7 +864,7 @@ export default class Host extends lib.Link {
|
||||
|
||||
packageName = pluginInfo.npmPackage ?? pluginInfo.name;
|
||||
const pluginPackageJsonPath = require.resolve(path.posix.join(pluginInfo.requirePath, "package.json"));
|
||||
const pluginPackageJson = await fs.readJSON(pluginPackageJsonPath);
|
||||
const pluginPackageJson = JSON.parse(await fs.readFile(pluginPackageJsonPath, "utf8"));
|
||||
if (pluginInfo.version !== pluginPackageJson.version) {
|
||||
this.config.restartRequired = true;
|
||||
return true;
|
||||
@@ -931,12 +931,17 @@ export default class Host extends lib.Link {
|
||||
checkRequestSaveName(newName);
|
||||
let instance = this.getRequestInstance(instanceId);
|
||||
try {
|
||||
await fs.move(
|
||||
// Link save in the new location.
|
||||
await fs.link(
|
||||
path.join(instance.path, "saves", oldName),
|
||||
path.join(instance.path, "saves", newName),
|
||||
{ overwrite: false },
|
||||
);
|
||||
// Unlink save in old location.
|
||||
await fs.unlink(path.join(instance.path, "saves", oldName));
|
||||
} catch (err: any) {
|
||||
if (err.code === "EEXIST") {
|
||||
throw new lib.RequestError(`${newName} already exists`);
|
||||
}
|
||||
if (err.code === "ENOENT") {
|
||||
throw new lib.RequestError(`${oldName} does not exist`);
|
||||
}
|
||||
@@ -951,10 +956,10 @@ export default class Host extends lib.Link {
|
||||
checkRequestSaveName(destination);
|
||||
let instance = this.getRequestInstance(instanceId);
|
||||
try {
|
||||
await fs.copy(
|
||||
await fs.copyFile(
|
||||
path.join(instance.path, "saves", source),
|
||||
path.join(instance.path, "saves", destination),
|
||||
{ overwrite: false, errorOnExist: true },
|
||||
fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE
|
||||
);
|
||||
} catch (err: any) {
|
||||
if (err.code === "ENOENT") {
|
||||
@@ -980,16 +985,15 @@ export default class Host extends lib.Link {
|
||||
|
||||
try {
|
||||
if (copy) {
|
||||
await fs.copy(
|
||||
await fs.copyFile(
|
||||
path.join(sourceInstance.path, "saves", sourceName),
|
||||
path.join(targetInstance.path, "saves", targetName),
|
||||
{ overwrite: true },
|
||||
fs.constants.COPYFILE_FICLONE
|
||||
);
|
||||
} else {
|
||||
await fs.move(
|
||||
await fs.rename(
|
||||
path.join(sourceInstance.path, "saves", sourceName),
|
||||
path.join(targetInstance.path, "saves", targetName),
|
||||
{ overwrite: true },
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
@@ -1051,11 +1055,9 @@ export default class Host extends lib.Link {
|
||||
checkRequestSaveName(name);
|
||||
let instance = this.getRequestInstance(instanceId);
|
||||
|
||||
let content = fs.createReadStream(path.join(instance.path, "saves", name));
|
||||
let content;
|
||||
try {
|
||||
// TODO: Due to a bug in fs-extra the ready event is not emitted here. Replace
|
||||
// "open" with "ready" when fs-extra is replaced with node's fs module.
|
||||
await events.once(content, "open");
|
||||
content = (await fs.open(path.join(instance.path, "saves", name))).createReadStream();
|
||||
} catch (err: any) {
|
||||
if (err.code === "ENOENT") {
|
||||
throw new lib.RequestError(`${name} does not exist`);
|
||||
@@ -1087,7 +1089,7 @@ export default class Host extends lib.Link {
|
||||
|
||||
this.discoveredInstances.delete(instanceId);
|
||||
this.assignedInstances.delete(instanceId);
|
||||
await fs.remove(instance.path);
|
||||
await fs.rm(instance.path, { recursive: true, maxRetries: 10 });
|
||||
}
|
||||
|
||||
async handleHostUpdateRequest(request: lib.HostUpdateRequest) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import pidusage from "pidusage";
|
||||
import util from "util";
|
||||
@@ -755,8 +755,8 @@ end`.replace(/\r?\n/g, " ");
|
||||
* @param instanceDir -
|
||||
*/
|
||||
static async populate_folders(instanceDir: string) {
|
||||
await fs.ensureDir(path.join(instanceDir, "script-output"));
|
||||
await fs.ensureDir(path.join(instanceDir, "saves"));
|
||||
await fs.mkdir(path.join(instanceDir, "script-output"), { recursive: true });
|
||||
await fs.mkdir(path.join(instanceDir, "saves"), { recursive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -786,7 +786,7 @@ end`.replace(/\r?\n/g, " ");
|
||||
|
||||
const mods = await this._host.fetchMods(modPack.mods.values());
|
||||
|
||||
await fs.ensureDir(this.path("mods"));
|
||||
await fs.mkdir(this.path("mods"), { recursive: true });
|
||||
|
||||
// Remove all files
|
||||
for (let entry of await fs.readdir(this.path("mods"), { withFileTypes: true })) {
|
||||
@@ -825,12 +825,12 @@ end`.replace(/\r?\n/g, " ");
|
||||
}
|
||||
|
||||
// Write mod-list.json
|
||||
await fs.outputFile(this.path("mods", "mod-list.json"), JSON.stringify({
|
||||
await fs.writeFile(this.path("mods", "mod-list.json"), JSON.stringify({
|
||||
mods: [...this.activeModPack.mods.values()],
|
||||
}, null, "\t"));
|
||||
|
||||
// Write mod-settings.dat
|
||||
await fs.outputFile(this.path("mods", "mod-settings.dat"), this.activeModPack.toModSettingsDat());
|
||||
await fs.writeFile(this.path("mods", "mod-settings.dat"), this.activeModPack.toModSettingsDat());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -921,7 +921,12 @@ end`.replace(/\r?\n/g, " ");
|
||||
now.getUTCMinutes().toLocaleString("en", { minimumIntegerDigits: 2 }),
|
||||
saveName,
|
||||
);
|
||||
await fs.copy(this.path("saves", saveName), this.path("saves", newName));
|
||||
// This will overwrite the save if this runs twice in the same minute.
|
||||
await fs.copyFile(
|
||||
this.path("saves", saveName),
|
||||
this.path("saves", newName),
|
||||
fs.constants.COPYFILE_FICLONE,
|
||||
);
|
||||
saveName = newName;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Export of item icons and locale
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import { Jimp, JimpMime, type JimpInstance } from "jimp";
|
||||
import JSZip from "jszip";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Library for patching Factorio saves with scenario code.
|
||||
|
||||
import events from "events";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import JSZip from "jszip";
|
||||
import path from "path";
|
||||
import semver from "semver";
|
||||
@@ -93,7 +93,9 @@ export class SaveModule {
|
||||
static async fromPlugin(plugin: BaseInstancePlugin) {
|
||||
let pluginPackagePath = require.resolve(path.posix.join(plugin.info.requirePath, "package.json"));
|
||||
let moduleDirectory = path.join(path.dirname(pluginPackagePath), "module");
|
||||
if (!await fs.pathExists(moduleDirectory)) {
|
||||
try {
|
||||
await fs.access(moduleDirectory);
|
||||
} catch (err: any) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+13
-12
@@ -1,5 +1,6 @@
|
||||
// Factorio Server interface
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import { type FSWatcher, watch as fsWatch, writeFileSync } from "node:fs";
|
||||
import child_process from "child_process";
|
||||
import path from "path";
|
||||
import JSZip from "jszip";
|
||||
@@ -116,7 +117,7 @@ async function downloadAndExtractZip(url: string, targetDir: string) {
|
||||
const zip = await JSZip.loadAsync(buffer);
|
||||
|
||||
// Extract each file/folder
|
||||
await fs.ensureDir(targetDir);
|
||||
await fs.mkdir(targetDir, { recursive: true });
|
||||
await Promise.all(
|
||||
Object.values(zip.files).map(async (entry) => {
|
||||
const parts = entry.name.split("/").filter(Boolean);
|
||||
@@ -127,9 +128,9 @@ async function downloadAndExtractZip(url: string, targetDir: string) {
|
||||
|
||||
const entryPath = path.join(targetDir, strippedPath);
|
||||
if (entry.dir) {
|
||||
await fs.ensureDir(entryPath);
|
||||
await fs.mkdir(entryPath, { recursive: true });
|
||||
} else {
|
||||
await fs.ensureDir(path.dirname(entryPath));
|
||||
await fs.mkdir(path.dirname(entryPath), { recursive: true });
|
||||
await fs.writeFile(entryPath, await entry.async("nodebuffer"));
|
||||
}
|
||||
})
|
||||
@@ -153,7 +154,7 @@ async function downloadAndExtractTar(url: string, targetDir: string) {
|
||||
const tmpFilePath = `${targetDir}.tmp`;
|
||||
const buffer = Buffer.from(await res.arrayBuffer());
|
||||
await fs.writeFile(tmpFilePath, buffer);
|
||||
await fs.ensureDir(targetDir);
|
||||
await fs.mkdir(targetDir, { recursive: true });
|
||||
|
||||
// Execute tar CLI to extract
|
||||
try {
|
||||
@@ -165,7 +166,7 @@ async function downloadAndExtractTar(url: string, targetDir: string) {
|
||||
throw err;
|
||||
} finally {
|
||||
// Clean up temp file
|
||||
await fs.remove(tmpFilePath);
|
||||
await fs.rm(tmpFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,7 +632,7 @@ export class FactorioServer extends events.EventEmitter<FactorioServerEvents> {
|
||||
|
||||
// Due to inconsistencies in the factorio api, we must manually watch the whitelist
|
||||
// https://forums.factorio.com/viewtopic.php?t=123673
|
||||
_whitelistWatcher: fs.FSWatcher | null = null;
|
||||
_whitelistWatcher: FSWatcher | null = null;
|
||||
_whitelist = new Set<string>();
|
||||
|
||||
_logger: lib.Logger;
|
||||
@@ -885,7 +886,7 @@ export class FactorioServer extends events.EventEmitter<FactorioServerEvents> {
|
||||
|
||||
const filePath = this.writePath("server-whitelist.json");
|
||||
try {
|
||||
this._whitelistWatcher = fs.watch(filePath);
|
||||
this._whitelistWatcher = fsWatch(filePath);
|
||||
} catch (err: any) {
|
||||
this._logger.warn(`Unable to watch whitelist, bidirectional sync will not be available:\n${err}`);
|
||||
return;
|
||||
@@ -907,7 +908,7 @@ export class FactorioServer extends events.EventEmitter<FactorioServerEvents> {
|
||||
|
||||
let newWhitelistJson;
|
||||
try {
|
||||
newWhitelistJson = await fs.readJSON(filePath);
|
||||
newWhitelistJson = JSON.parse(await fs.readFile(filePath, "utf8"));
|
||||
} catch (err: any) {
|
||||
this._logger.error(`Unable to read whitelist, bidirectional sync will not be available:\n${err}`);
|
||||
return;
|
||||
@@ -1527,20 +1528,20 @@ export class FactorioServer extends events.EventEmitter<FactorioServerEvents> {
|
||||
});
|
||||
// Must be sync to allow process spawn without awaiting
|
||||
// eslint-disable-next-line node/no-sync
|
||||
fs.writeFileSync(this.writePath("config.ini"), content);
|
||||
writeFileSync(this.writePath("config.ini"), content);
|
||||
}
|
||||
|
||||
_writeMapSettingsSync(mapGenSettings?: object, mapSettings?: object) {
|
||||
// Must be sync to allow process spawn without awaiting
|
||||
if (mapGenSettings) {
|
||||
// eslint-disable-next-line node/no-sync
|
||||
fs.writeFileSync(
|
||||
writeFileSync(
|
||||
this.writePath("map-gen-settings.json"), JSON.stringify(mapGenSettings, null, "\t")
|
||||
);
|
||||
}
|
||||
if (mapSettings) {
|
||||
// eslint-disable-next-line node/no-sync
|
||||
fs.writeFileSync(
|
||||
writeFileSync(
|
||||
this.writePath("map-settings.json"), JSON.stringify(mapSettings, null, "\t")
|
||||
);
|
||||
}
|
||||
|
||||
+38
-26
@@ -1,9 +1,7 @@
|
||||
/* eslint-disable no-console */
|
||||
"use strict";
|
||||
const events = require("events");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const JSZip = require("jszip");
|
||||
const klaw = require("klaw");
|
||||
const path = require("path");
|
||||
const stream = require("stream");
|
||||
const util = require("util");
|
||||
@@ -14,30 +12,36 @@ const finished = util.promisify(stream.finished);
|
||||
|
||||
async function buildMod(args, info, modName) {
|
||||
if (args.build) {
|
||||
await fs.ensureDir(args.outputDir);
|
||||
await fs.mkdir(args.outputDir, { recursive: true });
|
||||
modName = args.modName ?? `${info.name}_${info.version}`;
|
||||
|
||||
if (args.pack) {
|
||||
let zip = new JSZip();
|
||||
let walker = klaw(args.sourceDir)
|
||||
.on("data", item => {
|
||||
if (item.stats.isFile()) {
|
||||
// On Windows the path created uses backslashes as the directory sepparator
|
||||
// but the zip file needs to use forward slashes. We can't use the posix
|
||||
// version of relative here as it doesn't work with Windows style paths.
|
||||
let basePath = path.relative(args.sourceDir, item.path).replace(/\\/g, "/");
|
||||
zip.file(path.posix.join(modName, basePath), fs.createReadStream(item.path));
|
||||
for await (const dirent of await fs.opendir(args.sourceDir, { recursive: true })) {
|
||||
if (dirent.isFile()) {
|
||||
const itemPath = path.join(dirent.parentPath, dirent.name);
|
||||
// On Windows the path created uses backslashes as the directory sepparator
|
||||
// but the zip file needs to use forward slashes. We can't use the posix
|
||||
// version of relative here as it doesn't work with Windows style paths.
|
||||
let basePath = path.relative(args.sourceDir, itemPath).replace(/\\/g, "/");
|
||||
// eslint-disable-next-line max-depth
|
||||
if (basePath === "info.json") {
|
||||
// The info.json file is overridden later.
|
||||
continue;
|
||||
}
|
||||
})
|
||||
;
|
||||
await events.once(walker, "end");
|
||||
zip.file(path.posix.join(modName, basePath), (await fs.open(itemPath)).createReadStream());
|
||||
}
|
||||
}
|
||||
|
||||
for (let [fileName, pathParts] of Object.entries(info.additional_files || {})) {
|
||||
let filePath = path.join(args.sourceDir, ...pathParts);
|
||||
if (!await fs.pathExists(filePath)) {
|
||||
throw new Error(`Additional file ${filePath} does not exist`);
|
||||
let fileStream;
|
||||
try {
|
||||
fileStream = (await fs.open(filePath)).createReadStream();
|
||||
} catch (err) {
|
||||
throw new Error(`Error reading additional file ${filePath}`, { cause: err });
|
||||
}
|
||||
zip.file(path.posix.join(modName, fileName), fs.createReadStream(filePath));
|
||||
zip.file(path.posix.join(modName, fileName), fileStream);
|
||||
}
|
||||
delete info.additional_files;
|
||||
|
||||
@@ -45,20 +49,28 @@ async function buildMod(args, info, modName) {
|
||||
|
||||
let modPath = path.join(args.outputDir, `${modName}.zip`);
|
||||
console.log(`Writing ${modPath}`);
|
||||
let writeStream = zip.generateNodeStream().pipe(fs.createWriteStream(modPath));
|
||||
let writeStream = zip.generateNodeStream().pipe(
|
||||
(await fs.open(modPath, "w")).createWriteStream()
|
||||
);
|
||||
await finished(writeStream);
|
||||
|
||||
} else {
|
||||
let modDir = path.join(args.outputDir, modName);
|
||||
if (await fs.exists(modDir)) {
|
||||
console.log(`Removing existing build ${modDir}`);
|
||||
await fs.remove(modDir);
|
||||
}
|
||||
console.log(`Removing potentially existing build ${modDir}`);
|
||||
await fs.rm(modDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
console.log(`Building ${modDir}`);
|
||||
await fs.copy(args.sourceDir, modDir);
|
||||
await fs.cp(args.sourceDir, modDir, {
|
||||
errorOnExists: true,
|
||||
mode: fs.constants.COPYFILE_FICLONE,
|
||||
recursive: true,
|
||||
});
|
||||
for (let [fileName, pathParts] of Object.entries(info.additional_files) || []) {
|
||||
let filePath = path.join(...pathParts);
|
||||
await fs.copy(filePath, path.join(modDir, fileName));
|
||||
await fs.copyFile(
|
||||
filePath,
|
||||
path.join(modDir, fileName),
|
||||
fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE,
|
||||
);
|
||||
}
|
||||
delete info.additional_files;
|
||||
|
||||
@@ -87,7 +99,7 @@ async function build(args) {
|
||||
if (name === info.name) {
|
||||
let modPath = path.join(args.outputDir, entry);
|
||||
console.log(`Removing ${modPath}`);
|
||||
await fs.remove(modPath);
|
||||
await fs.rm(modPath, { force: true, recursive: true, maxRetries: 10 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,28 +19,25 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "catalog:",
|
||||
"set-blocking": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"jszip": "catalog:",
|
||||
"ajv": "^8.17.1",
|
||||
"chalk": "catalog:",
|
||||
"ws": "catalog:",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"jszip": "catalog:",
|
||||
"set-blocking": "catalog:",
|
||||
"triple-beam": "^1.4.1",
|
||||
"winston": "catalog:",
|
||||
"winston-transport": "catalog:",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"triple-beam": "^1.4.1",
|
||||
"ajv": "^8.17.1",
|
||||
"klaw": "^4.1.0",
|
||||
"ws": "catalog:",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/ws": "catalog:",
|
||||
"@types/set-blocking": "catalog:",
|
||||
"@types/triple-beam": "^1.3.2",
|
||||
"@types/ws": "catalog:",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs");
|
||||
const path = require("path");
|
||||
const Ajv = require("ajv");
|
||||
const standaloneCode = require("ajv/dist/standalone").default;
|
||||
@@ -7,7 +7,7 @@ const standaloneCode = require("ajv/dist/standalone").default;
|
||||
/* eslint-disable node/no-sync */
|
||||
|
||||
// Provide an empty file for message_core to load
|
||||
fs.outputFileSync(path.join(__dirname, "../dist/node/src/data/message_validate.js"), "");
|
||||
fs.writeFileSync(path.join(__dirname, "../dist/node/src/data/message_validate.js"), "");
|
||||
|
||||
const { Message } = require("../dist/node/src/data/messages_core");
|
||||
|
||||
@@ -23,7 +23,7 @@ const ajv = new Ajv({
|
||||
const validate = ajv.compile(Message.jsonSchema);
|
||||
let moduleCode = standaloneCode(ajv, validate);
|
||||
function write(output) {
|
||||
fs.outputFileSync(
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, output),
|
||||
`// generated by scripts/compile_validator.js\n// @ts-nocheck\n/* eslint-disable */\n${moduleCode}`
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { FileHandle, open, unlink, readFile } from "fs/promises";
|
||||
import { unlinkSync } from "fs";
|
||||
import { FileHandle, open, unlink, readFile } from "node:fs/promises";
|
||||
import { unlinkSync } from "node:fs";
|
||||
import assert from "assert";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import events from "events";
|
||||
import { Static } from "@sinclair/typebox";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Configuration classes
|
||||
import { Type, Static } from "@sinclair/typebox";
|
||||
import * as fs from "fs-extra";
|
||||
import * as fs from "node:fs/promises";
|
||||
import events from "events";
|
||||
|
||||
import isDeepStrictEqual from "../is_deep_strict_equal";
|
||||
@@ -446,7 +446,7 @@ export class Config<
|
||||
* @returns Instance of this config
|
||||
*/
|
||||
static async fromFile(location: ConfigLocation, filepath: string) {
|
||||
const fields = await fs.readJson(filepath, { encoding: "utf8" });
|
||||
const fields = JSON.parse(await fs.readFile(filepath, { encoding: "utf8" }));
|
||||
return this.fromJSON(fields, location, filepath);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import JSZip from "jszip";
|
||||
import { Type, Static } from "@sinclair/typebox";
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @module lib/database
|
||||
* @author Hornwitser
|
||||
*/
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
import * as libFileOps from "./file_ops";
|
||||
import { basicType } from "./helpers";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from "path";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
import { safeOutputFile } from "./file_ops";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
import crypto from "crypto";
|
||||
import events from "events";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import stream from "stream";
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function getNewestFile(directory: string, filter = (_name: string)
|
||||
* @returns The size in bytes of all files in the directory.
|
||||
*/
|
||||
export async function directorySize(directory: string) {
|
||||
let dirEntries: fs.Dirent[];
|
||||
let dirEntries;
|
||||
try {
|
||||
dirEntries = await fs.readdir(directory, { withFileTypes: true });
|
||||
} catch (err: any) {
|
||||
@@ -91,8 +91,12 @@ export async function findUnusedName(directory: string, name: string, extension
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!await fs.pathExists(path.join(directory, `${name}${extension}`))) {
|
||||
return `${name}${extension}`;
|
||||
try {
|
||||
await fs.access(path.join(directory, `${name}${extension}`));
|
||||
} catch (err: any) {
|
||||
if (err.code === "ENOENT") {
|
||||
return `${name}${extension}`;
|
||||
}
|
||||
}
|
||||
|
||||
let match = /^(.*?)(-(\d+))?$/.exec(name)!;
|
||||
@@ -125,12 +129,13 @@ export async function getTempFile(prefix = "tmp.", suffix = "", tmpdir = "./") {
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely write data to a file
|
||||
* Safely overwrite file data
|
||||
*
|
||||
* Same as fs-extra.outputFile except the data is written to a temporary
|
||||
* file that's renamed over the target file. The name of the temporary file
|
||||
* is the same as the target file with the suffix `.tmp` added before the
|
||||
* extension.
|
||||
* Writes the passed data to a temporary file and rename it over the target
|
||||
* file. If the operation fails because the parent directory does not exist, it
|
||||
* attempts to create the directory, including any ancestors and then retries
|
||||
* the operation. The name of the temporary file is the same as the target file
|
||||
* with the suffix `.tmp` added before the extension.
|
||||
*
|
||||
* If the operation fails it may leave behind the temporary file. This
|
||||
* should not be too much of an issue as the next time the same file is
|
||||
@@ -138,26 +143,32 @@ export async function getTempFile(prefix = "tmp.", suffix = "", tmpdir = "./") {
|
||||
*
|
||||
* @param file - Path to file to write.
|
||||
* @param data - Content to write.
|
||||
* @param options - see fs.writeFile, `flag` must not be set.
|
||||
* @param options - see fs.writeFile, `flag` and `flush` may not be used.
|
||||
*/
|
||||
export async function safeOutputFile(file: string, data: string | Buffer, options: fs.WriteFileOptions ={}) {
|
||||
export async function safeOutputFile(
|
||||
file: string,
|
||||
data: string | Buffer,
|
||||
options?: Omit<
|
||||
Extract<Parameters<typeof fs.writeFile>[2], { flush?: boolean }>,
|
||||
"flag" | "flush"
|
||||
>,
|
||||
) {
|
||||
let { dir, name, ext } = path.parse(file);
|
||||
if (dir && !await fs.pathExists(dir)) {
|
||||
await fs.mkdirs(dir);
|
||||
}
|
||||
let temporary = path.join(dir, `${name}.tmp${ext}`);
|
||||
let fd = await fs.open(temporary, "w");
|
||||
try {
|
||||
await fs.writeFile(fd, data, options);
|
||||
await fs.fsync(fd);
|
||||
} finally {
|
||||
await fs.close(fd);
|
||||
await fs.writeFile(temporary, data, { ...options, flush: true });
|
||||
} catch (err: any) {
|
||||
if (err.code === "ENOENT") {
|
||||
// Try creating the folder and then retry the operation.
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
await fs.writeFile(temporary, data, { ...options, flush: true });
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
await fs.rename(temporary, file);
|
||||
}
|
||||
|
||||
|
||||
// Reserved names by almost all filesystems
|
||||
const badNames = [".", ".."];
|
||||
|
||||
@@ -246,16 +257,14 @@ export function cleanFilename(name: string) {
|
||||
* @param filePath - The target file to create a temporary stream for.
|
||||
* @returns The path to the temporary file that was opened and the write stream to it.
|
||||
*/
|
||||
export async function createTempWriteStream(filePath: string): Promise<[string, fs.WriteStream]> {
|
||||
export async function createTempWriteStream(filePath: string) {
|
||||
const { dir, name: fileName, ext } = path.parse(filePath);
|
||||
let increment = 1;
|
||||
let writeStream;
|
||||
let tempFilePath = path.format({ dir, name: `${fileName}.tmp`, ext });
|
||||
while (true) {
|
||||
try {
|
||||
writeStream = fs.createWriteStream(tempFilePath, { flags: "wx", flush: true });
|
||||
// replace "open" event with "ready" after removing fs-extra
|
||||
await events.once(writeStream, "open");
|
||||
writeStream = (await fs.open(tempFilePath, "wx")).createWriteStream({ flush: true });
|
||||
break;
|
||||
} catch (err: any) {
|
||||
if (err.code === "EEXIST") {
|
||||
@@ -267,7 +276,7 @@ export async function createTempWriteStream(filePath: string): Promise<[string,
|
||||
}
|
||||
}
|
||||
|
||||
return [tempFilePath, writeStream];
|
||||
return [tempFilePath, writeStream] as const;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,9 +300,7 @@ export async function downloadFile(
|
||||
let tempFilePath;
|
||||
if (overwriteMode === "error") {
|
||||
tempFilePath = filePath;
|
||||
writeStream = fs.createWriteStream(tempFilePath, { flags: "wx", flush: true });
|
||||
// replace "open" event with "ready" after removing fs-extra
|
||||
await events.once(writeStream, "open");
|
||||
writeStream = (await fs.open(tempFilePath, "wx")).createWriteStream({ flush: true });
|
||||
} else {
|
||||
[tempFilePath, writeStream] = await createTempWriteStream(filePath);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @module lib/hash
|
||||
*/
|
||||
import crypto from "crypto";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
|
||||
/**
|
||||
@@ -35,6 +35,6 @@ export function hashStream(stream: NodeJS.ReadableStream): Promise<string> {
|
||||
* @param path - Path to the file to hash.
|
||||
* @returns hash of the file given.
|
||||
*/
|
||||
export function hashFile(path: string) {
|
||||
return hashStream(fs.createReadStream(path));
|
||||
export async function hashFile(path: string) {
|
||||
return hashStream((await fs.open(path)).createReadStream());
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import { logger } from "./logging";
|
||||
import * as libFileOps from "./file_ops";
|
||||
@@ -174,12 +174,15 @@ export async function loadPluginList(pluginListPath: string, findLocal = true):
|
||||
*/
|
||||
async function checkPackageJson(pluginPath: string): Promise<boolean> {
|
||||
const packageJsonPath = path.join(pluginPath, "package.json");
|
||||
if (!await fs.exists(packageJsonPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read and parse package.json
|
||||
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, { encoding: "utf8" }));
|
||||
let content;
|
||||
try {
|
||||
content = await fs.readFile(packageJsonPath, { encoding: "utf8" });
|
||||
} catch (err: any) {
|
||||
return false;
|
||||
}
|
||||
const packageJson = JSON.parse(content);
|
||||
|
||||
// Check if package has the clusterio-plugin keyword
|
||||
return packageJson.keywords?.includes("clusterio-plugin");
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
* @author Hornwitser
|
||||
* @module lib/logging_utils
|
||||
*/
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import type { ReadStream } from "node:fs";
|
||||
import path from "path";
|
||||
import stream from "stream";
|
||||
import util from "util";
|
||||
@@ -320,7 +321,7 @@ export class LogIndex {
|
||||
async indexFile(file: string) {
|
||||
let filePath = path.join(this.logDirectory, file);
|
||||
let lineStream = new libStream.LineSplitter({ readableObjectMode: true });
|
||||
let fileStream = fs.createReadStream(filePath);
|
||||
let fileStream = (await fs.open(filePath)).createReadStream();
|
||||
fileStream.pipe(lineStream);
|
||||
let entry = {
|
||||
levels: new Set() as Set<keyof typeof levels>,
|
||||
@@ -478,11 +479,11 @@ export async function queryLog(logDirectory: string, filter: QueryLogFilter, ind
|
||||
continue;
|
||||
}
|
||||
let filePath = path.join(logDirectory, file);
|
||||
let fileStream: fs.ReadStream;
|
||||
let fileStream: ReadStream;
|
||||
let lineStream: libStream.LineSplitter;
|
||||
if (filter.order === "asc") {
|
||||
lineStream = new libStream.LineSplitter({ readableObjectMode: true });
|
||||
fileStream = fs.createReadStream(filePath);
|
||||
fileStream = (await fs.open(filePath)).createReadStream();
|
||||
} else {
|
||||
lineStream = new libStream.ReverseLineSplitter({ readableObjectMode: true });
|
||||
fileStream = await libStream.createReverseReadStream(filePath);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @module lib/plugin_loader
|
||||
*/
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import fs from "node:fs/promises";
|
||||
import * as libErrors from "./errors";
|
||||
import * as libPlugin from "./plugin";
|
||||
import { logger } from "./logging";
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import assert from "assert";
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs"; // Need the non-promise api for reverse stream
|
||||
import stream from "stream";
|
||||
import util from "node:util";
|
||||
|
||||
const fsOpen = util.promisify(fs.open);
|
||||
const fsFstat = util.promisify(fs.fstat);
|
||||
|
||||
|
||||
/**
|
||||
@@ -13,7 +17,7 @@ import stream from "stream";
|
||||
* ```ts
|
||||
* let lineStream = new lib.LineSplitter({ readableObjectMode: true });
|
||||
* lineStream.on("data", line => { console.log(line.toString("utf8")); });
|
||||
* let fileStream = fs.createReadStream(path);
|
||||
* let fileStream = (await fs.open(path)).createReadStream();
|
||||
* fileStream.pipe(lineStream);
|
||||
* ```
|
||||
*/
|
||||
@@ -141,8 +145,8 @@ export async function createReverseReadStream(path: string, options?: ReadStream
|
||||
if (typeof options === "string") {
|
||||
options = { encoding: options };
|
||||
}
|
||||
const fileFd = await fs.open(path, "r");
|
||||
const fileSize = (await fs.fstat(fileFd)).size;
|
||||
const fileFd = await fsOpen(path, "r");
|
||||
const fileSize = (await fsFstat(fileFd)).size;
|
||||
let filePosition = fileSize;
|
||||
const reverseFs = {
|
||||
read(
|
||||
@@ -162,8 +166,5 @@ export async function createReverseReadStream(path: string, options?: ReadStream
|
||||
open() { assert(false); },
|
||||
close(fd: number, callback: () => void) { return fs.close(fd, callback); },
|
||||
};
|
||||
return fs.createReadStream(
|
||||
"",
|
||||
{ ...options, fd: fileFd, fs: reverseFs } as ReadStreamOptions, // type specification is missing fs prop.
|
||||
);
|
||||
return fs.createReadStream("", { ...options, fd: fileFd, fs: reverseFs });
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "os";
|
||||
import util from "util";
|
||||
import { Gauge } from "./prometheus";
|
||||
@@ -92,14 +92,7 @@ export const systemDiskCapacityBytes = new Gauge(
|
||||
"Size of the filesystem of the current working directory Node.js runs on",
|
||||
{
|
||||
callback: async function(collector) {
|
||||
// statfs was added in Node.js v18.15.0 and may not be present.
|
||||
// TODO: remove this check once minimum supported Node.js >= v18.15.0.
|
||||
if (!fs.statfs) {
|
||||
collector.set(0);
|
||||
return;
|
||||
}
|
||||
const statFsAsync = util.promisify(fs.statfs);
|
||||
const stats = await statFsAsync(".");
|
||||
const stats = await fs.statfs(".");
|
||||
collector.set(stats.blocks * stats.bsize);
|
||||
},
|
||||
},
|
||||
@@ -110,13 +103,7 @@ export const systemDiskAvailableBytes = new Gauge(
|
||||
"Available space on the filesystem of the current working directory Node.js runs on",
|
||||
{
|
||||
callback: async function(collector) {
|
||||
// TODO: remove this check once minimum supported Node.js >= v18.15.0.
|
||||
if (!fs.statfs) {
|
||||
collector.set(0);
|
||||
return;
|
||||
}
|
||||
const statFsAsync = util.promisify(fs.statfs);
|
||||
const stats = await statFsAsync(".");
|
||||
const stats = await fs.statfs(".");
|
||||
collector.set(stats.bavail * stats.bsize);
|
||||
},
|
||||
},
|
||||
@@ -148,14 +135,9 @@ export async function gatherSystemInfo(
|
||||
const cpuUsage = minZip(deltaTotalCpuMs, deltaIdleCpuMs).map(([total, idle]) => (total - idle) / total);
|
||||
previousTotalCpuMs = currentTotalCpuMs;
|
||||
previousIdleCpuMs = currentIdleCpuMs;
|
||||
let diskCapacity = 0;
|
||||
let diskAvailable = 0;
|
||||
if (fs.statfs) { // TODO: remove this check once minimum supported Node.js >= v18.15.0.
|
||||
const statFsAsync = util.promisify(fs.statfs);
|
||||
const stats = await statFsAsync(".");
|
||||
diskCapacity = stats.blocks * stats.bsize;
|
||||
diskAvailable = stats.bavail * stats.bsize;
|
||||
}
|
||||
const stats = await fs.statfs(".");
|
||||
let diskCapacity = stats.blocks * stats.bsize;
|
||||
let diskAvailable = stats.bavail * stats.bsize;
|
||||
|
||||
const now = Date.now();
|
||||
return new SystemInfo(
|
||||
|
||||
@@ -45,10 +45,6 @@ module.exports = (env = {}, argv = {}) => ({
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /node_modules.fs-extra/,
|
||||
use: require.resolve("null-loader"),
|
||||
},
|
||||
{
|
||||
test: /node_modules.winston.dist.winston.transports.(http|file)/,
|
||||
use: require.resolve("null-loader"),
|
||||
@@ -124,6 +120,8 @@ module.exports = (env = {}, argv = {}) => ({
|
||||
extensions: [".js", ".ts", ".json", ".jsx", ".tsx"],
|
||||
fallback: {
|
||||
"crypto": false,
|
||||
"fs": false,
|
||||
"fs/promises": false,
|
||||
|
||||
// Required for winston
|
||||
"util": require.resolve("util/"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BaseControllerPlugin, type InstanceRecord } from "@clusterio/controller";
|
||||
import type { IpcPlayerData } from "./messages";
|
||||
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import * as lib from "@clusterio/lib";
|
||||
import * as msg from "./messages";
|
||||
|
||||
@@ -24,20 +24,18 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@clusterio/controller": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@clusterio/host": "workspace:*",
|
||||
"@clusterio/lib": "workspace:*"
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "catalog:",
|
||||
"fs-extra": "catalog:"
|
||||
"@sinclair/typebox": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/controller": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@clusterio/host": "workspace:*",
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@types/node": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"antd": "catalog:",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import { BaseControllerPlugin } from "@clusterio/controller";
|
||||
import { Static } from "@sinclair/typebox";
|
||||
|
||||
@@ -24,20 +24,18 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@clusterio/controller": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@clusterio/host": "workspace:*",
|
||||
"@clusterio/lib": "workspace:*"
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "catalog:",
|
||||
"fs-extra": "catalog:"
|
||||
"@sinclair/typebox": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/controller": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@clusterio/host": "workspace:*",
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@types/node": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BaseControllerPlugin, type ControlConnection } from "@clusterio/controller";
|
||||
|
||||
import fs from "fs-extra";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
|
||||
import * as lib from "@clusterio/lib";
|
||||
|
||||
@@ -24,21 +24,19 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@clusterio/controller": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@clusterio/host": "workspace:*",
|
||||
"@clusterio/lib": "workspace:*"
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "catalog:",
|
||||
"fs-extra": "catalog:"
|
||||
"@sinclair/typebox": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/controller": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@clusterio/host": "workspace:*",
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@clusterio/web_ui": "workspace:*",
|
||||
"@types/express": "catalog:",
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"antd": "catalog:",
|
||||
|
||||
+33
-33
@@ -1,40 +1,40 @@
|
||||
packages:
|
||||
- "packages/*"
|
||||
- "plugins/*"
|
||||
- "external_plugins/*"
|
||||
- "external_plugins/*/*"
|
||||
- "test/"
|
||||
- packages/*
|
||||
- plugins/*
|
||||
- external_plugins/*
|
||||
- external_plugins/*/*
|
||||
- test/
|
||||
|
||||
catalog:
|
||||
"@sinclair/typebox": ^0.30.4
|
||||
"@swc/core": ^1.13.3
|
||||
"@types/express": ^4.17.17
|
||||
"@types/finalhandler": ^1.2.0
|
||||
"@types/fs-extra": ^11.0.1
|
||||
"@types/jsonwebtoken": ^9.0.9
|
||||
"@types/node": ^20.17.19
|
||||
"@types/react": ^18.3.18
|
||||
"@types/set-blocking": ^2.0.0
|
||||
"@types/ws": ^8.5.14
|
||||
"antd": ^5.24.2
|
||||
"chalk": ^4.1.2
|
||||
"express": ^5.1.0
|
||||
"fs-extra": ^8.1.0
|
||||
"jsonwebtoken": ^9.0.2
|
||||
"jszip": ^3.10.1
|
||||
"react": ^18.2.0
|
||||
"react-dom": ^18.2.0
|
||||
"react-router-dom": ^6.29.0
|
||||
"set-blocking": ^2.0.0
|
||||
"typescript": ^5.7.3
|
||||
"webpack": ^5.98.0
|
||||
"webpack-cli": ^5.1.4
|
||||
"webpack-merge": ^5.9.0
|
||||
"winston": ^3.17.0
|
||||
"winston-daily-rotate-file": ^4.7.1
|
||||
"winston-transport": ^4.9.0
|
||||
"ws": ^8.18.1
|
||||
'@sinclair/typebox': ^0.30.4
|
||||
'@swc/core': ^1.13.3
|
||||
'@types/express': ^4.17.17
|
||||
'@types/finalhandler': ^1.2.0
|
||||
'@types/jsonwebtoken': ^9.0.9
|
||||
'@types/node': ^20.17.19
|
||||
'@types/react': ^18.3.18
|
||||
'@types/set-blocking': ^2.0.0
|
||||
'@types/ws': ^8.5.14
|
||||
antd: ^5.24.2
|
||||
chalk: ^4.1.2
|
||||
express: ^5.1.0
|
||||
jsonwebtoken: ^9.0.2
|
||||
jszip: ^3.10.1
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
react-router-dom: ^6.29.0
|
||||
set-blocking: ^2.0.0
|
||||
typescript: ^5.7.3
|
||||
webpack: ^5.98.0
|
||||
webpack-cli: ^5.1.4
|
||||
webpack-merge: ^5.9.0
|
||||
winston: ^3.17.0
|
||||
winston-daily-rotate-file: ^4.7.1
|
||||
winston-transport: ^4.9.0
|
||||
ws: ^8.18.1
|
||||
|
||||
catalogMode: strict
|
||||
|
||||
linkWorkspacePackages: true
|
||||
|
||||
shellEmulator: true
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
"use strict";
|
||||
const FormData = require("form-data");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const yargs = require("yargs");
|
||||
|
||||
let DRY = false;
|
||||
@@ -62,7 +62,7 @@ async function uploadModRelease(file, name) {
|
||||
// Upload the new version of the mod
|
||||
const uploadUrl = (await initResponse.json()).upload_url;
|
||||
const uploadFormData = new FormData();
|
||||
uploadFormData.append("file", fs.createReadStream(file));
|
||||
uploadFormData.append("file", (await fs.open(file)).createReadStream(file));
|
||||
|
||||
const uploadResponse = await fetch(uploadUrl, {
|
||||
method: "POST",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
const util = require("util");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const assert = require("assert").strict;
|
||||
const jwt = require("jsonwebtoken");
|
||||
const { Role, SubscribableDatastore, ControllerConfig } = require("@clusterio/lib");
|
||||
@@ -26,7 +26,8 @@ describe("controller/UserManager", function () {
|
||||
"controller.auth_secret": Buffer.from("mysecret").toString("base64"),
|
||||
});
|
||||
userManager = new UserManager(records, roles, config);
|
||||
await fs.emptyDir(TEMP_DIR);
|
||||
await fs.rm(TEMP_DIR, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(TEMP_DIR, { recursive: true });
|
||||
});
|
||||
|
||||
describe(".has() / .get() / .getMutable() / .hasName() / .getByName() / .getByNameMutable()", function () {
|
||||
@@ -142,14 +143,14 @@ describe("controller/UserManager", function () {
|
||||
});
|
||||
|
||||
it("should do nothing if roles key missing", async function () {
|
||||
await fs.writeJson(usersFile, { users: [] });
|
||||
await fs.writeFile(usersFile, JSON.stringify({ users: [] }));
|
||||
await UserManager.attemptMigrateLegacyUsersFile(usersFile, rolesFile);
|
||||
const content = await fs.readJson(usersFile);
|
||||
const content = JSON.parse(await fs.readFile(usersFile));
|
||||
assert.deepEqual(content, { users: [] });
|
||||
});
|
||||
|
||||
it("should throw if users key missing in legacy file", async function () {
|
||||
await fs.writeJson(usersFile, { roles: [] });
|
||||
await fs.writeFile(usersFile, JSON.stringify({ roles: [] }));
|
||||
await assert.rejects(
|
||||
() => UserManager.attemptMigrateLegacyUsersFile(usersFile, rolesFile),
|
||||
/Error: Legacy users json does not contain users property/
|
||||
@@ -158,12 +159,12 @@ describe("controller/UserManager", function () {
|
||||
|
||||
it("should migrate valid legacy file", async function () {
|
||||
const legacy = { roles: [{ id: 1 }, { id: 2 }], users: [{ name: "Karl" }] };
|
||||
await fs.writeJson(usersFile, legacy);
|
||||
await fs.writeFile(usersFile, JSON.stringify(legacy));
|
||||
|
||||
await UserManager.attemptMigrateLegacyUsersFile(usersFile, rolesFile);
|
||||
|
||||
const writtenRoles = await fs.readJson(rolesFile);
|
||||
const writtenUsers = await fs.readJson(usersFile);
|
||||
const writtenRoles = JSON.parse(await fs.readFile(rolesFile));
|
||||
const writtenUsers = JSON.parse(await fs.readFile(usersFile));
|
||||
|
||||
assert.deepEqual(writtenRoles, [{ id: 1 }, { id: 2 }]);
|
||||
assert.deepEqual(writtenUsers, [{ name: "Karl" }]);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const JSZip = require("jszip");
|
||||
const path = require("path");
|
||||
|
||||
@@ -231,7 +231,7 @@ describe("host/patch", function() {
|
||||
let zip = new JSZip();
|
||||
zip.file("world/control.lua", "-- unknown\n");
|
||||
let zipPath = path.join("temp", "test", "patch.zip");
|
||||
await fs.outputFile(zipPath, await zip.generateAsync({ type: "nodebuffer" }));
|
||||
await fs.writeFile(zipPath, await zip.generateAsync({ type: "nodebuffer" }));
|
||||
|
||||
await assert.rejects(
|
||||
patch.patch(zipPath, []),
|
||||
|
||||
+12
-8
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const events = require("events");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const hostServer = require("@clusterio/host/dist/node/src/server");
|
||||
@@ -135,9 +135,10 @@ describe("host/server", function() {
|
||||
slowTest(this);
|
||||
const url = "https://github.com/clusterio/clusterio/archive/refs/tags/v2.0.0-alpha.22.zip";
|
||||
const downloads = path.join("temp", "test", "downloads");
|
||||
await fs.emptyDir(downloads);
|
||||
await fs.rm(downloads, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(downloads, { recursive: true });
|
||||
await hostServer._downloadAndExtractZip(url, path.join(downloads, "zip"));
|
||||
assert.ok(await fs.exists(path.join(downloads, "zip", "packages", "controller", "package.json")));
|
||||
await fs.access(path.join(downloads, "zip", "packages", "controller", "package.json"));
|
||||
});
|
||||
it("errors and bad status", async function() {
|
||||
global.fetch = () => ({ ok: false, status: -1, statusText: "Fetch called" });
|
||||
@@ -157,9 +158,10 @@ describe("host/server", function() {
|
||||
it("works", async function() {
|
||||
const url = "https://github.com/clusterio/clusterio/archive/refs/tags/v2.0.0-alpha.22.tar.gz";
|
||||
const downloads = path.join("temp", "test", "downloads");
|
||||
await fs.emptyDir(downloads);
|
||||
await fs.rm(downloads, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(downloads, { recursive: true });
|
||||
await hostServer._downloadAndExtractTar(url, path.join(downloads, "tar"));
|
||||
assert.ok(await fs.exists(path.join(downloads, "tar", "packages", "controller", "package.json")));
|
||||
await fs.access(path.join(downloads, "tar", "packages", "controller", "package.json"));
|
||||
});
|
||||
it("errors and bad status", async function() {
|
||||
global.fetch = () => ({ ok: false, status: -1, statusText: "Fetch called" });
|
||||
@@ -281,13 +283,14 @@ describe("host/server", function() {
|
||||
);
|
||||
});
|
||||
it("should load and delete json file", async function() {
|
||||
await fs.mkdir(server.writePath("script-output"), { recursive: true });
|
||||
let filePath = server.writePath("script-output", "data.json");
|
||||
await fs.outputFile(filePath, '{"data":"spam"}');
|
||||
await fs.writeFile(filePath, '{"data":"spam"}');
|
||||
let waiter = events.once(server, "ipc-channel");
|
||||
await server._handleIpc(Buffer.from("\f$ipc:channel?fdata.json"));
|
||||
let result = await waiter;
|
||||
assert.deepEqual(result[0], { "data": "spam" });
|
||||
assert(!await fs.pathExists(filePath), "File was not deleted");
|
||||
await assert.rejects(fs.access(filePath), "File was not deleted");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -469,7 +472,8 @@ describe("host/server", function() {
|
||||
server._factorioDir = path.join("test", "file", "factorioDownload");
|
||||
server._targetVersion = "latest";
|
||||
global.fetch = _fetch;
|
||||
await fs.emptyDir(server._factorioDir);
|
||||
await fs.rm(server._factorioDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(server._factorioDir, { recursive: true });
|
||||
await server.checkForUpdates([{
|
||||
stable: true,
|
||||
version: "2.0.73",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const path = require("path");
|
||||
const events = require("events");
|
||||
@@ -36,9 +36,9 @@ async function spawnAltHost(config = "alt-host-config.json") {
|
||||
async function startAltHost() {
|
||||
let config = "alt-host-config.json";
|
||||
let configPath = path.join("temp", "test", config);
|
||||
await fs.remove(configPath);
|
||||
await fs.remove(path.join("temp", "test", "alt-instances"));
|
||||
await fs.remove(path.join("temp", "test", "alt-mods"));
|
||||
await fs.rm(configPath, { force: true });
|
||||
await fs.rm(path.join("temp", "test", "alt-instances"), { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.rm(path.join("temp", "test", "alt-mods"), { force: true, recursive: true, maxRetries: 10 });
|
||||
await execCtl(`host create-config --id 5 --name alt-host --generate-token --output ${config}`);
|
||||
await execHost(`--config ${config} config set host.instances_directory alt-instances`);
|
||||
const dir = path.isAbsolute(factorioDir) ? factorioDir : path.join("..", "..", factorioDir);
|
||||
@@ -131,10 +131,10 @@ describe("Integration of Clusterio", function() {
|
||||
try {
|
||||
await execController("bootstrap create-admin BootstrapAdminTest2 --bypass-lock-file");
|
||||
} finally {
|
||||
await fs.move("temp.lock", lockFilePath); // Replace the lockfile after it is deleted
|
||||
await fs.rename("temp.lock", lockFilePath); // Replace the lockfile after it is deleted
|
||||
}
|
||||
|
||||
const json = await fs.readJSON(path.join(databaseDir, "users.json"));
|
||||
const json = JSON.parse(await fs.readFile(path.join(databaseDir, "users.json"), "utf8"));
|
||||
assert.equal(Object.values(json).some(user => user.name === "BootstrapAdminTest2"), true);
|
||||
});
|
||||
});
|
||||
@@ -154,10 +154,10 @@ describe("Integration of Clusterio", function() {
|
||||
try {
|
||||
await execController("config set controller.name ConfigEditTest2 --bypass-lock-file");
|
||||
} finally {
|
||||
await fs.move("temp.lock", lockFilePath); // Replace the lockfile after it is deleted
|
||||
await fs.rename("temp.lock", lockFilePath); // Replace the lockfile after it is deleted
|
||||
}
|
||||
|
||||
const json = await fs.readJSON(path.join(controllerConfigPath));
|
||||
const json = JSON.parse(await fs.readFile(path.join(controllerConfigPath), "utf8"));
|
||||
assert.equal(json["controller.name"], "ConfigEditTest2");
|
||||
});
|
||||
});
|
||||
@@ -197,17 +197,19 @@ describe("Integration of Clusterio", function() {
|
||||
} catch (err) {
|
||||
assert.equal(/Error: Server listening failed/.test(err.stderr), true);
|
||||
}
|
||||
await fs.move("temp.lock", lockFilePath); // Replace the lockfile after it is deleted
|
||||
await fs.rename("temp.lock", lockFilePath); // Replace the lockfile after it is deleted
|
||||
});
|
||||
it("should refuse to start when no users are loaded", async function() {
|
||||
const dir = path.join("temp", "test", "empty_controller");
|
||||
await fs.emptyDir(dir);
|
||||
await fs.rm(dir, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
await assert.rejects(execController("run", { cwd: dir }));
|
||||
});
|
||||
it("should start when no users are loaded if bypass option given", async function() {
|
||||
slowTest(this);
|
||||
const dir = path.join("temp", "test", "empty_controller");
|
||||
await fs.emptyDir(dir);
|
||||
await fs.rm(dir, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
const child = await spawnNode(
|
||||
"altController",
|
||||
"../../../packages/controller run --no-check-user-count",
|
||||
@@ -349,7 +351,7 @@ describe("Integration of Clusterio", function() {
|
||||
await stopAltHost(hostProcess);
|
||||
}
|
||||
|
||||
const json = await fs.readJSON(path.join("temp", "test", config));
|
||||
const json = JSON.parse(await fs.readFile(path.join("temp", "test", config), "utf8"));
|
||||
assert.equal(json["host.name"], "ConfigEditTest2");
|
||||
});
|
||||
});
|
||||
@@ -615,7 +617,10 @@ describe("Integration of Clusterio", function() {
|
||||
describe("instance assign", function() {
|
||||
it("creates the instance files", async function() {
|
||||
await execCtl("instance assign test 4");
|
||||
assert(await fs.exists(path.join(instancesDir, "test")), "Instance directory was not created");
|
||||
await assert.doesNotReject(
|
||||
fs.access(path.join(instancesDir, "test")),
|
||||
"Instance directory was not created"
|
||||
);
|
||||
await checkInstanceStatus(44, "stopped");
|
||||
});
|
||||
});
|
||||
@@ -652,15 +657,15 @@ describe("Integration of Clusterio", function() {
|
||||
slowTest(this);
|
||||
requiresFactorio(this);
|
||||
let exportPath = path.join("temp", "test", "static");
|
||||
await fs.remove(exportPath);
|
||||
await fs.rm(exportPath, { force: true, recursive: true, maxRetries: 10 });
|
||||
await execCtl("instance export-data test");
|
||||
let modPack = await getControl().send(new lib.ModPackGetDefaultRequest());
|
||||
let assets = modPack.exportManifest.assets;
|
||||
assert(Object.keys(assets).length > 1, "Export assets is empty");
|
||||
for (let key of ["settings", "prototypes", "metadata", "spritesheet", "locale"]) {
|
||||
assert(assets[key], `Missing ${key} from assets`);
|
||||
assert(
|
||||
await fs.exists(path.join(exportPath, assets[key])),
|
||||
await assert.doesNotReject(
|
||||
fs.access(path.join(exportPath, assets[key])),
|
||||
`Manifest entry for ${key} was not written to filesystem`
|
||||
);
|
||||
}
|
||||
@@ -733,7 +738,7 @@ describe("Integration of Clusterio", function() {
|
||||
slowTest(this);
|
||||
await execCtl("instance stop 44");
|
||||
let savesDir = path.join("temp", "test", "instances", "test", "saves");
|
||||
await fs.copy(path.join(savesDir, "world.zip"), path.join(savesDir, "_autosave1.zip"));
|
||||
await fs.copyFile(path.join(savesDir, "world.zip"), path.join(savesDir, "_autosave1.zip"));
|
||||
await prepareToStart();
|
||||
await execCtl(`instance ${cmd} test`);
|
||||
let saves = await getControl().sendTo({ instanceId: 44 }, new lib.InstanceSaveDetailsListRequest());
|
||||
@@ -1156,28 +1161,28 @@ describe("Integration of Clusterio", function() {
|
||||
|
||||
describe("instance save upload", function() {
|
||||
it("should upload a zip file", async function() {
|
||||
await fs.outputFile(path.join("temp", "test", "upload.zip"), "a test");
|
||||
await fs.writeFile(path.join("temp", "test", "upload.zip"), "a test");
|
||||
await execCtl("instance save upload 44 upload.zip");
|
||||
assert(
|
||||
await fs.pathExists(path.join("temp", "test", "instances", "test", "saves", "upload.zip")),
|
||||
"file not uploaded to saves directory"
|
||||
await assert.doesNotReject(
|
||||
fs.access(path.join("temp", "test", "instances", "test", "saves", "upload.zip")),
|
||||
"file not uploaded to saves directory",
|
||||
);
|
||||
});
|
||||
it("should reject non-zip files", async function() {
|
||||
await fs.outputFile(path.join("temp", "test", "invalid"), "a test");
|
||||
await fs.writeFile(path.join("temp", "test", "invalid"), "a test");
|
||||
await assert.rejects(execCtl("instance save upload 44 invalid"));
|
||||
});
|
||||
it("should reject path traversal attacks", async function() {
|
||||
await fs.outputFile(path.join("temp", "test", "upload.zip"), "a test");
|
||||
await fs.writeFile(path.join("temp", "test", "upload.zip"), "a test");
|
||||
await assert.rejects(execCtl("instance save upload 44 upload.zip --name ../traversal.zip"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("instance save download", function() {
|
||||
it("should download a save", async function() {
|
||||
await fs.remove(path.join("temp", "test", "upload.zip"));
|
||||
await fs.rm(path.join("temp", "test", "upload.zip"), { force: true });
|
||||
await execCtl("instance save download 44 upload.zip");
|
||||
assert(await fs.pathExists(path.join("temp", "test", "upload.zip")));
|
||||
await fs.access(path.join("temp", "test", "upload.zip"));
|
||||
});
|
||||
it("should error if save does not exist", async function() {
|
||||
await assert.rejects(execCtl("instance save download 44 invalid"));
|
||||
@@ -1190,7 +1195,7 @@ describe("Integration of Clusterio", function() {
|
||||
describe("instance save copy", function() {
|
||||
it("copy a save file", async function() {
|
||||
await execCtl("instance save copy 44 upload.zip copy.zip");
|
||||
assert(await fs.pathExists(path.join("temp", "test", "instances", "test", "saves", "copy.zip")));
|
||||
await fs.access(path.join("temp", "test", "instances", "test", "saves", "copy.zip"));
|
||||
});
|
||||
it("should error if destination exist", async function() {
|
||||
await assert.rejects(execCtl("instance save copy 44 upload.zip copy.zip"));
|
||||
@@ -1208,8 +1213,8 @@ describe("Integration of Clusterio", function() {
|
||||
describe("instance save rename", function() {
|
||||
it("rename a save file", async function() {
|
||||
await execCtl("instance save rename 44 copy.zip rename.zip");
|
||||
assert(!await fs.pathExists(path.join("temp", "test", "instances", "test", "saves", "copy.zip")));
|
||||
assert(await fs.pathExists(path.join("temp", "test", "instances", "test", "saves", "rename.zip")));
|
||||
await assert.rejects(fs.access(path.join("temp", "test", "instances", "test", "saves", "copy.zip")));
|
||||
await fs.access(path.join("temp", "test", "instances", "test", "saves", "rename.zip"));
|
||||
});
|
||||
it("should error if new name exist", async function() {
|
||||
await assert.rejects(execCtl("instance save rename 44 upload.zip rename.zip"));
|
||||
@@ -1264,31 +1269,31 @@ describe("Integration of Clusterio", function() {
|
||||
it("should transfers a save", async function() {
|
||||
await uploadSave(pri, "transfer.zip", "transfer.zip");
|
||||
await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec}`);
|
||||
assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri");
|
||||
assert(await fs.pathExists(path.join(secSaves, "transfer.zip")), "save not at sec");
|
||||
await assert.rejects(fs.access(path.join(priSaves, "transfer.zip")), "save still at pri");
|
||||
await assert.doesNotReject(fs.access(path.join(secSaves, "transfer.zip")), "save not at sec");
|
||||
await deleteSave(sec, "transfer.zip");
|
||||
});
|
||||
it("should support rename", async function() {
|
||||
await uploadSave(pri, "transfer.zip", "transfer.zip");
|
||||
await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec} rename.zip`);
|
||||
assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri");
|
||||
assert(await fs.pathExists(path.join(secSaves, "rename.zip")), "save not at sec");
|
||||
await assert.rejects(fs.access(path.join(priSaves, "transfer.zip")), "save still at pri");
|
||||
assert(await (path.join(secSaves, "rename.zip")), "save not at sec");
|
||||
await deleteSave(sec, "rename.zip");
|
||||
});
|
||||
it("should auto-rename if target exists", async function() {
|
||||
await uploadSave(pri, "transfer.zip", "transfer.zip");
|
||||
await uploadSave(sec, "transfer.zip", "transfer.zip");
|
||||
await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec}`);
|
||||
assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri");
|
||||
assert(await fs.pathExists(path.join(secSaves, "transfer-2.zip")), "save not at sec");
|
||||
await assert.rejects(fs.access(path.join(priSaves, "transfer.zip")), "save still at pri");
|
||||
await assert.doesNotReject(fs.access(path.join(secSaves, "transfer-2.zip")), "save not at sec");
|
||||
await deleteSave(sec, "transfer.zip");
|
||||
await deleteSave(sec, "transfer-2.zip");
|
||||
});
|
||||
it("should copy when using --copy", async function() {
|
||||
await uploadSave(pri, "transfer.zip", "transfer.zip");
|
||||
await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec} --copy`);
|
||||
assert(await fs.pathExists(path.join(priSaves, "transfer.zip")), "save not at pri");
|
||||
assert(await fs.pathExists(path.join(secSaves, "transfer.zip")), "save not at sec");
|
||||
await assert.doesNotReject(fs.access(path.join(priSaves, "transfer.zip")), "save not at pri");
|
||||
await assert.doesNotReject(fs.access(path.join(secSaves, "transfer.zip")), "save not at sec");
|
||||
await deleteSave(pri, "transfer.zip");
|
||||
await deleteSave(sec, "transfer.zip");
|
||||
});
|
||||
@@ -1344,16 +1349,16 @@ describe("Integration of Clusterio", function() {
|
||||
describe("instance save delete", function() {
|
||||
it("should delete a save", async function() {
|
||||
await execCtl("instance save delete 44 upload.zip");
|
||||
assert(
|
||||
!await fs.pathExists(path.join("temp", "test", "instances", "test", "saves", "upload.zip")),
|
||||
"file not deleted"
|
||||
await assert.rejects(
|
||||
fs.access(path.join("temp", "test", "instances", "test", "saves", "upload.zip")),
|
||||
"file not deleted",
|
||||
);
|
||||
});
|
||||
it("should error if save does not exist", async function() {
|
||||
await assert.rejects(execCtl("instance save delete 44 upload.zip"));
|
||||
});
|
||||
it("should error on path traversal attacks", async function() {
|
||||
await fs.outputFile(path.join("temp", "test", "upload.zip"), "a test");
|
||||
await fs.writeFile(path.join("temp", "test", "upload.zip"), "a test");
|
||||
await assert.rejects(execCtl("instance save delete 44 ../../upload.zip"));
|
||||
});
|
||||
});
|
||||
@@ -1363,7 +1368,7 @@ describe("Integration of Clusterio", function() {
|
||||
slowTest(this);
|
||||
getControl().saveUpdates = [];
|
||||
await execCtl("instance delete test");
|
||||
assert(!await fs.exists(path.join(instancesDir, "test")), "Instance files was not deleted");
|
||||
await assert.rejects(fs.access(path.join(instancesDir, "test")), "Instance files was not deleted");
|
||||
let instances = await getInstances();
|
||||
assert(!instances.has(44), "instance was not deleted from controller");
|
||||
|
||||
@@ -1497,9 +1502,9 @@ describe("Integration of Clusterio", function() {
|
||||
outputDir: path.join("temp", "test"),
|
||||
});
|
||||
await execCtl("mod upload empty_mod_1.0.0.zip");
|
||||
assert(
|
||||
await fs.pathExists(path.join("temp", "test", "mods", "empty_mod_1.0.0.zip")),
|
||||
"mod not present in mods directory"
|
||||
await assert.doesNotReject(
|
||||
fs.access(path.join("temp", "test", "mods", "empty_mod_1.0.0.zip")),
|
||||
"mod not present in mods directory",
|
||||
);
|
||||
});
|
||||
it("rejects path traversing mods", async function() {
|
||||
@@ -1510,11 +1515,11 @@ describe("Integration of Clusterio", function() {
|
||||
outputDir: path.join("temp", "test"),
|
||||
modName: "path_traversing_mod_1.0.0",
|
||||
});
|
||||
await fs.remove(path.join("temp", "test", "bad_path_mod_1.0.0.zip"));
|
||||
await fs.rm(path.join("temp", "test", "bad_path_mod_1.0.0.zip"), { force: true });
|
||||
await assert.rejects(execCtl("mod upload path_traversing_mod_1.0.0.zip"));
|
||||
assert(
|
||||
!await fs.pathExists(path.join("temp", "test", "bad_path_mod_1.0.0.zip")),
|
||||
"mod executed a path traversal attack",
|
||||
await assert.rejects(
|
||||
fs.access(path.join("temp", "test", "bad_path_mod_1.0.0.zip")),
|
||||
"mod executed a path traversal attack"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1562,9 +1567,9 @@ describe("Integration of Clusterio", function() {
|
||||
it("downloads a mod", async function() {
|
||||
await fs.unlink(path.join("temp", "test", "empty_mod_1.0.0.zip"));
|
||||
await execCtl("mod download empty_mod 1.0.0");
|
||||
assert(
|
||||
await fs.pathExists(path.join("temp", "test", "empty_mod_1.0.0.zip")),
|
||||
"mod not downloaded to cwd"
|
||||
await assert.doesNotReject(
|
||||
fs.access(path.join("temp", "test", "empty_mod_1.0.0.zip")),
|
||||
"mod not downloaded to cwd",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1572,9 +1577,9 @@ describe("Integration of Clusterio", function() {
|
||||
describe("mod delete", function() {
|
||||
it("deletes a mod", async function() {
|
||||
await execCtl("mod delete empty_mod 1.0.0");
|
||||
assert(
|
||||
!await fs.pathExists(path.join("temp", "test", "mods", "empty_mod_1.0.0.zip")),
|
||||
"mod still present in mods dir"
|
||||
await assert.rejects(
|
||||
fs.access(path.join("temp", "test", "mods", "empty_mod_1.0.0.zip")),
|
||||
"mod still present in mods dir",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1761,7 +1766,7 @@ describe("Integration of Clusterio", function() {
|
||||
await execCtl("user set-whitelisted import_control --remove");
|
||||
});
|
||||
it("should import users", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "import-users.json"), {
|
||||
await fs.writeFile(path.join("temp", "test", "import-users.json"), JSON.stringify({
|
||||
export_version: "2.0.0.alpha20",
|
||||
users: [
|
||||
{ username: "import_user_admin", is_admin: true },
|
||||
@@ -1771,7 +1776,7 @@ describe("Integration of Clusterio", function() {
|
||||
{ username: "import_user_none" },
|
||||
{ username: "import_control", is_admin: true },
|
||||
],
|
||||
});
|
||||
}));
|
||||
|
||||
await execCtl("user import --users import-users.json");
|
||||
|
||||
@@ -1794,10 +1799,10 @@ describe("Integration of Clusterio", function() {
|
||||
assert.equal(userBanReason.banReason, "banned", "import_user_ban_reason had incorrect ban reason");
|
||||
});
|
||||
it("should import users by json format", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "import-users.json"), {
|
||||
await fs.writeFile(path.join("temp", "test", "import-users.json"), JSON.stringify({
|
||||
export_version: "2.0.0.alpha20",
|
||||
users: [{ username: "import_user", is_admin: true, is_whitelisted: true }],
|
||||
});
|
||||
}));
|
||||
|
||||
await execCtl("user import import-users.json");
|
||||
const userFoo = (await getUsers()).get("import_user");
|
||||
@@ -1805,11 +1810,11 @@ describe("Integration of Clusterio", function() {
|
||||
assert(userFoo.isWhitelisted, "import_user was not whitelisted");
|
||||
});
|
||||
it("should import bans", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "import-bans.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "import-bans.json"), JSON.stringify([
|
||||
{ username: "import_ban_reason", reason: "banned" },
|
||||
"import_ban",
|
||||
"import_control",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user import --bans import-bans.json");
|
||||
|
||||
@@ -1826,17 +1831,17 @@ describe("Integration of Clusterio", function() {
|
||||
assert(users.get("import_control").isBanned, "import_control was not banned");
|
||||
});
|
||||
it("should import bans by filename", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "import-bans.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "import-bans.json"), JSON.stringify([
|
||||
"import_ban_filename",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user import import-bans.json");
|
||||
assert((await getUsers()).get("import_ban_filename").isBanned, "import_ban_filename was not banned");
|
||||
});
|
||||
it("should import admins", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "import-admins.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "import-admins.json"), JSON.stringify([
|
||||
"import_admin", "import_control",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user import --admins import-admins.json");
|
||||
const users = await getUsers();
|
||||
@@ -1844,17 +1849,17 @@ describe("Integration of Clusterio", function() {
|
||||
assert(users.get("import_control").isAdmin, "import_control was not admin");
|
||||
});
|
||||
it("should import admins by filename", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "import-admins.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "import-admins.json"), JSON.stringify([
|
||||
"import_admin_filename",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user import import-admins.json");
|
||||
assert((await getUsers()).get("import_admin_filename").isAdmin, "import_admin_filename was not admin");
|
||||
});
|
||||
it("should import whitelist", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "import-whitelist.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "import-whitelist.json"), JSON.stringify([
|
||||
"import_whitelist", "import_control",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user import --whitelist import-whitelist.json");
|
||||
const users = await getUsers();
|
||||
@@ -1862,9 +1867,9 @@ describe("Integration of Clusterio", function() {
|
||||
assert(users.get("import_control").isWhitelisted, "import_control was not whitelisted");
|
||||
});
|
||||
it("should import whitelist by filename", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "import-whitelist.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "import-whitelist.json"), JSON.stringify([
|
||||
"import_whitelist_filename",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user import import-whitelist.json");
|
||||
assert(
|
||||
@@ -1885,7 +1890,7 @@ describe("Integration of Clusterio", function() {
|
||||
await execCtl("user set-admin export_user --create");
|
||||
await execCtl("user set-whitelisted export_user");
|
||||
await execCtl("user export --users user-export.json");
|
||||
const data = await fs.readJson(path.join("temp", "test", "user-export.json"));
|
||||
const data = JSON.parse(await fs.readFile(path.join("temp", "test", "user-export.json")));
|
||||
assert(data.users, "Users array does not exist");
|
||||
assert(!data.users.find(u => u.username === "export_control"), "Control user is present");
|
||||
assert.deepEqual(data.users.find(u => u.username === "export_user"), {
|
||||
@@ -1896,7 +1901,7 @@ describe("Integration of Clusterio", function() {
|
||||
await execCtl("user set-banned export_ban --create");
|
||||
await execCtl("user set-banned export_ban_reason --create --reason banned");
|
||||
await execCtl("user export --bans ban-export.json");
|
||||
const data = await fs.readJson(path.join("temp", "test", "ban-export.json"));
|
||||
const data = JSON.parse(await fs.readFile(path.join("temp", "test", "ban-export.json")));
|
||||
assert.equal(data.indexOf("export_control"), -1, "Control user is present");
|
||||
assert(data.indexOf("export_ban") >= 0, "export_ban is missing");
|
||||
assert.deepEqual(data.find(u => typeof u === "object" && u.username === "export_ban_reason"), {
|
||||
@@ -1906,14 +1911,14 @@ describe("Integration of Clusterio", function() {
|
||||
it("should export admins", async function() {
|
||||
await execCtl("user set-admin export_admin --create");
|
||||
await execCtl("user export --admins admin-export.json");
|
||||
const data = await fs.readJson(path.join("temp", "test", "admin-export.json"));
|
||||
const data = JSON.parse(await fs.readFile(path.join("temp", "test", "admin-export.json")));
|
||||
assert.equal(data.indexOf("export_control"), -1, "Control user is present");
|
||||
assert(data.indexOf("export_admin") >= 0, "export_admin is missing");
|
||||
});
|
||||
it("should export whitelist", async function() {
|
||||
await execCtl("user set-whitelisted export_whitelist --create");
|
||||
await execCtl("user export --whitelist whitelist-export.json");
|
||||
const data = await fs.readJson(path.join("temp", "test", "whitelist-export.json"));
|
||||
const data = JSON.parse(await fs.readFile(path.join("temp", "test", "whitelist-export.json")));
|
||||
assert.equal(data.indexOf("export_control"), -1, "Control user is present");
|
||||
assert(data.indexOf("export_whitelist") >= 0, "export_whitelist is missing");
|
||||
});
|
||||
@@ -1927,13 +1932,13 @@ describe("Integration of Clusterio", function() {
|
||||
await execCtl("user set-whitelisted restore_control");
|
||||
});
|
||||
it("should restore users", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "restore-users.json"), {
|
||||
await fs.writeFile(path.join("temp", "test", "restore-users.json"), JSON.stringify({
|
||||
export_version: "2.0.0.alpha20",
|
||||
users: [
|
||||
{ username: "restore_user", is_admin: true, is_whitelisted: true },
|
||||
{ username: "restore_user_two", is_banned: true, ban_reason: "Test" },
|
||||
],
|
||||
});
|
||||
}));
|
||||
|
||||
await execCtl("user restore restore-users.json");
|
||||
const users = await getUsers();
|
||||
@@ -1951,17 +1956,17 @@ describe("Integration of Clusterio", function() {
|
||||
assert(!control.isBanned, "restore_control was banned");
|
||||
assert(!control.isWhitelisted, "restore_control was whitelisted");
|
||||
|
||||
const data = await fs.readJson(path.join("temp", "test", "users-backup.json"));
|
||||
const data = JSON.parse(await fs.readFile(path.join("temp", "test", "users-backup.json")));
|
||||
assert(data.users, "Users array does not exist");
|
||||
assert.deepEqual(data.users.find(u => u.username === "restore_control"), {
|
||||
username: "restore_control", is_admin: true, is_banned: true, is_whitelisted: true,
|
||||
});
|
||||
});
|
||||
it("should restore bans", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "restore-bans.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "restore-bans.json"), JSON.stringify([
|
||||
{ username: "restore_ban_reason", reason: "banned" },
|
||||
"restore_ban",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user restore --bans restore-bans.json");
|
||||
|
||||
@@ -1977,13 +1982,13 @@ describe("Integration of Clusterio", function() {
|
||||
|
||||
assert(!users.get("restore_control").isBanned, "restore_control was banned");
|
||||
|
||||
const data = await fs.readJson(path.join("temp", "test", "bans-backup.json"));
|
||||
const data = JSON.parse(await fs.readFile(path.join("temp", "test", "bans-backup.json")));
|
||||
assert(data.indexOf("restore_control") >= 0, "restore_control is missing");
|
||||
});
|
||||
it("should restore admins", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "restore-admins.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "restore-admins.json"), JSON.stringify([
|
||||
"restore_admin",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user restore --admins restore-admins.json");
|
||||
const users = await getUsers();
|
||||
@@ -1991,13 +1996,13 @@ describe("Integration of Clusterio", function() {
|
||||
assert(users.get("restore_admin").isAdmin, "restore_admin was not admin");
|
||||
assert(!users.get("restore_control").isAdmin, "restore_control was admin");
|
||||
|
||||
const data = await fs.readJson(path.join("temp", "test", "admins-backup.json"));
|
||||
const data = JSON.parse(await fs.readFile(path.join("temp", "test", "admins-backup.json")));
|
||||
assert(data.indexOf("restore_control") >= 0, "restore_control is missing");
|
||||
});
|
||||
it("should restore whitelist", async function() {
|
||||
await fs.writeJSON(path.join("temp", "test", "restore-whitelist.json"), [
|
||||
await fs.writeFile(path.join("temp", "test", "restore-whitelist.json"), JSON.stringify([
|
||||
"restore_whitelist",
|
||||
]);
|
||||
]));
|
||||
|
||||
await execCtl("user restore --whitelist restore-whitelist.json");
|
||||
const users = await getUsers();
|
||||
@@ -2005,7 +2010,7 @@ describe("Integration of Clusterio", function() {
|
||||
assert(users.get("restore_whitelist").isWhitelisted, "restore_whitelist was not whitelisted");
|
||||
assert(!users.get("restore_control").isWhitelisted, "restore_control was whitelisted");
|
||||
|
||||
const data = await fs.readJson(path.join("temp", "test", "whitelist-backup.json"));
|
||||
const data = JSON.parse(await fs.readFile(path.join("temp", "test", "whitelist-backup.json")));
|
||||
assert(data.indexOf("restore_control") >= 0, "restore_control is missing");
|
||||
});
|
||||
it("should reject multiple provided options", async function() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const path = require("path");
|
||||
const events = require("events");
|
||||
@@ -18,10 +18,10 @@ async function exec(command, options = {}) {
|
||||
|
||||
describe("Integration of create tool", function () {
|
||||
beforeEach(async function () {
|
||||
await fs.ensureDir(cwd);
|
||||
await fs.mkdir(cwd, { recursive: true });
|
||||
});
|
||||
afterEach(async function () {
|
||||
await fs.remove(cwd);
|
||||
await fs.rm(cwd, { recursive: true, maxRetries: 10 });
|
||||
});
|
||||
it("should create a new standalone installation", async function () {
|
||||
this.skip();
|
||||
@@ -37,10 +37,10 @@ describe("Integration of create tool", function () {
|
||||
].join(" "));
|
||||
|
||||
// Check that the generated config is correct
|
||||
const controllerConfig = await fs.readJson(path.join(cwd, "config-controller.json"));
|
||||
const controllerConfig = JSON.parse(await fs.readFile(path.join(cwd, "config-controller.json")));
|
||||
assert.equal(controllerConfig["controller.http_port"], 8099);
|
||||
|
||||
const hostConfig = await fs.readJson(path.join(cwd, "config-host.json"));
|
||||
const hostConfig = JSON.parse(await fs.readFile(path.join(cwd, "config-host.json")));
|
||||
assert.equal(hostConfig["host.name"], "local");
|
||||
assert.equal(hostConfig["host.public_address"], "localhost");
|
||||
assert.equal(hostConfig["host.factorio_directory"], "test/factorio");
|
||||
@@ -58,9 +58,9 @@ describe("Integration of create tool", function () {
|
||||
].join(" "));
|
||||
|
||||
// Check that the generated config is correct
|
||||
const controllerConfig = await fs.readJson(path.join(cwd, "config-controller.json"));
|
||||
const controllerConfig = JSON.parse(await fs.readFile(path.join(cwd, "config-controller.json")));
|
||||
assert.equal(controllerConfig["controller.http_port"], 8099);
|
||||
|
||||
await assert.rejects(fs.readJson(path.join(cwd, "config-host.json")));
|
||||
await assert.rejects(fs.access(path.join(cwd, "config-host.json")));
|
||||
}).timeout(60000);
|
||||
});
|
||||
+12
-11
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable no-console */
|
||||
"use strict";
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const { readFileSync } = require("node:fs");
|
||||
const child_process = require("child_process");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const util = require("util");
|
||||
@@ -160,7 +161,7 @@ async function get(urlPath) {
|
||||
function loadJSON(filePath) {
|
||||
try {
|
||||
// eslint-disable-next-line node/no-sync
|
||||
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||
return JSON.parse(readFileSync(filePath, "utf8"));
|
||||
} catch (err) {
|
||||
if (err.code === "ENOENT") {
|
||||
return undefined;
|
||||
@@ -319,19 +320,19 @@ before(async function() {
|
||||
console.log("FAST_TEST is present in env, slow tests will be skipped.");
|
||||
}
|
||||
|
||||
await fs.remove(instancesDir);
|
||||
await fs.remove(modsDir);
|
||||
await fs.remove(databaseDir);
|
||||
await fs.rm(instancesDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.rm(modsDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.rm(databaseDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
|
||||
await fs.remove(pluginListPath);
|
||||
await fs.remove(controllerConfigPath);
|
||||
await fs.remove(hostConfigPath);
|
||||
await fs.remove(controlConfigPath);
|
||||
await fs.rm(pluginListPath, { force: true });
|
||||
await fs.rm(controllerConfigPath, { force: true });
|
||||
await fs.rm(hostConfigPath, { force: true });
|
||||
await fs.rm(controlConfigPath, { force: true });
|
||||
|
||||
await fs.ensureDir(path.join("temp", "test"));
|
||||
await fs.mkdir(path.join("temp", "test"), { recursive: true });
|
||||
|
||||
console.log("Building Mods");
|
||||
await fs.ensureDir(modsDir);
|
||||
await fs.mkdir(modsDir, { recursive: true });
|
||||
await lib.build({
|
||||
build: true,
|
||||
pack: true,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
const path = require("path");
|
||||
const assert = require("assert").strict;
|
||||
const lib = require("@clusterio/lib");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
|
||||
const { testMatrix } = require("../common");
|
||||
const {
|
||||
@@ -82,16 +82,16 @@ describe("Clusterio Instance", function() {
|
||||
});
|
||||
it("should always create a whitelist json even if unused", async function() {
|
||||
slowTest(this);
|
||||
await fs.remove(whitelistPath);
|
||||
await fs.rm(whitelistPath, { force: true });
|
||||
await execCtl(`instance start ${instName}`);
|
||||
const whitelist = await fs.readJSON(whitelistPath);
|
||||
const whitelist = JSON.parse(await fs.readFile(whitelistPath, "utf8"));
|
||||
assert.deepEqual(whitelist, []);
|
||||
});
|
||||
it("should not overwrite the existing whitelist if sync is disabled", async function() {
|
||||
slowTest(this);
|
||||
await fs.writeJSON(whitelistPath, ["test_user"]);
|
||||
await fs.writeFile(whitelistPath, JSON.stringify(["test_user"]));
|
||||
await execCtl(`instance start ${instName}`);
|
||||
const whitelist = await fs.readJSON(whitelistPath);
|
||||
const whitelist = JSON.parse(await fs.readFile(whitelistPath, "utf8"));
|
||||
assert.deepEqual(whitelist, ["test_user"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const events = require("events");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const jwt = require("jsonwebtoken");
|
||||
|
||||
const { TestControlConnector, TestHostConnector, TestControl, get, exec, url, slowTest } = require("./index");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const JSZip = require("jszip");
|
||||
const path = require("path");
|
||||
|
||||
|
||||
+10
-11
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const events = require("events");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const lib = require("@clusterio/lib");
|
||||
@@ -44,12 +44,10 @@ describe("Integration of host/src/server", function() {
|
||||
|
||||
before(async function() {
|
||||
// Delete result from previous run of these tests
|
||||
if (await fs.pathExists(writePath)) {
|
||||
await fs.remove(writePath);
|
||||
}
|
||||
await fs.rm(writePath, { force: true, recursive: true, maxRetries: 10 });
|
||||
|
||||
await fs.ensureDir(writePath);
|
||||
logFile = fs.createWriteStream(path.join(writePath, "log.txt"), "utf8");
|
||||
await fs.mkdir(writePath, { recursive: true });
|
||||
logFile = (await fs.open(path.join(writePath, "log.txt"), "w")).createWriteStream({ encoding: "utf8" });
|
||||
server.on("output", function(output) {
|
||||
if (output.source === "stderr") {
|
||||
// Factorio doesn't print on stderr but sometimes on
|
||||
@@ -103,10 +101,10 @@ describe("Integration of host/src/server", function() {
|
||||
|
||||
// Make sure the test is not fooled by previous data
|
||||
let mapPath = server.writePath("saves", "test.zip");
|
||||
assert(!await fs.exists(mapPath), "save exist before test");
|
||||
await assert.rejects(fs.access(mapPath), "save exist before test");
|
||||
|
||||
await server.create("test.zip", undefined, { width: 50, height: 50 });
|
||||
assert(await fs.exists(mapPath), "test did not create save");
|
||||
assert.doesNotReject(await fs.access(mapPath), "test did not create save");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,11 +117,11 @@ describe("Integration of host/src/server", function() {
|
||||
let settings = await server.exampleSettings();
|
||||
settings.visibility = { public: false, lan: true };
|
||||
settings.require_user_verification = false;
|
||||
await fs.outputFile(server.writePath("server-settings.json"), JSON.stringify(settings, null, "\t"));
|
||||
await fs.writeFile(server.writePath("server-settings.json"), JSON.stringify(settings, null, "\t"));
|
||||
|
||||
// Make sure the test does not fail due to create() failing.
|
||||
let mapPath = server.writePath("saves", "test.zip");
|
||||
assert(await fs.exists(mapPath), "save is missing");
|
||||
await assert.doesNotReject(fs.access(mapPath), "save is missing");
|
||||
|
||||
await server.start("test.zip");
|
||||
});
|
||||
@@ -171,7 +169,8 @@ describe("Integration of host/src/server", function() {
|
||||
describe(".startScenario()", function() {
|
||||
before("Write test_scenario", async function() {
|
||||
let content = "script.on_init(function() print('test_scenario init') end)\n";
|
||||
await fs.outputFile(server.writePath("scenarios", "test_scenario", "control.lua"), content);
|
||||
await fs.mkdir(server.writePath("scenarios", "test_scenario"), { recursive: true });
|
||||
await fs.writeFile(server.writePath("scenarios", "test_scenario", "control.lua"), content);
|
||||
});
|
||||
|
||||
it("runs the given scenario", async function() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable node/no-sync */
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const { LockFile } = require("@clusterio/lib");
|
||||
@@ -15,7 +15,7 @@ describe("class LockFile", function () {
|
||||
let lock;
|
||||
|
||||
before(async function () {
|
||||
await fs.ensureDir(baseDir);
|
||||
await fs.mkdir(baseDir, { recursive: true });
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -30,7 +30,7 @@ describe("class LockFile", function () {
|
||||
describe("acquire()", function () {
|
||||
it("should create the lock file when acquiring", async function () {
|
||||
await lock.acquire();
|
||||
assert.ok(fs.existsSync(filePath));
|
||||
await fs.access(filePath);
|
||||
});
|
||||
|
||||
it("should throw LockFileBusyError if called when not idle", async function () {
|
||||
@@ -41,7 +41,7 @@ describe("class LockFile", function () {
|
||||
it("should remove stale lock file and acquire it", async function () {
|
||||
await fs.writeFile(filePath, "999999\n"); // stale lock
|
||||
await lock.acquire();
|
||||
assert.ok(fs.existsSync(filePath));
|
||||
await fs.access(filePath);
|
||||
});
|
||||
|
||||
it("should throw if existing lock file is not stale", async function () {
|
||||
@@ -54,7 +54,7 @@ describe("class LockFile", function () {
|
||||
it("should remove the lock file when releasing", async function () {
|
||||
await lock.acquire();
|
||||
await lock.release();
|
||||
assert.ok(!fs.existsSync(filePath));
|
||||
await assert.rejects(fs.access(filePath));
|
||||
});
|
||||
|
||||
it("should throw LockFileBusyError if called when not acquired", async function () {
|
||||
|
||||
+14
-11
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const stream = require("node:stream");
|
||||
const JSZip = require("jszip"); // Added for creating mock zips
|
||||
|
||||
@@ -44,7 +44,7 @@ async function waitBriefly() {
|
||||
|
||||
describe("lib/ModStore", function () {
|
||||
before(async function () {
|
||||
await fs.ensureDir(MODS_DIR);
|
||||
await fs.mkdir(MODS_DIR, { recursive: true });
|
||||
});
|
||||
|
||||
after(function() {
|
||||
@@ -59,7 +59,8 @@ describe("lib/ModStore", function () {
|
||||
});
|
||||
|
||||
afterEach(async function () {
|
||||
await fs.emptyDir(MODS_DIR);
|
||||
await fs.rm(MODS_DIR, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(MODS_DIR, { recursive: true });
|
||||
});
|
||||
|
||||
describe("constructor", function () {
|
||||
@@ -120,7 +121,7 @@ describe("lib/ModStore", function () {
|
||||
|
||||
// Add other file types and directories
|
||||
await fs.writeFile(path.join(MODS_DIR, "some-text.txt"), "hello");
|
||||
await fs.ensureDir(path.join(MODS_DIR, "a-directory"));
|
||||
await fs.mkdir(path.join(MODS_DIR, "a-directory"), { recursive: true });
|
||||
await fs.writeFile(path.join(MODS_DIR, "archive.tar.gz"), "not a zip");
|
||||
await createMockModZip(path.join(MODS_DIR, "temp_file.tmp.zip"), "temp", "1.0.0"); // Should ignore .tmp.zip
|
||||
|
||||
@@ -139,7 +140,7 @@ describe("lib/ModStore", function () {
|
||||
await createMockModZip(modPath, modName, modVersion);
|
||||
let modStore = await ModStore.fromDirectory(MODS_DIR);
|
||||
assert.equal(modStore.files.size, 1);
|
||||
assert(await fs.pathExists(CACHE_FILE), "Cache file should exist");
|
||||
await assert.doesNotReject(fs.access(CACHE_FILE), "Cache file should exist");
|
||||
|
||||
// Wait and recreate the file to update mtime
|
||||
await waitBriefly();
|
||||
@@ -197,7 +198,9 @@ describe("lib/ModStore", function () {
|
||||
|
||||
beforeEach(async function () {
|
||||
// Start with an empty store for download tests
|
||||
await fs.emptyDir(MODS_DIR); // Clear any existing files in the temp dir
|
||||
// Clear any existing files in the temp dir
|
||||
await fs.rm(MODS_DIR, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(MODS_DIR, { recursive: true });
|
||||
modStore = await ModStore.fromDirectory(MODS_DIR); // Initialize using fromDirectory
|
||||
|
||||
// Restore fetch before each test in this suite to avoid pollution
|
||||
@@ -269,7 +272,7 @@ describe("lib/ModStore", function () {
|
||||
assert.equal(modStore.files.size, 1, "Should have 1 mod after download");
|
||||
const filename = "download-mod-1_1.1.0.zip";
|
||||
assert(modStore.files.has(filename), `Mod ${filename} should be in the store`);
|
||||
assert(await fs.pathExists(path.join(MODS_DIR, filename)), `File ${filename} should exist`);
|
||||
await assert.doesNotReject(fs.access(path.join(MODS_DIR, filename)), `File ${filename} should exist`);
|
||||
const modInfo = modStore.files.get(filename);
|
||||
assert(modInfo instanceof ModInfo);
|
||||
assert.equal(modInfo.name, "download-mod-1");
|
||||
@@ -290,8 +293,8 @@ describe("lib/ModStore", function () {
|
||||
assert.equal(modStore.files.size, 2);
|
||||
assert(modStore.files.has("multi-mod-a_1.0.0.zip"));
|
||||
assert(modStore.files.has("multi-mod-b_2.0.0.zip"));
|
||||
assert(await fs.pathExists(path.join(MODS_DIR, "multi-mod-a_1.0.0.zip")));
|
||||
assert(await fs.pathExists(path.join(MODS_DIR, "multi-mod-b_2.0.0.zip")));
|
||||
await fs.access(path.join(MODS_DIR, "multi-mod-a_1.0.0.zip"));
|
||||
await fs.access(path.join(MODS_DIR, "multi-mod-b_2.0.0.zip"));
|
||||
});
|
||||
it("should throw when no releases available", async function () {
|
||||
const modsToDownload = [{ name: "download-mod-1", version: ModVersionEquality.fromString("1.1.0") }];
|
||||
@@ -803,12 +806,12 @@ describe("lib/ModStore", function () {
|
||||
describe("deleteFile", function () {
|
||||
it("should delete an existing file from store and filesystem", async function () {
|
||||
assert(modStore.files.has(testMod1Filename), "Mod should exist before deletion");
|
||||
assert(await fs.pathExists(testMod1Path), "Mod file should exist before deletion");
|
||||
await assert.doesNotReject(fs.access(testMod1Path), "Mod file should exist before deletion");
|
||||
|
||||
await modStore.deleteFile(testMod1Filename);
|
||||
|
||||
assert(!modStore.files.has(testMod1Filename), "Mod should not exist in store after deletion");
|
||||
assert(!(await fs.pathExists(testMod1Path)), "Mod file should not exist after deletion");
|
||||
await assert.rejects(fs.access(testMod1Path), "Mod file should not exist after deletion");
|
||||
});
|
||||
|
||||
it("should emit 'change' event with isDeleted flag when deleting", async function () {
|
||||
|
||||
+15
-16
@@ -1,7 +1,6 @@
|
||||
"use strict";
|
||||
const lib = require("@clusterio/lib");
|
||||
const { optional } = require("@clusterio/lib/dist/node/src/config/validators");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
const assert = require("assert").strict;
|
||||
const CA = lib.ConfigAccess;
|
||||
@@ -289,10 +288,10 @@ describe("lib/config/classes", function() {
|
||||
await assert.rejects(TestConfig.fromFile("local", filepath));
|
||||
});
|
||||
it("should load defaults for missing fields", async function() {
|
||||
await fs.writeJSON(filepath, {
|
||||
await fs.writeFile(filepath, JSON.stringify({
|
||||
"alpha.foo": "a",
|
||||
"test.enum": "a",
|
||||
});
|
||||
}));
|
||||
const testInstance = await TestConfig.fromFile("local", filepath);
|
||||
assert.equal(testInstance.get("alpha.foo"), "a");
|
||||
assert.deepEqual(testInstance.get("beta.bar"), {});
|
||||
@@ -302,14 +301,14 @@ describe("lib/config/classes", function() {
|
||||
assert.equal(testInstance.filepath, filepath);
|
||||
});
|
||||
it("should load fields", async function() {
|
||||
await fs.writeJSON(filepath, {
|
||||
await fs.writeFile(filepath, JSON.stringify({
|
||||
"test.enum": "c",
|
||||
"test.test": "blah",
|
||||
"test.func": 22,
|
||||
"test.bool": null,
|
||||
"test.json": { valid: true },
|
||||
"test.priv": "bar",
|
||||
});
|
||||
}));
|
||||
const testInstance = await TestConfig.fromFile("local", filepath);
|
||||
assert.equal(testInstance.get("test.enum"), "c");
|
||||
assert.equal(testInstance.get("test.test"), "blah");
|
||||
@@ -335,13 +334,13 @@ describe("lib/config/classes", function() {
|
||||
"test.beta": "decay",
|
||||
"test.gamma": 99,
|
||||
};
|
||||
await fs.writeJson(filepath, testFields);
|
||||
await fs.writeFile(filepath, JSON.stringify(testFields));
|
||||
const testInstance = await TestConfig.fromFile("local", filepath);
|
||||
assert.deepEqual(testInstance.toJSON(), testFields);
|
||||
assert.equal(testInstance.filepath, filepath);
|
||||
});
|
||||
it("should ignore inaccessible fields", async function() {
|
||||
await fs.writeJSON(filepath, { "test.priv": "bad" });
|
||||
await fs.writeFile(filepath, JSON.stringify({ "test.priv": "bad" }));
|
||||
const testInstance = await TestConfig.fromFile("remote", filepath);
|
||||
assert.equal(testInstance.fields["test.priv"], undefined);
|
||||
assert.equal(testInstance.fields["test.func"], 42);
|
||||
@@ -352,8 +351,8 @@ describe("lib/config/classes", function() {
|
||||
describe(".save()", function() {
|
||||
const filepath = path.join("temp", "test", "config_test.json");
|
||||
beforeEach(async function() {
|
||||
await fs.remove(filepath);
|
||||
assert(!await fs.exists(filepath), "File was not removed");
|
||||
await fs.rm(filepath, { force: true });
|
||||
await assert.rejects(fs.access(filepath), "File was not removed");
|
||||
});
|
||||
it("should throw if there is no filepath", async function() {
|
||||
const testInstance = new TestConfig("local", { "alpha.foo": "a" });
|
||||
@@ -366,24 +365,24 @@ describe("lib/config/classes", function() {
|
||||
it("should do nothing when not dirty", async function() {
|
||||
const testInstance = new TestConfig("local", { "alpha.foo": "a" }, filepath);
|
||||
await testInstance.save();
|
||||
assert(!await fs.exists(filepath), "File was created");
|
||||
await assert.rejects(fs.access(filepath), "File was created");
|
||||
});
|
||||
it("should save data to file", async function() {
|
||||
const testInstance = new TestConfig("local", { "alpha.foo": "a" }, filepath);
|
||||
testInstance.set("alpha.foo", "b"); // Sets the dirty flag
|
||||
await testInstance.save();
|
||||
const json = await fs.readJSON(filepath);
|
||||
const json = JSON.parse(await fs.readFile(filepath, "utf8"));
|
||||
assert.deepEqual(json, testInstance.toJSON());
|
||||
});
|
||||
it("should clear the dirty flag after saving", async function() {
|
||||
const testInstance = new TestConfig("local", { "alpha.foo": "a" }, filepath);
|
||||
testInstance.set("alpha.foo", "b"); // Sets the dirty flag
|
||||
await testInstance.save();
|
||||
assert(await fs.exists(filepath), "File was not created");
|
||||
await fs.remove(filepath);
|
||||
assert(!await fs.exists(filepath), "File was not removed");
|
||||
await assert.doesNotReject(fs.access(filepath), "File was not created");
|
||||
await fs.rm(filepath);
|
||||
await assert.rejects(fs.access(filepath), "File was was not removed");
|
||||
await testInstance.save();
|
||||
assert(!await fs.exists(filepath), "File was created");
|
||||
await assert.rejects(fs.access(filepath), "File was created");
|
||||
});
|
||||
it("should be round trip savable", async function() {
|
||||
const testInstance = new TestConfig("local", {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const lib = require("@clusterio/lib");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const lib = require("@clusterio/lib");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
const events = require("events");
|
||||
const assert = require("assert").strict;
|
||||
const lib = require("@clusterio/lib");
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
class MockDatastoreProvider extends lib.DatastoreProvider {
|
||||
@@ -37,7 +37,8 @@ describe("lib/datastore", function() {
|
||||
const baseDir = path.join("temp", "test", "datastore");
|
||||
const testFiles = path.join("test", "file");
|
||||
before(async function() {
|
||||
await fs.emptyDir(baseDir);
|
||||
await fs.rm(baseDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(baseDir, { recursive: true });
|
||||
});
|
||||
|
||||
describe("class DatastoreProvider", function() {
|
||||
@@ -107,7 +108,7 @@ describe("lib/datastore", function() {
|
||||
});
|
||||
|
||||
beforeEach(async function() {
|
||||
await fs.copy(path.join(testFiles, "json", "load_map.json"), dataPath);
|
||||
await fs.copyFile(path.join(testFiles, "json", "load_map.json"), dataPath);
|
||||
datastoreProvider = new lib.JsonDatastoreProvider(dataPath);
|
||||
});
|
||||
|
||||
@@ -170,7 +171,7 @@ describe("lib/datastore", function() {
|
||||
assert.equal(loadedData.size, 0);
|
||||
});
|
||||
it("throws for invalid file contents", async function() {
|
||||
await fs.copy(path.join(testFiles, "json", "invalid.json"), dataPath);
|
||||
await fs.copyFile(path.join(testFiles, "json", "invalid.json"), dataPath);
|
||||
await assert.rejects(() => datastoreProvider.load(dataPath));
|
||||
});
|
||||
it("writes a backup if size changes after load", async function() {
|
||||
@@ -201,7 +202,7 @@ describe("lib/datastore", function() {
|
||||
});
|
||||
|
||||
beforeEach(async function() {
|
||||
await fs.copy(path.join(testFiles, "json", "load_array_map.json"), dataPath);
|
||||
await fs.copyFile(path.join(testFiles, "json", "load_array_map.json"), dataPath);
|
||||
datastoreProvider = new lib.JsonIdDatastoreProvider(dataPath);
|
||||
});
|
||||
|
||||
@@ -265,7 +266,7 @@ describe("lib/datastore", function() {
|
||||
assert.equal(loadedData.size, 0);
|
||||
});
|
||||
it("throws for invalid file contents", async function() {
|
||||
await fs.copy(path.join(testFiles, "json", "invalid.json"), dataPath);
|
||||
await fs.copyFile(path.join(testFiles, "json", "invalid.json"), dataPath);
|
||||
await assert.rejects(() => datastoreProvider.load(dataPath));
|
||||
});
|
||||
it("writes a backup if size changes after load", async function() {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
const assert = require("assert").strict;
|
||||
const { readFile } = require("fs-extra");
|
||||
const { readFile } = require("node:fs/promises");
|
||||
|
||||
const { fetchFactorioVersions } = require("@clusterio/lib");
|
||||
const { slowTest } = require("../../integration");
|
||||
|
||||
+27
-25
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const http = require("node:http");
|
||||
const path = require("path");
|
||||
const util = require("node:util");
|
||||
@@ -10,20 +10,21 @@ const lib = require("@clusterio/lib");
|
||||
describe("lib/file_ops", function() {
|
||||
let baseDir = path.join("temp", "test", "file_ops");
|
||||
async function setupTestingEnv() {
|
||||
await fs.ensureDir(path.join(baseDir, "test", "folder"));
|
||||
await fs.ensureDir(path.join(baseDir, "test", "another folder"));
|
||||
await fs.remove(path.join(baseDir, "safe"));
|
||||
await fs.ensureDir(path.join(baseDir, "safe"));
|
||||
await fs.mkdir(path.join(baseDir, "test", "folder"), { recursive: true });
|
||||
await fs.mkdir(path.join(baseDir, "test", "another folder"), { recursive: true });
|
||||
await fs.rm(path.join(baseDir, "safe"), { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(path.join(baseDir, "safe"), { recursive: true });
|
||||
|
||||
await fs.outputFile(path.join(baseDir, "test", "file.txt"), "contents");
|
||||
await fs.outputFile(path.join(baseDir, "test", "another file.txt"), "more contents");
|
||||
await fs.writeFile(path.join(baseDir, "test", "file.txt"), "contents");
|
||||
await fs.writeFile(path.join(baseDir, "test", "another file.txt"), "more contents");
|
||||
|
||||
await fs.outputFile(path.join(baseDir, "find", "file"), "contents");
|
||||
await fs.outputFile(path.join(baseDir, "find", "file.txt"), "contents");
|
||||
await fs.outputFile(path.join(baseDir, "find", "foo-1"), "contents");
|
||||
await fs.outputFile(path.join(baseDir, "find", "foo-2"), "contents");
|
||||
await fs.outputFile(path.join(baseDir, "find", "bar-1.txt"), "contents");
|
||||
await fs.outputFile(path.join(baseDir, "find", "bar-2.txt"), "contents");
|
||||
await fs.mkdir(path.join(baseDir, "find"), { recursive: true });
|
||||
await fs.writeFile(path.join(baseDir, "find", "file"), "contents");
|
||||
await fs.writeFile(path.join(baseDir, "find", "file.txt"), "contents");
|
||||
await fs.writeFile(path.join(baseDir, "find", "foo-1"), "contents");
|
||||
await fs.writeFile(path.join(baseDir, "find", "foo-2"), "contents");
|
||||
await fs.writeFile(path.join(baseDir, "find", "bar-1.txt"), "contents");
|
||||
await fs.writeFile(path.join(baseDir, "find", "bar-2.txt"), "contents");
|
||||
}
|
||||
|
||||
before(setupTestingEnv);
|
||||
@@ -90,14 +91,14 @@ describe("lib/file_ops", function() {
|
||||
it("should write new target file", async function() {
|
||||
let target = path.join(baseDir, "safe", "simple.txt");
|
||||
await lib.safeOutputFile(target, "a text file", "utf8");
|
||||
assert(!await fs.pathExists(target.replace(".txt", ".tmp.txt")), "temporary was left behind");
|
||||
await assert.rejects(fs.access(target.replace(".txt", ".tmp.txt")), "temporary was left behind");
|
||||
assert.equal(await fs.readFile(target, "utf8"), "a text file");
|
||||
});
|
||||
it("should overwrite existing target file", async function() {
|
||||
let target = path.join(baseDir, "safe", "exists.txt");
|
||||
await fs.outputFile(target, "previous", "utf8");
|
||||
await fs.writeFile(target, "previous", "utf8");
|
||||
await lib.safeOutputFile(target, "current", "utf8");
|
||||
assert(!await fs.pathExists(target.replace(".txt", ".tmp.txt")), "temporary was left behind");
|
||||
await assert.rejects(fs.access(target.replace(".txt", ".tmp.txt")), "temporary was left behind");
|
||||
assert.equal(await fs.readFile(target, "utf8"), "current");
|
||||
});
|
||||
it("should handle creating file in current working directory", async function() {
|
||||
@@ -228,7 +229,8 @@ describe("lib/file_ops", function() {
|
||||
let server;
|
||||
|
||||
before(async function () {
|
||||
await fs.emptyDir(downloadDir);
|
||||
await fs.rm(downloadDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
await fs.mkdir(downloadDir, { recursive: true });
|
||||
server = http.createServer();
|
||||
server.unref();
|
||||
server.on("request", (req, res) => {
|
||||
@@ -303,7 +305,7 @@ describe("lib/file_ops", function() {
|
||||
|
||||
it("should throw if file exists and overwriteMode is error", async function () {
|
||||
const downloadPath = path.join(downloadDir, "exists.txt");
|
||||
await fs.outputFile(downloadPath, "");
|
||||
await fs.writeFile(downloadPath, "");
|
||||
await assert.rejects(
|
||||
lib.downloadFile(new URL("/simple-file", baseUrl), downloadPath, "error"),
|
||||
{ code: "EEXIST" },
|
||||
@@ -322,7 +324,7 @@ describe("lib/file_ops", function() {
|
||||
|
||||
it("should write a new file if it exists and overwriteMode is rename", async function () {
|
||||
const downloadPath = path.join(downloadDir, "exists.txt");
|
||||
await fs.outputFile(downloadPath, "");
|
||||
await fs.writeFile(downloadPath, "");
|
||||
const newDownloadPath = await lib.downloadFile(new URL("/simple-file", baseUrl), downloadPath, "rename");
|
||||
assert.equal(newDownloadPath, path.join(downloadDir, "exists-2.txt"));
|
||||
const writtenContent = await fs.readFile(newDownloadPath, "utf8");
|
||||
@@ -336,9 +338,9 @@ describe("lib/file_ops", function() {
|
||||
path.join(downloadDir, "contended-3.txt"),
|
||||
path.join(downloadDir, "contended-4.txt"),
|
||||
];
|
||||
await fs.outputFile(downloadPath, "");
|
||||
await fs.writeFile(downloadPath, "");
|
||||
for (const additionalPath of additionalPaths) {
|
||||
await fs.outputFile(additionalPath, "");
|
||||
await fs.writeFile(additionalPath, "");
|
||||
}
|
||||
const newDownloadPath = await lib.downloadFile(new URL("/simple-file", baseUrl), downloadPath, "rename");
|
||||
assert.equal(newDownloadPath, path.join(downloadDir, "contended-5.txt"));
|
||||
@@ -348,7 +350,7 @@ describe("lib/file_ops", function() {
|
||||
|
||||
it("should overwrite existing file if overwriteMode is overwrite", async function () {
|
||||
const downloadPath = path.join(downloadDir, "overwrite.txt");
|
||||
await fs.outputFile(downloadPath, "original content");
|
||||
await fs.writeFile(downloadPath, "original content");
|
||||
const newDownloadPath = await lib.downloadFile(new URL("/simple-file", baseUrl), downloadPath, "overwrite");
|
||||
assert.equal(newDownloadPath, downloadPath);
|
||||
const writtenContent = await fs.readFile(newDownloadPath, "utf8");
|
||||
@@ -357,7 +359,7 @@ describe("lib/file_ops", function() {
|
||||
|
||||
it("should not overwrite existing temp files", async function () {
|
||||
const tempPath = path.join(downloadDir, "temp-exists.tmp.txt");
|
||||
await fs.outputFile(tempPath, "original content");
|
||||
await fs.writeFile(tempPath, "original content");
|
||||
const downloadPath = path.join(downloadDir, "temp-exists.txt");
|
||||
await lib.downloadFile(new URL("/simple-file", baseUrl), downloadPath, "overwrite");
|
||||
const writtenContent = await fs.readFile(tempPath, "utf8");
|
||||
@@ -377,7 +379,7 @@ describe("lib/file_ops", function() {
|
||||
|
||||
const downloadSubdir = path.join(downloadDir, "subdir");
|
||||
const downloadPath = path.join(downloadSubdir, "test.txt");
|
||||
await fs.ensureDir(downloadSubdir);
|
||||
await fs.mkdir(downloadSubdir, { recursive: true });
|
||||
|
||||
const [stage1, waitForStage1] = stageWaiter();
|
||||
const [stage2, waitForStage2] = stageWaiter();
|
||||
@@ -391,7 +393,7 @@ describe("lib/file_ops", function() {
|
||||
download.catch(() => {});
|
||||
await waitForStage1;
|
||||
|
||||
await fs.remove(downloadSubdir);
|
||||
await fs.rm(downloadSubdir, { recursive: true, maxRetries: 10 });
|
||||
stage2();
|
||||
|
||||
await assert.rejects(
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const lib = require("@clusterio/lib");
|
||||
@@ -9,7 +9,7 @@ const lib = require("@clusterio/lib");
|
||||
describe("lib/hash", function() {
|
||||
describe("hashStream()", function() {
|
||||
it("should hash a stream of bytes", async function() {
|
||||
let stream = fs.createReadStream(path.join("test", "file", "hash.txt"));
|
||||
let stream = (await fs.open(path.join("test", "file", "hash.txt"))).createReadStream();
|
||||
let result = await lib.hashStream(stream);
|
||||
assert.equal(result, "be417768b5c3c5c1d9bcb2e7c119196dd76b5570");
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const lib = require("@clusterio/lib");
|
||||
|
||||
describe("lib/ini", function() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const { testLines } = require("./factorio/lines");
|
||||
@@ -47,7 +47,7 @@ describe("lib/logging_utils.js", function() {
|
||||
});
|
||||
it("should handle loading broken index", async function() {
|
||||
let tempDir = path.join("temp", "test", "log");
|
||||
await fs.outputFile(path.join(tempDir, "index.json"), "Broken JSON");
|
||||
await fs.writeFile(path.join(tempDir, "index.json"), "Broken JSON");
|
||||
let logIndex = await lib.LogIndex.load(tempDir);
|
||||
assert.deepEqual(logIndex.index, new Map());
|
||||
});
|
||||
@@ -60,7 +60,7 @@ describe("lib/logging_utils.js", function() {
|
||||
if (file === "excluded.txt") {
|
||||
continue;
|
||||
}
|
||||
let fileStream = fs.createReadStream(path.join(logDir, file));
|
||||
let fileStream = (await fs.open(path.join(logDir, file))).createReadStream();
|
||||
let lineStream = new lib.LineSplitter({ readableObjectMode: true });
|
||||
fileStream.pipe(lineStream);
|
||||
for await (let line of lineStream) {
|
||||
|
||||
+19
-14
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
|
||||
const lib = require("@clusterio/lib");
|
||||
@@ -16,11 +16,12 @@ describe("lib/plugin_loader", function() {
|
||||
let invalidPlugin = path.join(baseDir, "invalid_plugin");
|
||||
before(async function() {
|
||||
async function writePlugin(pluginPath, infoName) {
|
||||
await fs.outputFile(
|
||||
await fs.mkdir(pluginPath, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(pluginPath, "index.js"),
|
||||
`module.exports.plugin = { name: "${infoName}" };`
|
||||
);
|
||||
await fs.outputFile(
|
||||
await fs.writeFile(
|
||||
path.join(pluginPath, "package.json"),
|
||||
'{ "version": "0.0.1" }'
|
||||
);
|
||||
@@ -28,7 +29,7 @@ describe("lib/plugin_loader", function() {
|
||||
|
||||
await writePlugin(testPlugin, "test");
|
||||
await writePlugin(brokenPlugin, "broken");
|
||||
await fs.outputFile(path.join(brokenPlugin, "index.js"), "Syntax Error");
|
||||
await fs.writeFile(path.join(brokenPlugin, "index.js"), "Syntax Error");
|
||||
await writePlugin(invalidPlugin, "wrong");
|
||||
});
|
||||
|
||||
@@ -70,7 +71,8 @@ describe("lib/plugin_loader", function() {
|
||||
let wrongParentClass = path.join(baseDir, "wrong_parent_class_plugin");
|
||||
before(async function() {
|
||||
async function writeEntrypoint(pluginPath, content) {
|
||||
await fs.outputFile(path.join(pluginPath, "controller.js"), content);
|
||||
await fs.mkdir(pluginPath, { recursive: true });
|
||||
await fs.writeFile(path.join(pluginPath, "controller.js"), content);
|
||||
}
|
||||
|
||||
await writeEntrypoint(missingClass, "");
|
||||
@@ -122,16 +124,18 @@ describe("lib/plugin_loader", function() {
|
||||
const npmPluginPath = path.join(baseDir, "node_modules", "npm-plugin");
|
||||
const monorepoPluginPath = path.join(baseDir, "external_plugins", "monorepo", "monorepo-plugin");
|
||||
const monorepoPluginPathAbs = path.resolve(monorepoPluginPath);
|
||||
const notAPluginPath = path.join(baseDir, "node_modules", "not-a-plugin");
|
||||
let pluginList;
|
||||
|
||||
before(async function() {
|
||||
// Setup test plugins
|
||||
async function writePlugin(pluginPath, name) {
|
||||
await fs.outputFile(
|
||||
await fs.mkdir(pluginPath, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(pluginPath, "index.js"),
|
||||
`module.exports.plugin = { name: "${name}" };`
|
||||
);
|
||||
await fs.outputFile(
|
||||
await fs.writeFile(
|
||||
path.join(pluginPath, "package.json"),
|
||||
JSON.stringify({
|
||||
name: path.basename(pluginPath),
|
||||
@@ -150,7 +154,7 @@ describe("lib/plugin_loader", function() {
|
||||
// Write a monorepo plugin
|
||||
await writePlugin(monorepoPluginPath, "monorepo-plugin");
|
||||
// Create root package.json
|
||||
await fs.outputFile(
|
||||
await fs.writeFile(
|
||||
path.join(baseDir, "package.json"),
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
@@ -160,8 +164,9 @@ describe("lib/plugin_loader", function() {
|
||||
})
|
||||
);
|
||||
// Create an npm module that is not a plugin
|
||||
await fs.outputFile(
|
||||
path.join(baseDir, "node_modules", "not-a-plugin", "package.json"),
|
||||
await fs.mkdir(notAPluginPath, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(notAPluginPath, "package.json"),
|
||||
JSON.stringify({ name: "not-a-plugin", version: "1.0.0" })
|
||||
);
|
||||
|
||||
@@ -169,7 +174,7 @@ describe("lib/plugin_loader", function() {
|
||||
pluginList = await lib.loadPluginList(pluginListPath, true);
|
||||
});
|
||||
beforeEach(async function() {
|
||||
await fs.remove(pluginListPath);
|
||||
await fs.rm(pluginListPath, { force: true });
|
||||
});
|
||||
|
||||
it("should discover local plugins", async function() {
|
||||
@@ -188,7 +193,7 @@ describe("lib/plugin_loader", function() {
|
||||
|
||||
it("should load existing plugin list", async function() {
|
||||
const existingList = new Map([["test", "/test/path"]]);
|
||||
await fs.outputFile(pluginListPath, JSON.stringify([...existingList]));
|
||||
await fs.writeFile(pluginListPath, JSON.stringify([...existingList]));
|
||||
const loadedPlugins = await lib.loadPluginList(pluginListPath, false);
|
||||
assert.ok(loadedPlugins.has("test"));
|
||||
assert.strictEqual(loadedPlugins.get("test"), "/test/path");
|
||||
@@ -201,7 +206,7 @@ describe("lib/plugin_loader", function() {
|
||||
|
||||
it("should not throw when package.json has no dependencies field", async function() {
|
||||
// Create a package.json without dependencies
|
||||
await fs.outputFile(
|
||||
await fs.writeFile(
|
||||
path.join("package.json"),
|
||||
JSON.stringify({
|
||||
name: "test-package",
|
||||
@@ -217,7 +222,7 @@ describe("lib/plugin_loader", function() {
|
||||
|
||||
after(async function() {
|
||||
process.chdir(old_cwd);
|
||||
await fs.remove(baseDir);
|
||||
await fs.rm(baseDir, { force: true, recursive: true, maxRetries: 10 });
|
||||
});
|
||||
});
|
||||
});
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs-extra");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("path");
|
||||
const stream = require("stream");
|
||||
const util = require("util");
|
||||
@@ -109,7 +109,7 @@ describe("lib/stream", function() {
|
||||
it("should read chunks in a file in reverse", async function() {
|
||||
let content = "";
|
||||
for (let i = 0; i < 10; i++) { content += String(i).repeat(10); }
|
||||
await fs.outputFile(path.join("temp", "test", "reverse.txt"), content);
|
||||
await fs.writeFile(path.join("temp", "test", "reverse.txt"), content);
|
||||
let reverseStream = await lib.createReverseReadStream(
|
||||
path.join("temp", "test", "reverse.txt"),
|
||||
{ encoding: "utf8", highWaterMark: 10 }
|
||||
@@ -123,7 +123,7 @@ describe("lib/stream", function() {
|
||||
});
|
||||
|
||||
it("should reverse the lines of a file", async function() {
|
||||
let ws = fs.createWriteStream(path.join("temp", "test", "reverse.txt"));
|
||||
let ws = (await fs.open(path.join("temp", "test", "reverse.txt"), "w")).createWriteStream();
|
||||
for (let i = 1; i < 100000; i++) {
|
||||
ws.write(Buffer.from(`${i}\n`));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user