CODEBASE: Fix lint errors 1 (#1732)

This commit is contained in:
catloversg 2024-11-04 13:35:14 +07:00 committed by GitHub
parent f7ee3a340f
commit f6502dd490
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
55 changed files with 252 additions and 255 deletions

@ -34,6 +34,7 @@ module.exports = {
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off",
"react/no-unescaped-entities": "off",
"@typescript-eslint/restrict-template-expressions": "off",
},
settings: {
react: {

@ -31,4 +31,21 @@ declare global {
};
};
}
/**
* "loader" is not exposed in the public API.
*/
module "monaco-editor" {
namespace languages {
interface ILanguageExtensionPoint {
loader: () => Promise<{
language: {
tokenizer: {
root: any[];
};
};
}>;
}
}
}
}

@ -159,7 +159,7 @@ function sanitizeRewardType(rewardType: CodingContractRewardType): CodingContrac
try {
return Factions[fac].getInfo().offerHackingWork;
} catch (e) {
console.error(`Error when trying to filter Hacking Factions for Coding Contract Generation: ${e}`);
console.error("Error when trying to filter Hacking Factions for Coding Contract Generation", e);
return false;
}
});

@ -207,8 +207,8 @@ export function sellMaterial(material: Material, amount: string, price: string):
try {
if (temp.includes("MP")) throw "Only one reference to MP is allowed in sell price.";
temp = eval?.(temp);
} catch (e) {
throw new Error("Invalid value or expression for sell price field: " + e);
} catch (error) {
throw new Error("Invalid value or expression for sell price field", { cause: error });
}
if (temp == null || isNaN(parseFloat(temp))) {
@ -231,8 +231,8 @@ export function sellMaterial(material: Material, amount: string, price: string):
tempQty = tempQty.replace(/INV/g, material.productionAmount.toString());
try {
tempQty = eval?.(tempQty);
} catch (e) {
throw new Error("Invalid value or expression for sell quantity field: " + e);
} catch (error) {
throw new Error("Invalid value or expression for sell quantity field", { cause: error });
}
if (tempQty == null || isNaN(parseFloat(tempQty))) {
@ -263,8 +263,8 @@ export function sellProduct(product: Product, city: CityName, amt: string, price
try {
if (temp.includes("MP")) throw "Only one reference to MP is allowed in sell price.";
temp = eval?.(temp);
} catch (e) {
throw new Error("Invalid value or expression for sell price field: " + e);
} catch (error) {
throw new Error("Invalid value or expression for sell price field.", { cause: error });
}
if (temp == null || isNaN(parseFloat(temp))) {
throw new Error("Invalid value or expression for sell price field.");
@ -291,8 +291,8 @@ export function sellProduct(product: Product, city: CityName, amt: string, price
temp = temp.replace(/INV/g, product.cityData[city].stored.toString());
try {
temp = eval?.(temp);
} catch (e) {
throw new Error("Invalid value or expression for sell quantity field: " + e);
} catch (error) {
throw new Error("Invalid value or expression for sell quantity field", { cause: error });
}
if (temp == null || isNaN(parseFloat(temp))) {
@ -585,13 +585,16 @@ Attempted export amount: ${amount}`);
}
if (!error && isNaN(evaluated)) error = "evaluated value is NaN";
if (error) {
throw new Error(`Error while trying to set the exported amount of ${material.name}.
throw new Error(
`Error while trying to set the exported amount of ${material.name}.
Error occurred while testing keyword replacement with ${testReplacement}.
Your input: ${amount}
Sanitized input: ${sanitizedAmt}
Input after replacement: ${replaced}
Evaluated value: ${evaluated}
Error encountered: ${error}`);
Evaluated value: ${evaluated}` +
// eslint-disable-next-line @typescript-eslint/no-base-to-string
`Error encountered: ${error}`,
);
}
}

@ -29,8 +29,8 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
function expand(): void {
try {
purchaseOffice(corp, division, city);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
return;
}

@ -39,8 +39,8 @@ export function NewDivisionTab(props: IProps): React.ReactElement {
if (disabledText) return;
try {
createDivision(corp, industry, name);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
return;
}

@ -43,8 +43,8 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
props.onClose();
props.rerender();
setShares(NaN);
} catch (err) {
dialogBoxCreate(`${err}`);
} catch (error) {
dialogBoxCreate(String(error));
}
}

@ -59,8 +59,8 @@ export function ExportModal(props: ExportModalProps): React.ReactElement {
try {
if (!targetDivision || !targetCity) return;
actions.exportMaterial(targetDivision, targetCity, props.mat, exportAmount);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
}
props.onClose();
}

@ -37,8 +37,8 @@ export function FindInvestorsModal(props: IProps): React.ReactElement {
);
props.onClose();
props.rerender();
} catch (err) {
dialogBoxCreate(`${err}`);
} catch (error) {
dialogBoxCreate(String(error));
}
}

@ -45,8 +45,8 @@ export function GoPublicModal(props: IProps): React.ReactElement {
props.onClose();
props.rerender();
setShares(NaN);
} catch (err) {
dialogBoxCreate(`${err}`);
} catch (error) {
dialogBoxCreate(String(error));
}
}

@ -27,8 +27,8 @@ export function IssueDividendsModal(props: IProps): React.ReactElement {
if (percent === null) return;
try {
actions.issueDividends(corp, percent / 100);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
}
props.onClose();

@ -55,8 +55,8 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
);
props.onClose();
props.rerender();
} catch (err) {
dialogBoxCreate(`${err}`);
} catch (error) {
dialogBoxCreate(String(error));
}
}

@ -47,8 +47,8 @@ export function MakeProductModal(props: IProps): React.ReactElement {
if (isNaN(design) || isNaN(marketing)) return;
try {
actions.makeProduct(corp, division, city, name, design, marketing);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
}
props.onClose();
}

@ -67,8 +67,8 @@ function BulkPurchaseSection(props: IBPProps): React.ReactElement {
function bulkPurchase(): void {
try {
actions.bulkPurchase(corp, division, props.warehouse, props.mat, parseFloat(buyAmt));
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
}
props.onClose();
}
@ -119,8 +119,8 @@ export function PurchaseMaterialModal(props: IProps): React.ReactElement {
if (buyAmt === null) return;
try {
actions.buyMaterial(division, props.mat, buyAmt);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
}
props.onClose();

@ -39,8 +39,8 @@ function Upgrade({ n, division }: INodeProps): React.ReactElement {
if (n === null || disabled) return;
try {
actions.research(division, n.researchName);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
return;
}

@ -24,8 +24,8 @@ export function SellMaterialModal(props: IProps): React.ReactElement {
function sellMaterial(): void {
try {
actions.sellMaterial(props.mat, amt, price);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
}
props.onClose();
}

@ -31,8 +31,8 @@ export function SellProductModal(props: IProps): React.ReactElement {
function sellProduct(): void {
try {
actions.sellProduct(props.product, props.city, iQty, px, checked);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
}
props.onClose();

@ -27,8 +27,9 @@ function SSoption(props: ISSoptionProps): React.ReactElement {
const matName = props.matName;
const material = props.warehouse.materials[matName];
setSmartSupplyOption(props.warehouse, material, newValue);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
return;
}
setChecked(newValue);
}
@ -40,8 +41,9 @@ function SSoption(props: ISSoptionProps): React.ReactElement {
const matName = props.matName;
const material = props.warehouse.materials[matName];
setSmartSupplyOption(props.warehouse, material, newValue);
} catch (err) {
dialogBoxCreate(err + "");
} catch (error) {
dialogBoxCreate(String(error));
return;
}
setChecked(newValue);
}

@ -12,7 +12,7 @@ import { CONSTANTS } from "./Constants";
import { commitHash } from "./utils/helpers/commitHash";
import { resolveFilePath } from "./Paths/FilePath";
import { hasScriptExtension } from "./Paths/ScriptFilePath";
import { handleGetSaveDataError } from "./Netscript/ErrorMessages";
import { handleGetSaveDataInfoError } from "./Netscript/ErrorMessages";
interface IReturnWebStatus extends IReturnStatus {
data?: Record<string, unknown>;
@ -103,14 +103,14 @@ function initAppNotifier(): void {
const funcs = {
terminal: (message: string, type?: string) => {
const typesFn: Record<string, (s: string) => void> = {
info: Terminal.info,
warn: Terminal.warn,
error: Terminal.error,
success: Terminal.success,
info: (s) => Terminal.info(s),
warn: (s) => Terminal.warn(s),
error: (s) => Terminal.error(s),
success: (s) => Terminal.success(s),
};
let fn;
if (type) fn = typesFn[type];
if (!fn) fn = Terminal.print;
if (!fn) fn = (s: string) => Terminal.print(s);
fn.bind(Terminal)(message);
},
toast: (message: string, type: ToastVariant, duration = 2000) => SnackbarEvents.emit(message, type, duration),
@ -124,12 +124,10 @@ function initSaveFunctions(): void {
const funcs = {
triggerSave: (): Promise<void> => saveObject.saveGame(true),
triggerGameExport: (): void => {
try {
saveObject.exportGame();
} catch (error) {
saveObject.exportGame().catch((error) => {
console.error(error);
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
}
});
},
triggerScriptsExport: (): void => exportScripts("*", Player.getHomeComputer()),
getSaveData: async (): Promise<{ save: SaveData; fileName: string }> => {
@ -159,22 +157,28 @@ function initElectronBridge(): void {
const bridge = window.electronBridge;
if (!bridge) return;
bridge.receive("get-save-data-request", async () => {
let saveData;
try {
saveData = await window.appSaveFns.getSaveData();
} catch (error) {
handleGetSaveDataError(error);
return;
}
bridge.receive("get-save-data-request", () => {
window.appSaveFns
.getSaveData()
.then((saveData) => {
bridge.send("get-save-data-response", saveData);
})
.catch((error) => {
handleGetSaveDataInfoError(error);
});
bridge.receive("get-save-info-request", async (saveData: unknown) => {
});
bridge.receive("get-save-info-request", (saveData: unknown) => {
if (typeof saveData !== "string" && !(saveData instanceof Uint8Array)) {
throw new Error("Error while trying to get save info");
}
const saveInfo = await window.appSaveFns.getSaveInfo(saveData);
window.appSaveFns
.getSaveInfo(saveData)
.then((saveInfo) => {
bridge.send("get-save-info-response", saveInfo);
})
.catch((error) => {
handleGetSaveDataInfoError(error, true);
});
});
bridge.receive("push-save-request", (params: unknown) => {
if (typeof params !== "object") throw new Error("Error trying to push save request");
@ -182,7 +186,7 @@ function initElectronBridge(): void {
window.appSaveFns.pushSaveData(save, automatic);
});
bridge.receive("trigger-save", () => {
return window.appSaveFns
window.appSaveFns
.triggerSave()
.then(() => {
bridge.send("save-completed");

@ -97,7 +97,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
startWork();
}
// We have a special flag for whether the player this faction is the player's
// We have a special flag for whether this faction is the player's
// gang faction because if the player has a gang, they cannot do any other action
const isPlayersGang = Player.gang && Player.getGangName() === faction.name;

@ -90,6 +90,7 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
try {
await saveObject.importGame(importData.saveData);
} catch (e: unknown) {
console.error(e);
SnackbarEvents.emit(String(e), ToastVariant.ERROR, 5000);
}

@ -112,7 +112,7 @@ export class Gang {
this.processTerritoryAndPowerGains(cycles);
this.storedCycles -= cycles;
} catch (e: unknown) {
console.error(`Exception caught when processing Gang: ${e}`);
console.error("Exception caught when processing Gang", e);
}
// Handle "nextUpdate" resolver after this update

@ -1,96 +0,0 @@
/** @param {NS} ns */
export async function main(ns) {
let result;
do {
const board = ns.go.getBoardState();
const validMoves = ns.go.analysis.getValidMoves();
const [growX, growY] = getGrowMove(board, validMoves);
const [randX, randY] = getRandomMove(board, validMoves);
// Try to pick a grow move, otherwise choose a random move
const x = growX ?? randX;
const y = growY ?? randY;
if (x === undefined) {
// Pass turn if no moves are found
result = await ns.go.passTurn();
} else {
// Play the selected move
result = await ns.go.makeMove(x, y);
}
await ns.sleep(100);
} while (result?.type !== "gameOver" && result?.type !== "pass");
// After the opponent passes, end the game by passing as well
await ns.go.passTurn();
}
/**
* Choose one of the empty points on the board at random to play
*/
const getRandomMove = (board, validMoves) => {
const moveOptions = [];
const size = board[0].length;
// Look through all the points on the board
for (let x = 0; x < size; x++) {
for (let y = 0; y < size; y++) {
// Make sure the point is a valid move
const isValidMove = validMoves[x][y];
// Leave some spaces to make it harder to capture our pieces
const isNotReservedSpace = x % 2 || y % 2;
if (isValidMove && isNotReservedSpace) {
moveOptions.push([x, y]);
}
}
}
// Choose one of the found moves at random
const randomIndex = Math.floor(Math.random() * moveOptions.length);
return moveOptions[randomIndex] ?? [];
};
/**
* Choose a point connected to a friendly stone to play
*/
const getGrowMove = (board, validMoves) => {
const moveOptions = [];
const size = board[0].length;
// Look through all the points on the board
for (let x = 0; x < size; x++) {
for (let y = 0; y < size; y++) {
// make sure the move is valid
const isValidMove = validMoves[x][y];
// Leave some open spaces to make it harder to capture our pieces
const isNotReservedSpace = x % 2 || y % 2;
// Make sure we are connected to a friendly piece
const neighbors = getNeighbors(board, x, y);
const hasFriendlyNeighbor = neighbors.includes("X");
if (isValidMove && isNotReservedSpace && hasFriendlyNeighbor) {
moveOptions.push([x, y]);
}
}
}
// Choose one of the found moves at random
const randomIndex = Math.floor(Math.random() * moveOptions.length);
return moveOptions[randomIndex] ?? [];
};
/**
* Find all adjacent points in the four connected directions
*/
const getNeighbors = (board, x, y) => {
const north = board[x + 1]?.[y];
const east = board[x][y + 1];
const south = board[x - 1]?.[y];
const west = board[x]?.[y - 1];
return [north, east, south, west];
};

@ -20,6 +20,7 @@ import { GoSubnetSearch } from "./GoSubnetSearch";
import { CorruptableText } from "../../ui/React/CorruptableText";
import { makeAIMove, resetAI, resolveCurrentTurn } from "../boardAnalysis/goAI";
import { GoScoreExplanation } from "./GoScoreExplanation";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
interface GoGameboardWrapperProps {
showInstructions: () => void;
@ -63,7 +64,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
// Do not implement useCallback for this function without ensuring GoGameboard still rerenders for every move
// Currently this function changing is what triggers a GoGameboard rerender, which is needed
async function clickHandler(x: number, y: number) {
function clickHandler(x: number, y: number) {
if (showPriorMove) {
SnackbarEvents.emit(
`Currently showing a past board state. Please disable "Show previous move" to continue.`,
@ -94,7 +95,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
const didUpdateBoard = makeMove(boardState, x, y, currentPlayer);
if (didUpdateBoard) {
rerender();
takeAiTurn(boardState);
takeAiTurn(boardState).catch((error) => exceptionAlert(error));
}
}
@ -113,7 +114,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
}
setTimeout(() => {
takeAiTurn(boardState);
takeAiTurn(boardState).catch((error) => exceptionAlert(error));
}, 100);
}

@ -99,13 +99,13 @@ export function handleUnknownError(e: unknown, ws: WorkerScript | null = null, i
Error has been logged to the console.\n\nType of error: ${typeof e}\nValue of error: ${e}`;
e = ws ? basicErrorMessage(ws, msg, "UNKNOWN") : msg;
}
dialogBoxCreate(initialText + e);
dialogBoxCreate(initialText + String(e));
}
/** Use this handler to handle the error when we call getSaveData function */
export function handleGetSaveDataError(error: unknown) {
/** Use this handler to handle the error when we call getSaveData function or getSaveInfo function */
export function handleGetSaveDataInfoError(error: unknown, fromGetSaveInfo = false) {
console.error(error);
let errorMessage = `Cannot get save data. Error: ${error}.`;
let errorMessage = `Cannot get save ${fromGetSaveInfo ? "info" : "data"}. Error: ${error}.`;
if (error instanceof RangeError) {
errorMessage += " This may be because the save data is too large.";
}

@ -232,7 +232,7 @@ function spawnOptions(ctx: NetscriptContext, threadOrOption: unknown): CompleteS
function mapToString(map: Map<unknown, unknown>): string {
const formattedMap = [...map]
.map((m) => {
return `${m[0]} => ${m[1]}`;
return `${String(m[0])} => ${String(m[1])}`;
})
.join("; ");
return `< Map: ${formattedMap} >`;
@ -245,7 +245,7 @@ function setToString(set: Set<unknown>): string {
/** Convert multiple arguments for tprint or print into a single string. */
function argsToString(args: unknown[]): string {
// Reduce array of args into a single output string
return args.reduce((out, arg) => {
return args.reduce((out: string, arg) => {
if (arg === null) {
return (out += "null");
}
@ -264,12 +264,13 @@ function argsToString(args: unknown[]): string {
return (out += setToString(nativeArg));
}
if (typeof nativeArg === "object") {
return (out += JSON.stringify(nativeArg, (_, value) => {
return (out += JSON.stringify(nativeArg, (_, value: unknown) => {
/**
* If the property is a promise, we will return a string that clearly states that it's a promise object, not a
* normal object. If we don't do that, all promises will be serialized into "{}".
*/
if (value instanceof Promise) {
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- "[object Promise]" is exactly the string that we want.
return value.toString();
}
if (value instanceof Map) {
@ -282,8 +283,8 @@ function argsToString(args: unknown[]): string {
}));
}
return (out += `${nativeArg}`);
}, "") as string;
return (out += String(nativeArg));
}, "");
}
function validateHGWOptions(ctx: NetscriptContext, opts: unknown): CompleteHGWOptions {
@ -296,6 +297,7 @@ function validateHGWOptions(ctx: NetscriptContext, opts: unknown): CompleteHGWOp
return result;
}
if (typeof opts !== "object") {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
throw errorMessage(ctx, `BasicHGWOptions must be an object if specified, was ${opts}`);
}
// Safe assertion since threadOrOption type has been narrowed to a non-null object
@ -723,7 +725,7 @@ function createPublicRunningScript(runningScript: RunningScript, workerScript?:
args: runningScript.args.slice(),
dynamicRamUsage: workerScript && roundToTwo(workerScript.dynamicRamUsage),
filename: runningScript.filename,
logs: runningScript.logs.map((x) => "" + x),
logs: runningScript.logs.map((x) => String(x)),
offlineExpGained: runningScript.offlineExpGained,
offlineMoneyMade: runningScript.offlineMoneyMade,
offlineRunningTime: runningScript.offlineRunningTime,
@ -782,13 +784,21 @@ function validateBitNodeOptions(ctx: NetscriptContext, bitNodeOptions: unknown):
return result;
}
if (typeof bitNodeOptions !== "object") {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
throw errorMessage(ctx, `bitNodeOptions must be an object if it's specified. It was ${bitNodeOptions}.`);
}
const options = bitNodeOptions as Unknownify<BitNodeOptions>;
if (!(options.sourceFileOverrides instanceof Map)) {
throw errorMessage(ctx, `sourceFileOverrides must be a Map.`);
}
const validationResultForSourceFileOverrides = validateSourceFileOverrides(options.sourceFileOverrides, true);
const validationResultForSourceFileOverrides = validateSourceFileOverrides(
/**
* Cast the type from Map<any, any> to Map<number, number> to satisfy the lint rule. The validation logic in
* validateSourceFileOverrides will check the data.
*/
options.sourceFileOverrides as Map<number, number>,
true,
);
if (!validationResultForSourceFileOverrides.valid) {
throw errorMessage(
ctx,

@ -542,7 +542,7 @@ export const ns: InternalAPI<NSFull> = {
return [] as string[];
}
return runningScriptObj.logs.map((x) => "" + x);
return runningScriptObj.logs.map((x) => String(x));
},
tail:
(ctx) =>
@ -1870,7 +1870,7 @@ function getFunctionNames(obj: object, prefix: string): string[] {
} else if (typeof value === "function") {
functionNames.push(prefix + key);
} else if (typeof value === "object") {
functionNames.push(...getFunctionNames(value, `${prefix}${key}.`));
functionNames.push(...getFunctionNames(value as object, `${prefix}${key}.`));
}
}
return functionNames;

@ -1,4 +1,4 @@
import type { Singularity as ISingularity, Task as ITask } from "@nsdefs";
import type { Singularity as ISingularity } from "@nsdefs";
import { Player } from "@player";
import { AugmentationName, CityName, FactionWorkType, GymType, LocationName, UniversityClassType } from "@enums";
@ -1142,7 +1142,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
? resolveScriptFilePath(helpers.string(ctx, "cbScript", _cbScript), ctx.workerScript.name)
: false;
if (cbScript === null) {
throw helpers.errorMessage(ctx, `Could not resolve file path: ${_cbScript}`);
throw helpers.errorMessage(ctx, `Could not resolve file path. callbackScript is null.`);
}
enterBitNode(true, Player.bitNodeN, nextBN, helpers.validateBitNodeOptions(ctx, _bitNodeOptions));
if (cbScript) {
@ -1159,7 +1159,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
? resolveScriptFilePath(helpers.string(ctx, "cbScript", _cbScript), ctx.workerScript.name)
: false;
if (cbScript === null) {
throw helpers.errorMessage(ctx, `Could not resolve file path: ${_cbScript}`);
throw helpers.errorMessage(ctx, `Could not resolve file path. callbackScript is null.`);
}
const wd = GetServer(SpecialServers.WorldDaemon);
@ -1194,7 +1194,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
getCurrentWork: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
if (!Player.currentWork) return null;
return Player.currentWork.APICopy() as ITask;
return Player.currentWork.APICopy();
},
exportGame: (ctx) => () => {
helpers.checkSingularityAccess(ctx);

@ -30,7 +30,7 @@ function makeScriptBlob(code: string): Blob {
// config object to provide a hook point.
export const config = {
doImport(url: ScriptURL): Promise<ScriptModule> {
return import(/*webpackIgnore:true*/ url);
return import(/*webpackIgnore:true*/ url) as Promise<ScriptModule>;
},
};

@ -36,9 +36,9 @@ export function getGangFaction(this: PlayerObject): Faction {
return fac;
}
export function getGangName(this: PlayerObject): string {
export function getGangName(this: PlayerObject): FactionName | null {
const gang = this.gang;
return gang ? gang.facName : "";
return gang ? gang.facName : null;
}
export function hasGangWith(this: PlayerObject, facName: FactionName): boolean {

@ -302,7 +302,7 @@ export function applyForJob(
}
if (!company.hasPosition(pos)) {
console.error(`Company ${company.name} does not have position ${pos}. Player.applyToCompany() failed.`);
console.error(`Company ${company.name} does not have position ${pos.name}. Player.applyToCompany() failed.`);
return null;
}

@ -11,7 +11,7 @@ export function setPlayer(playerObj: PlayerObject): void {
}
export function loadPlayer(saveString: string): PlayerObject {
const player = JSON.parse(saveString, Reviver);
const player = JSON.parse(saveString, Reviver) as PlayerObject;
player.money = parseFloat(player.money + "");
player.exploits = sanitizeExploits(player.exploits);
return player;

@ -18,7 +18,7 @@ function error(errorMsg: string, { id }: RFAMessage): RFAMessage {
return new RFAMessage({ error: errorMsg, id: id });
}
export const RFARequestHandler: Record<string, (message: RFAMessage) => void | RFAMessage> = {
export const RFARequestHandler: Record<string, (message: RFAMessage) => RFAMessage> = {
pushFile: function (msg: RFAMessage): RFAMessage {
if (!isFileData(msg.params)) return error("Misses parameters", msg);

@ -40,7 +40,7 @@ export class Remote {
}
function handleMessageEvent(this: WebSocket, e: MessageEvent): void {
const msg: RFAMessage = JSON.parse(e.data);
const msg = JSON.parse(e.data as string) as RFAMessage;
if (!msg.method || !RFARequestHandler[msg.method]) {
const response = new RFAMessage({ error: "Unknown message received", id: msg.id });

@ -44,7 +44,7 @@ import { isBinaryFormat } from "../electron/saveDataBinaryFormat";
import { downloadContentAsFile } from "./utils/FileUtils";
import { showAPIBreaks } from "./utils/APIBreaks/APIBreak";
import { breakInfos261 } from "./utils/APIBreaks/2.6.1";
import { handleGetSaveDataError } from "./Netscript/ErrorMessages";
import { handleGetSaveDataInfoError } from "./Netscript/ErrorMessages";
/* SaveObject.js
* Defines the object used to save/load games
@ -130,7 +130,7 @@ class BitburnerSaveObject {
try {
saveData = await this.getSaveData();
} catch (error) {
handleGetSaveDataError(error);
handleGetSaveDataInfoError(error);
return;
}
try {
@ -170,7 +170,7 @@ class BitburnerSaveObject {
try {
saveData = await this.getSaveData();
} catch (error) {
handleGetSaveDataError(error);
handleGetSaveDataInfoError(error);
return;
}
const filename = this.getSaveFileName();
@ -698,9 +698,12 @@ function evaluateVersionCompatibility(ver: string | number): void {
} catch (e) {
anyExportsFailed = true;
// We just need the text error, not a full stack trace
console.error(`Failed to load export of material ${material.name} (${division.name} ${warehouse.city})
console.error(
`Failed to load export of material ${material.name} (${division.name} ${warehouse.city})
Original export details: ${JSON.stringify(originalExport)}
Error: ${e}`);
Error: ${e}`,
e,
);
}
}
}
@ -805,16 +808,16 @@ async function loadGame(saveData: SaveData): Promise<boolean> {
if (Object.hasOwn(saveObj, "LastExportBonus")) {
try {
ExportBonus.setLastExportBonus(JSON.parse(saveObj.LastExportBonus));
} catch (err) {
} catch (error) {
ExportBonus.setLastExportBonus(new Date().getTime());
console.error("ERROR: Failed to parse last export bonus Settings " + err);
console.error(`ERROR: Failed to parse last export bonus setting. Error: ${error}.`, error);
}
}
if (Player.gang && Object.hasOwn(saveObj, "AllGangsSave")) {
try {
loadAllGangs(saveObj.AllGangsSave);
} catch (e) {
console.error("ERROR: Failed to parse AllGangsSave: " + e);
} catch (error) {
console.error(`ERROR: Failed to parse AllGangsSave. Error: ${error}.`, error);
}
}
if (Object.hasOwn(saveObj, "VersionSave")) {

@ -106,7 +106,7 @@ export class RunningScript {
let logEntry = txt;
if (Settings.TimestampsFormat && typeof txt === "string") {
logEntry = "[" + formatTime(Settings.TimestampsFormat) + "] " + logEntry;
logEntry = `[${formatTime(Settings.TimestampsFormat)}] ${txt}`;
}
this.logs.push(logEntry);

@ -14,6 +14,7 @@ import { NetscriptExtra } from "../NetscriptFunctions/Extra";
import * as enums from "../Enums";
import { ns } from "../NetscriptFunctions";
import { isLegacyScript } from "../Paths/ScriptFilePath";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
/** Event emitter used for tracking when changes have been made to a content file. */
export const fileEditEvents = new EventEmitter<[hostname: string, filename: ContentFilePath]>();
@ -36,7 +37,9 @@ export class ScriptEditor {
for (const [apiKey, apiValue] of Object.entries(apiLayer)) {
if (apiLayer === api && apiKey in hiddenAPI) continue;
apiKeys.push(apiKey);
if (typeof apiValue === "object") populate(apiValue);
if (typeof apiValue === "object") {
populate(apiValue as object);
}
}
}
populate();
@ -44,23 +47,25 @@ export class ScriptEditor {
(async function () {
// We have to improve the default js language otherwise theme sucks
const jsLanguage = monaco.languages.getLanguages().find((l) => l.id === "javascript");
// Unsupported function is not exposed in monaco public API.
const l = await (jsLanguage as any).loader();
if (!jsLanguage) {
return;
}
const loader = await jsLanguage.loader();
// replaced the bare tokens with regexes surrounded by \b, e.g. \b{token}\b which matches a word-break on either side
// this prevents the highlighter from highlighting pieces of variables that start with a reserved token name
l.language.tokenizer.root.unshift([new RegExp("\\bns\\b"), { token: "ns" }]);
loader.language.tokenizer.root.unshift([new RegExp("\\bns\\b"), { token: "ns" }]);
for (const symbol of apiKeys)
l.language.tokenizer.root.unshift([new RegExp(`\\b${symbol}\\b`), { token: "netscriptfunction" }]);
loader.language.tokenizer.root.unshift([new RegExp(`\\b${symbol}\\b`), { token: "netscriptfunction" }]);
const otherKeywords = ["let", "const", "var", "function", "arguments"];
const otherKeyvars = ["true", "false", "null", "undefined"];
otherKeywords.forEach((k) =>
l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeywords" }]),
loader.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeywords" }]),
);
otherKeyvars.forEach((k) =>
l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeyvars" }]),
loader.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeyvars" }]),
);
l.language.tokenizer.root.unshift([new RegExp("\\bthis\\b"), { token: "this" }]);
})();
loader.language.tokenizer.root.unshift([new RegExp("\\bthis\\b"), { token: "this" }]);
})().catch((e) => exceptionAlert(e));
// Add ts definitions for API
const source = netscriptDefinitions.replace(/export /g, "");
@ -112,16 +117,20 @@ export class ScriptEditor {
// returns a reject promise if the language worker is not loaded yet, so we wait to
// call it until the language gets loaded.
const languageWorker = new Promise<(...uris: monaco.Uri[]) => unknown>((resolve) =>
monaco.languages.onLanguage(language, () => getLanguageWorker().then(resolve)),
monaco.languages.onLanguage(language, () => {
getLanguageWorker()
.then(resolve)
.catch((error) => exceptionAlert(error));
}),
);
// Whenever a model is created, arange for it synced to the language server.
// Whenever a model is created, arrange for it to be synced to the language server.
monaco.editor.onDidCreateModel((model) => {
if (language === "typescript" && isLegacyScript(model.uri.path)) {
// Don't sync legacy scripts to typescript worker.
return;
}
if (["javascript", "typescript"].includes(model.getLanguageId())) {
languageWorker.then((cb) => cb(model.uri));
languageWorker.then((resolve) => resolve(model.uri)).catch((error) => exceptionAlert(error));
}
});
}

@ -33,6 +33,7 @@ import { useCallback } from "react";
import { type AST, getFileType, parseAST } from "../../utils/ScriptTransformer";
import { RamCalculationErrorCode } from "../../Script/RamCalculationErrorCodes";
import { hasScriptExtension, isLegacyScript } from "../../Paths/ScriptFilePath";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
interface IProps {
// Map of filename -> code
@ -105,7 +106,9 @@ function Root(props: IProps): React.ReactElement {
const server = GetServer(currentScript.hostname);
if (server === null) throw new Error("Server should not be null but it is.");
server.writeToContentFile(currentScript.path, currentScript.code);
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
if (Settings.SaveGameOnFileSave) {
saveObject.saveGame().catch((error) => exceptionAlert(error));
}
rerender();
}, [rerender]);
@ -285,7 +288,9 @@ function Root(props: IProps): React.ReactElement {
if (!server) throw new Error("Server should not be null but it is.");
// This server helper already handles overwriting, etc.
server.writeToContentFile(scriptToSave.path, scriptToSave.code);
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
if (Settings.SaveGameOnFileSave) {
saveObject.saveGame().catch((error) => exceptionAlert(error));
}
}
function currentTabIndex(): number | undefined {

@ -35,7 +35,10 @@ export function Toolbar({ editor, onSave }: IProps) {
const [optionsOpen, { on: openOptions, off: closeOptions }] = useBoolean(false);
function beautify(): void {
editor?.getAction("editor.action.formatDocument")?.run();
editor
?.getAction("editor.action.formatDocument")
?.run()
.catch((error) => console.error(error));
}
const { ram, ramEntries, isUpdatingRAM, options, saveOptions } = useScriptEditorContext();

@ -218,7 +218,7 @@ export function makeTheme(theme: IScriptEditorTheme): editor.IStandaloneThemeDat
return { base: theme.base, inherit: theme.inherit, rules: themeRules, colors: themeColors };
}
export async function loadThemes(defineTheme: DefineThemeFn): Promise<void> {
export function loadThemes(defineTheme: DefineThemeFn): void {
defineTheme("monokai", {
base: "vs-dark",
inherit: true,

@ -157,7 +157,12 @@ export const Settings = {
disableSuffixes: false,
load(saveString: string) {
const save = JSON.parse(saveString);
const save = JSON.parse(saveString) as {
theme?: typeof Settings.theme;
styles?: typeof Settings.styles;
overview?: typeof Settings.overview;
EditorTheme?: typeof Settings.EditorTheme;
};
save.theme && Object.assign(Settings.theme, save.theme);
save.styles && Object.assign(Settings.styles, save.styles);
save.overview && Object.assign(Settings.overview, save.overview);

@ -50,7 +50,7 @@ export function buyStock(
}
if (stock == null || isNaN(shares)) {
if (ctx) {
helpers.log(ctx, () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
helpers.log(ctx, () => `Invalid arguments: stock='${stock?.name}' shares='${shares}'`);
} else if (opts.suppressDialog !== true) {
dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer");
}
@ -145,7 +145,7 @@ export function sellStock(
// Sanitize/Validate arguments
if (stock == null || shares < 0 || isNaN(shares)) {
if (ctx) {
helpers.log(ctx, () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
helpers.log(ctx, () => `Invalid arguments: stock='${stock?.name}' shares='${shares}'`);
} else if (opts.suppressDialog !== true) {
dialogBoxCreate(
"Failed to sell stock. This is probably due to an invalid quantity. Otherwise, this may be a bug, contact developer",
@ -225,7 +225,7 @@ export function shortStock(
}
if (stock == null || isNaN(shares)) {
if (ctx) {
helpers.log(ctx, () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
helpers.log(ctx, () => `Invalid arguments: stock='${stock?.name}' shares='${shares}'`);
} else if (opts.suppressDialog !== true) {
dialogBoxCreate(
"Failed to initiate a short position in a stock. This is probably " +
@ -321,7 +321,7 @@ export function sellShort(
): boolean {
if (stock == null || isNaN(shares) || shares < 0) {
if (ctx) {
helpers.log(ctx, () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
helpers.log(ctx, () => `Invalid arguments: stock='${stock?.name}' shares='${shares}'`);
} else if (!opts.suppressDialog) {
dialogBoxCreate(
"Failed to sell a short position in a stock. This is probably " +

@ -20,7 +20,13 @@ export function exportScripts(pattern: string, server: BaseServer, currDir = roo
const filename = `bitburner${
hasScriptExtension(pattern) ? "Scripts" : pattern.endsWith(".txt") ? "Texts" : "Files"
}.zip`;
zip.generateAsync({ type: "blob" }).then((content: Blob) => downloadContentAsFile(content, filename));
zip
.generateAsync({ type: "blob" })
.then((content: Blob) => downloadContentAsFile(content, filename))
.catch((error) => {
console.error(error);
Terminal.error(`Cannot compress scripts with pattern ${pattern} on ${server.hostname}. Error: ${error}`);
});
}
export function download(args: (string | number | boolean)[], server: BaseServer): void {
@ -33,9 +39,10 @@ export function download(args: (string | number | boolean)[], server: BaseServer
try {
exportScripts(pattern, server, Terminal.currDir);
return;
} catch (e: any) {
const msg = String(e?.message ?? e);
return Terminal.error(msg);
} catch (error) {
console.error(error);
Terminal.error(`Cannot export scripts with pattern ${pattern} on ${server.hostname}. Error: ${error}`);
return;
}
}
const path = Terminal.getFilepath(pattern);

@ -383,13 +383,19 @@ function writeToTerminal(
if (options.isVerbose) Terminal.print(verboseInfo);
}
function checkOutFile(outFileStr: string, options: Options, server: BaseServer): ContentFilePath | void {
if (!outFileStr) return;
function checkOutFile(outFileStr: string, options: Options, server: BaseServer): ContentFilePath | null {
if (!outFileStr) {
return null;
}
const outFilePath = Terminal.getFilepath(outFileStr);
if (!outFilePath || !hasTextExtension(outFilePath)) {
return Terminal.error(ERR.badOutFile(outFileStr));
Terminal.error(ERR.badOutFile(outFileStr));
return null;
}
if (!options.isOverWrite && server.textFiles.has(outFilePath)) {
Terminal.error(ERR.outFileExists(outFileStr));
return null;
}
if (!options.isOverWrite && server.textFiles.has(outFilePath)) return Terminal.error(ERR.outFileExists(outFileStr));
return outFilePath;
}
@ -433,7 +439,8 @@ export function grep(args: (string | number | boolean)[], server: BaseServer): v
if (options.isPipeIn) files.length = 0;
if (!options.isQuiet) writeToTerminal(prettyResult, options, results, files, pattern);
if (params.outfile && outFilePath) server.writeToContentFile(outFilePath, rawResult.join("\n"));
} catch (e) {
Terminal.error("grep processing error: " + e);
} catch (error) {
console.error(error);
Terminal.error(`grep processing error: ${error}`);
}
}

@ -44,7 +44,8 @@ export function kill(args: (string | number | boolean)[], server: BaseServer): v
if (killed >= 5) {
Terminal.print(`... killed ${killed} instances total`);
}
} catch (e) {
Terminal.error(e + "");
} catch (error) {
console.error(error);
Terminal.error(String(error));
}
}

@ -43,7 +43,8 @@ export function mem(args: (string | number | boolean)[], server: BaseServer): vo
// Let's warn the user that he might need to save his script again to generate the detailed entries
Terminal.warn("You might have to open & save this script to see the detailed RAM usage information.");
}
} catch (e) {
Terminal.error(e + "");
} catch (error) {
console.error(error);
Terminal.error(String(error));
}
}

@ -19,7 +19,10 @@ export function run(args: (string | number | boolean)[], server: BaseServer): vo
if (hasScriptExtension(path)) {
return runScript(path, args, server);
} else if (hasContractExtension(path)) {
Terminal.runContract(path);
Terminal.runContract(path).catch((error) => {
console.error(error);
Terminal.error(`Cannot run contract ${path} on ${server.hostname}. Error: ${error}.`);
});
return;
} else if (hasProgramExtension(path)) {
return runProgram(path, args, server);

@ -25,7 +25,7 @@ export function runScript(path: ScriptFilePath, commandArgs: (string | number |
argv: commandArgs,
});
} catch (error) {
Terminal.error(`Invalid arguments. ${String(error)}.`);
Terminal.error(`Invalid arguments. ${error}.`);
return;
}
const tailFlag = flags["--tail"] === true;

@ -32,7 +32,8 @@ export function tail(commandArray: (string | number | boolean)[], server: BaseSe
}
LogBoxEvents.emit(runningScript);
}
} catch (e) {
Terminal.error(e + "");
} catch (error) {
console.error(error);
Terminal.error(String(error));
}
}

@ -37,7 +37,7 @@ function getDB(): Promise<IDBObjectStore> {
export function load(): Promise<SaveData> {
return getDB().then((db) => {
return new Promise<SaveData>((resolve, reject) => {
const request: IDBRequest<SaveData> = db.get("save");
const request = db.get("save") as IDBRequest<SaveData>;
request.onerror = function (this: IDBRequest<SaveData>) {
reject(new Error("Error in Database request to get save data", { cause: this.error }));
};

@ -237,7 +237,7 @@ const Engine: {
Engine.Counters.autoSaveCounter = 60 * 5; // Let's check back in a bit
} else {
Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5;
saveObject.saveGame(!Settings.SuppressSavedGameToast);
saveObject.saveGame(!Settings.SuppressSavedGameToast).catch((error) => console.error(error));
}
}
},

@ -75,6 +75,7 @@ import { HistoryProvider } from "./React/Documentation";
import { GoRoot } from "../Go/ui/GoRoot";
import { Settings } from "../Settings/Settings";
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
const htmlLocation = location;
@ -154,8 +155,14 @@ export function GameRoot(): React.ReactElement {
for (const server of GetAllServers()) {
server.runningScriptMap.clear();
}
saveObject.saveGame();
saveObject
.saveGame()
.then(() => {
setTimeout(() => htmlLocation.reload(), 2000);
})
.catch((error) => {
exceptionAlert(error);
});
}
function attemptedForbiddenRouting(name: string) {
@ -333,11 +340,13 @@ export function GameRoot(): React.ReactElement {
case Page.Options: {
mainPage = (
<GameOptionsRoot
save={() => saveObject.saveGame()}
save={() => {
saveObject.saveGame().catch((error) => exceptionAlert(error));
}}
export={() => {
// Apply the export bonus before saving the game
onExport();
saveObject.exportGame();
saveObject.exportGame().catch((error) => exceptionAlert(error));
}}
forceKill={killAllScripts}
softReset={softReset}
@ -356,7 +365,7 @@ export function GameRoot(): React.ReactElement {
exportGameFn={() => {
// Apply the export bonus before saving the game
onExport();
saveObject.exportGame();
saveObject.exportGame().catch((error) => exceptionAlert(error));
}}
installAugmentationsFn={() => {
installAugmentations();
@ -395,7 +404,9 @@ export function GameRoot(): React.ReactElement {
!ITutorial.isRunning ? (
<CharacterOverview
parentOpen={parentOpen}
save={() => saveObject.saveGame()}
save={() => {
saveObject.saveGame().catch((error) => exceptionAlert(error));
}}
killScripts={killAllScripts}
/>
) : (

@ -38,7 +38,7 @@ import { useBoolean } from "../hooks";
import { ComparisonIcon } from "./ComparisonIcon";
import { SaveData } from "../../../types";
import { handleGetSaveDataError } from "../../../Netscript/ErrorMessages";
import { handleGetSaveDataInfoError } from "../../../Netscript/ErrorMessages";
const useStyles = makeStyles()((theme: Theme) => ({
root: {
@ -140,7 +140,7 @@ export const ImportSave = (props: { saveData: SaveData; automatic: boolean }): J
// We cannot show dialog box in this screen (due to "withPopups = false"), so we will try showing it with a
// delay. 1 second is usually enough to go back to other normal screens that allow showing popups.
setTimeout(() => {
handleGetSaveDataError(error);
handleGetSaveDataInfoError(error);
}, 1000);
});
}

@ -1,8 +1,6 @@
/** Generic Event Emitter class following a subscribe/publish paradigm. */
export class EventEmitter<T extends any[]> {
private subscribers: Set<(...args: [...T]) => void | undefined> = new Set();
constructor() {}
private subscribers: Set<(...args: [...T]) => void> = new Set();
subscribe(s: (...args: [...T]) => void): () => void {
this.subscribers.add(s);

@ -239,7 +239,7 @@ export const v2APIBreak = () => {
for (const server of GetAllServers()) {
server.runningScriptMap = new Map();
}
saveObject.exportGame();
saveObject.exportGame().catch((e) => console.error(e));
};
const formatOffenders = (offenders: IFileLine[]): string => {