CODEBASE: Fix lint errors 2 (#1756)

This commit is contained in:
catloversg 2024-11-07 14:09:11 +07:00 committed by GitHub
parent e3c10e9f0f
commit 36c143b687
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 267 additions and 146 deletions

@ -35,6 +35,7 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"react/no-unescaped-entities": "off", "react/no-unescaped-entities": "off",
"@typescript-eslint/restrict-template-expressions": "off", "@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/no-unsafe-enum-comparison": "off",
}, },
settings: { settings: {
react: { react: {

@ -49,3 +49,14 @@ declare global {
} }
} }
} }
module "monaco-vim" {
export const initVimMode: (...args: unknown[]) => { dispose: () => void };
export const VimMode: {
Vim: {
defineEx: (...args: unknown[]) => void;
mapCommand: (...args: unknown[]) => void;
defineAction: (...args: unknown[]) => void;
};
};
}

@ -3,6 +3,7 @@ import { Card, Suit } from "./Card";
import { makeStyles } from "tss-react/mui"; import { makeStyles } from "tss-react/mui";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import { throwIfReachable } from "../../utils/helpers/throwIfReachable";
interface Props { interface Props {
card: Card; card: Card;
@ -51,7 +52,7 @@ export const ReactCard: FC<Props> = ({ card, hidden }) => {
suit = <span>&#9824;</span>; suit = <span>&#9824;</span>;
break; break;
default: default:
throw new Error(`MissingCaseException: ${card.suit}`); throwIfReachable(card.suit);
} }
return ( return (
<Paper className={`${classes.card} ${card.isRedSuit() ? classes.red : classes.black}`}> <Paper className={`${classes.card} ${card.isRedSuit() ? classes.red : classes.black}`}>

@ -2,13 +2,15 @@ import { Company } from "./Company";
import { CompanyPosition } from "./CompanyPosition"; import { CompanyPosition } from "./CompanyPosition";
import { PlayerCondition, haveSkill, haveCompanyRep } from "../Faction/FactionJoinCondition"; import { PlayerCondition, haveSkill, haveCompanyRep } from "../Faction/FactionJoinCondition";
import type { Skills } from "../PersonObjects/Skills"; import { getRecordEntries } from "../Types/Record";
export function getJobRequirements(company: Company, pos: CompanyPosition): PlayerCondition[] { export function getJobRequirements(company: Company, pos: CompanyPosition): PlayerCondition[] {
const reqSkills = pos.requiredSkills(company.jobStatReqOffset); const reqSkills = pos.requiredSkills(company.jobStatReqOffset);
const reqs = []; const reqs = [];
for (const [skillName, value] of Object.entries(reqSkills)) { for (const [skillName, value] of getRecordEntries(reqSkills)) {
if (value > 0) reqs.push(haveSkill(skillName as keyof Skills, value)); if (value > 0) {
reqs.push(haveSkill(skillName, value));
}
} }
if (pos.requiredReputation > 0) { if (pos.requiredReputation > 0) {
reqs.push(haveCompanyRep(company.name, pos.requiredReputation)); reqs.push(haveCompanyRep(company.name, pos.requiredReputation));

@ -17,7 +17,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { JSONMap, JSONSet } from "../Types/Jsonable"; import { JSONMap, JSONSet } from "../Types/Jsonable";
import { formatMoney } from "../ui/formatNumber"; import { formatMoney } from "../ui/formatNumber";
import { isPositiveInteger } from "../types"; import { isPositiveInteger, type Result } from "../types";
import { createEnumKeyedRecord, getRecordValues } from "../Types/Record"; import { createEnumKeyedRecord, getRecordValues } from "../Types/Record";
import { getKeyList } from "../utils/helpers/getKeyList"; import { getKeyList } from "../utils/helpers/getKeyList";
@ -372,29 +372,56 @@ export class Corporation {
} }
} }
/** Purchasing a one-time unlock /**
* @returns A string on failure, indicating the reason for failure. */ * Purchasing a one-time unlock
purchaseUnlock(unlockName: CorpUnlockName): string | void { */
if (this.unlocks.has(unlockName)) return `The corporation has already unlocked ${unlockName}`; purchaseUnlock(unlockName: CorpUnlockName): Result {
if (this.unlocks.has(unlockName)) {
return {
success: false,
message: `${unlockName} has already been unlocked.`,
};
}
const price = CorpUnlocks[unlockName].price; const price = CorpUnlocks[unlockName].price;
if (this.funds < price) return `Insufficient funds to purchase ${unlockName}, requires ${formatMoney(price)}`; if (this.funds < price) {
return {
success: false,
message: `Insufficient funds to purchase ${unlockName}, requires ${formatMoney(price)}.`,
};
}
this.loseFunds(price, "upgrades"); this.loseFunds(price, "upgrades");
this.unlocks.add(unlockName); this.unlocks.add(unlockName);
// Apply effects for one-time unlocks // Apply effects for one-time unlocks
if (unlockName === CorpUnlockName.ShadyAccounting) this.dividendTax -= 0.05; if (unlockName === CorpUnlockName.ShadyAccounting) {
if (unlockName === CorpUnlockName.GovernmentPartnership) this.dividendTax -= 0.1; this.dividendTax -= 0.05;
}
if (unlockName === CorpUnlockName.GovernmentPartnership) {
this.dividendTax -= 0.1;
}
return {
success: true,
};
} }
/** Purchasing a levelable upgrade /**
* @returns A string on failure, indicating the reason for failure. */ * Purchasing a levelable upgrade
purchaseUpgrade(upgradeName: CorpUpgradeName, amount = 1): string | void { */
purchaseUpgrade(upgradeName: CorpUpgradeName, amount = 1): Result {
if (!isPositiveInteger(amount)) { if (!isPositiveInteger(amount)) {
return `Number of upgrade levels purchased must be a positive integer (attempted: ${amount}).`; return {
success: false,
message: `Number of upgrade levels purchased must be a positive integer (attempted: ${amount}).`,
};
} }
const upgrade = CorpUpgrades[upgradeName]; const upgrade = CorpUpgrades[upgradeName];
const totalCost = calculateUpgradeCost(this, upgrade, amount); const totalCost = calculateUpgradeCost(this, upgrade, amount);
if (this.funds < totalCost) return `Not enough funds to purchase ${amount} of upgrade ${upgradeName}.`; if (this.funds < totalCost) {
return {
success: false,
message: `Not enough funds to purchase ${amount} of upgrade ${upgradeName}.`,
};
}
this.loseFunds(totalCost, "upgrades"); this.loseFunds(totalCost, "upgrades");
this.upgrades[upgradeName].level += amount; this.upgrades[upgradeName].level += amount;
this.upgrades[upgradeName].value += upgrade.benefit * amount; this.upgrades[upgradeName].value += upgrade.benefit * amount;
@ -407,6 +434,9 @@ export class Corporation {
} }
} }
} }
return {
success: true,
};
} }
getProductionMultiplier(): number { getProductionMultiplier(): number {

@ -16,6 +16,7 @@ import { PartialRecord, getRecordEntries, getRecordKeys, getRecordValues } from
import { Material } from "./Material"; import { Material } from "./Material";
import { getKeyList } from "../utils/helpers/getKeyList"; import { getKeyList } from "../utils/helpers/getKeyList";
import { calculateMarkupMultiplier } from "./helpers"; import { calculateMarkupMultiplier } from "./helpers";
import { throwIfReachable } from "../utils/helpers/throwIfReachable";
interface DivisionParams { interface DivisionParams {
name: string; name: string;
@ -704,8 +705,7 @@ export class Division {
case "START": case "START":
break; break;
default: default:
console.error(`Invalid state: ${state}`); throwIfReachable(state);
break;
} //End switch(this.state) } //End switch(this.state)
this.updateWarehouseSizeUsed(warehouse); this.updateWarehouseSizeUsed(warehouse);
} }
@ -936,8 +936,7 @@ export class Division {
case "EXPORT": case "EXPORT":
break; break;
default: default:
console.error(`Invalid State: ${state}`); throwIfReachable(state);
break;
} //End switch(this.state) } //End switch(this.state)
this.updateWarehouseSizeUsed(warehouse); this.updateWarehouseSizeUsed(warehouse);
} }

@ -5,6 +5,7 @@ import { Division } from "./Division";
import { Corporation } from "./Corporation"; import { Corporation } from "./Corporation";
import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive";
import { createEnumKeyedRecord, getRecordKeys } from "../Types/Record"; import { createEnumKeyedRecord, getRecordKeys } from "../Types/Record";
import { throwIfReachable } from "../utils/helpers/throwIfReachable";
interface IParams { interface IParams {
city: CityName; city: CityName;
@ -165,8 +166,7 @@ export class OfficeSpace {
case "total": case "total":
continue; continue;
default: default:
console.error(`Invalid employee position: ${name}`); throwIfReachable(name);
break;
} }
this.employeeProductionByJob[name] = this.employeeJobs[name] * prodMult * prodBase; this.employeeProductionByJob[name] = this.employeeJobs[name] * prodMult * prodBase;
total += this.employeeProductionByJob[name]; total += this.employeeProductionByJob[name];

@ -214,7 +214,9 @@ export class Product {
calculateRating(industry: Division): void { calculateRating(industry: Division): void {
const weights = IndustriesData[industry.type].product?.ratingWeights; const weights = IndustriesData[industry.type].product?.ratingWeights;
if (!weights) return console.error(`Could not find product rating weights for: ${industry}`); if (!weights) {
return console.error(`Could not find product rating weights for: ${industry.name}`);
}
this.rating = getRecordEntries(weights).reduce( this.rating = getRecordEntries(weights).reduce(
(total, [statName, weight]) => total + this.stats[statName] * weight, (total, [statName, weight]) => total + this.stats[statName] * weight,
0, 0,

@ -31,8 +31,10 @@ export function LevelableUpgrade({ upgradeName, mult, rerender }: IProps): React
const tooltip = data.desc; const tooltip = data.desc;
function onClick(): void { function onClick(): void {
if (corp.funds < cost) return; if (corp.funds < cost) return;
const message = corp.purchaseUpgrade(upgradeName, amount); const result = corp.purchaseUpgrade(upgradeName, amount);
if (message) dialogBoxCreate(`Could not upgrade ${upgradeName} ${amount} times:\n${message}`); if (!result.success) {
dialogBoxCreate(`Could not upgrade ${upgradeName} ${amount} times:\n${result.message}`);
}
rerender(); rerender();
} }

@ -25,8 +25,10 @@ export function Unlock(props: UnlockProps): React.ReactElement {
function onClick(): void { function onClick(): void {
// corp.unlock handles displaying a dialog on failure // corp.unlock handles displaying a dialog on failure
const message = corp.purchaseUnlock(props.name); const result = corp.purchaseUnlock(props.name);
if (message) dialogBoxCreate(`Error while attempting to purchase ${props.name}:\n${message}`); if (!result.success) {
dialogBoxCreate(`Error while attempting to purchase ${props.name}:\n${result.message}`);
}
// Rerenders the parent, which should remove this item if the purchase was successful // Rerenders the parent, which should remove this item if the purchase was successful
props.rerender(); props.rerender();
} }

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

@ -7,17 +7,17 @@ export let staneksGift = new StaneksGift();
export function loadStaneksGift(saveString: string): void { export function loadStaneksGift(saveString: string): void {
if (saveString) { if (saveString) {
staneksGift = JSON.parse(saveString, Reviver); staneksGift = JSON.parse(saveString, Reviver) as StaneksGift;
} else { } else {
staneksGift = new StaneksGift(); staneksGift = new StaneksGift();
} }
} }
export function zeros(width: number, height: number): number[][] { export function zeros(width: number, height: number): number[][] {
const array = []; const array: number[][] = [];
for (let i = 0; i < width; ++i) { for (let i = 0; i < width; ++i) {
array.push(Array(height).fill(0)); array.push(Array<number>(height).fill(0));
} }
return array; return array;

@ -18,9 +18,6 @@ export class StaneksGift extends BaseGift {
isBonusCharging = false; isBonusCharging = false;
justCharged = true; justCharged = true;
storedCycles = 0; storedCycles = 0;
constructor() {
super();
}
baseSize(): number { baseSize(): number {
return StanekConstants.BaseSize + currentNodeMults.StaneksGiftExtraSize + Player.activeSourceFileLvl(13); return StanekConstants.BaseSize + currentNodeMults.StaneksGiftExtraSize + Player.activeSourceFileLvl(13);

@ -155,13 +155,27 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
> >
<Button onClick={startImport} startIcon={<Upload />} sx={{ gridArea: "import" }}> <Button onClick={startImport} startIcon={<Upload />} sx={{ gridArea: "import" }}>
Import Game Import Game
<input ref={importInput} id="import-game-file-selector" type="file" hidden onChange={onImport} /> <input
ref={importInput}
id="import-game-file-selector"
type="file"
hidden
onChange={(event) => {
onImport(event).catch((error) => {
console.error(error);
});
}}
/>
</Button> </Button>
</Tooltip> </Tooltip>
<ConfirmationModal <ConfirmationModal
open={importSaveOpen} open={importSaveOpen}
onClose={() => setImportSaveOpen(false)} onClose={() => setImportSaveOpen(false)}
onConfirm={() => confirmedImportGame()} onConfirm={() => {
confirmedImportGame().catch((error) => {
console.error(error);
});
}}
additionalButton={<Button onClick={compareSaveGame}>Compare Save</Button>} additionalButton={<Button onClick={compareSaveGame}>Compare Save</Button>}
confirmationText={ confirmationText={
<> <>

@ -15,6 +15,7 @@ import { Settings } from "../../Settings/Settings";
import { MoneyRate } from "../../ui/React/MoneyRate"; import { MoneyRate } from "../../ui/React/MoneyRate";
import { StatsRow } from "../../ui/React/StatsRow"; import { StatsRow } from "../../ui/React/StatsRow";
import { useStyles } from "../../ui/React/CharacterOverview"; import { useStyles } from "../../ui/React/CharacterOverview";
import { getKeyFromReactElements } from "../../utils/StringHelperFunctions";
interface IProps { interface IProps {
member: GangMember; member: GangMember;
@ -103,7 +104,7 @@ export function GangMemberStats(props: IProps): React.ReactElement {
</TableCell> </TableCell>
</TableRow> </TableRow>
{data.map(([a, b]) => ( {data.map(([a, b]) => (
<TableRow key={a.toString() + b.toString()}> <TableRow key={getKeyFromReactElements(a, b)}>
<TableCell classes={{ root: classes.cellNone }}> <TableCell classes={{ root: classes.cellNone }}>
<Typography>{a}</Typography> <Typography>{a}</Typography>
</TableCell> </TableCell>

@ -83,7 +83,11 @@ export function loadGo(data: unknown): boolean {
Go.storeCycles(loadStoredCycles(parsedData.storedCycles)); Go.storeCycles(loadStoredCycles(parsedData.storedCycles));
// If it's the AI's turn, initiate their turn, which will populate nextTurn // If it's the AI's turn, initiate their turn, which will populate nextTurn
if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) makeAIMove(currentGame); if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) {
makeAIMove(currentGame).catch((error) => {
showError(error);
});
}
// If it's not the AI's turn and we're not in gameover status, initialize nextTurn promise based on the previous move/pass // If it's not the AI's turn and we're not in gameover status, initialize nextTurn promise based on the previous move/pass
else if (currentGame.previousPlayer) { else if (currentGame.previousPlayer) {
const previousMove = getPreviousMove(); const previousMove = getPreviousMove();

@ -135,7 +135,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
return ( return (
<Typography <Typography
key={`${item}${uniqueId()}`} key={uniqueId()}
sx={{ sx={{
color: color, color: color,
border: `2px solid ${item.current ? Settings.theme.infolight : Settings.theme.primary}`, border: `2px solid ${item.current ? Settings.theme.infolight : Settings.theme.primary}`,

@ -66,7 +66,7 @@ class NSProxyHandler<API extends GenericAPI<API>> {
const descriptor = Object.getOwnPropertyDescriptor(this.ns, key); const descriptor = Object.getOwnPropertyDescriptor(this.ns, key);
if (!descriptor) return descriptor; if (!descriptor) return descriptor;
const field = descriptor.value; const field: unknown = descriptor.value;
if (typeof field === "function") { if (typeof field === "function") {
const arrayPath = [...this.tree, key]; const arrayPath = [...this.tree, key];
@ -74,7 +74,7 @@ class NSProxyHandler<API extends GenericAPI<API>> {
const ctx = { workerScript: this.ws, function: key, functionPath }; const ctx = { workerScript: this.ws, function: key, functionPath };
// Only do the context-binding once, instead of each time the function // Only do the context-binding once, instead of each time the function
// is called. // is called.
const func: any = field(ctx); const func = field(ctx) as (...args: unknown[]) => unknown;
const wrappedFunction = function (...args: unknown[]): unknown { const wrappedFunction = function (...args: unknown[]): unknown {
// What remains *must* be called every time. // What remains *must* be called every time.
helpers.checkEnvFlags(ctx); helpers.checkEnvFlags(ctx);

@ -692,8 +692,11 @@ function getRunningScript(ctx: NetscriptContext, ident: ScriptIdentifier): Runni
return findRunningScriptByPid(ident); return findRunningScriptByPid(ident);
} else { } else {
const scripts = getRunningScriptsByArgs(ctx, ident.scriptname, ident.hostname, ident.args); const scripts = getRunningScriptsByArgs(ctx, ident.scriptname, ident.hostname, ident.args);
if (scripts === null) return null; if (scripts === null) {
return scripts.values().next().value ?? null; return null;
}
const next = scripts.values().next();
return !next.done ? next.value : null;
} }
} }

@ -624,15 +624,19 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
checkAccess(ctx); checkAccess(ctx);
const unlockName = getEnumHelper("CorpUnlockName").nsGetMember(ctx, _unlockName, "unlockName"); const unlockName = getEnumHelper("CorpUnlockName").nsGetMember(ctx, _unlockName, "unlockName");
const corporation = getCorporation(); const corporation = getCorporation();
const message = corporation.purchaseUnlock(unlockName); const result = corporation.purchaseUnlock(unlockName);
if (message) throw new Error(`Could not unlock ${unlockName}: ${message}`); if (!result.success) {
throw new Error(`Could not unlock ${unlockName}: ${result.message}`);
}
}, },
levelUpgrade: (ctx) => (_upgradeName) => { levelUpgrade: (ctx) => (_upgradeName) => {
checkAccess(ctx); checkAccess(ctx);
const upgradeName = getEnumHelper("CorpUpgradeName").nsGetMember(ctx, _upgradeName, "upgradeName"); const upgradeName = getEnumHelper("CorpUpgradeName").nsGetMember(ctx, _upgradeName, "upgradeName");
const corporation = getCorporation(); const corporation = getCorporation();
const message = corporation.purchaseUpgrade(upgradeName, 1); const result = corporation.purchaseUpgrade(upgradeName, 1);
if (message) throw new Error(`Could not upgrade ${upgradeName}: ${message}`); if (!result.success) {
throw new Error(`Could not upgrade ${upgradeName}: ${result.message}`);
}
}, },
issueDividends: (ctx) => (_rate) => { issueDividends: (ctx) => (_rate) => {
checkAccess(ctx); checkAccess(ctx);

@ -13,6 +13,7 @@ import { Factions } from "../Faction/Factions";
import { getEnumHelper } from "../utils/EnumHelper"; import { getEnumHelper } from "../utils/EnumHelper";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { filterTruthy } from "../utils/helpers/ArrayHelpers"; import { filterTruthy } from "../utils/helpers/ArrayHelpers";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
export function NetscriptInfiltration(): InternalAPI<NetscriptInfiltation> { export function NetscriptInfiltration(): InternalAPI<NetscriptInfiltation> {
const getLocationsWithInfiltrations = Object.values(Locations).filter( const getLocationsWithInfiltrations = Object.values(Locations).filter(
@ -21,16 +22,29 @@ export function NetscriptInfiltration(): InternalAPI<NetscriptInfiltation> {
const calculateInfiltrationData = (ctx: NetscriptContext, locationName: LocationName): InfiltrationLocation => { const calculateInfiltrationData = (ctx: NetscriptContext, locationName: LocationName): InfiltrationLocation => {
const location = Locations[locationName]; const location = Locations[locationName];
if (location === undefined) throw helpers.errorMessage(ctx, `Location '${location}' does not exists.`); if (location === undefined) {
if (location.infiltrationData === undefined) throw helpers.errorMessage(ctx, `Location "${locationName}" does not exist.`);
throw helpers.errorMessage(ctx, `Location '${location}' does not provide infiltrations.`); }
if (location.infiltrationData === undefined) {
throw helpers.errorMessage(ctx, `Location "${locationName}" does not provide infiltrations.`);
}
const locationCity = location.city;
/**
* location.city is only null when the location is available in all cities. This kind of location does not have
* infiltration data.
*/
if (locationCity === null) {
const errorMessage = `Location "${locationName}" is available in all cities, but it still has infiltration data.`;
exceptionAlert(new Error(errorMessage));
throw helpers.errorMessage(ctx, errorMessage);
}
const startingSecurityLevel = location.infiltrationData.startingSecurityLevel; const startingSecurityLevel = location.infiltrationData.startingSecurityLevel;
const difficulty = calculateDifficulty(startingSecurityLevel); const difficulty = calculateDifficulty(startingSecurityLevel);
const reward = calculateReward(startingSecurityLevel); const reward = calculateReward(startingSecurityLevel);
const maxLevel = location.infiltrationData.maxClearanceLevel; const maxLevel = location.infiltrationData.maxClearanceLevel;
return { return {
location: { location: {
city: location.city!, city: locationCity,
name: location.name, name: location.name,
}, },
reward: { reward: {

@ -47,7 +47,7 @@ export function portHandle(n: PortNumber): NetscriptPort {
}; };
} }
export function writePort(n: PortNumber, value: unknown): any { export function writePort(n: PortNumber, value: unknown): unknown {
const port = getPort(n); const port = getPort(n);
// Primitives don't need to be cloned. // Primitives don't need to be cloned.
port.add(isObjectLike(value) ? structuredClone(value) : value); port.add(isObjectLike(value) ? structuredClone(value) : value);
@ -63,15 +63,15 @@ export function tryWritePort(n: PortNumber, value: unknown): boolean {
return true; return true;
} }
export function readPort(n: PortNumber): any { export function readPort(n: PortNumber): unknown {
const port = NetscriptPorts.get(n); const port = NetscriptPorts.get(n);
if (!port || !port.data.length) return emptyPortData; if (!port || !port.data.length) return emptyPortData;
const returnVal = port.data.shift(); const returnVal: unknown = port.data.shift();
if (!port.data.length && !port.resolver) NetscriptPorts.delete(n); if (!port.data.length && !port.resolver) NetscriptPorts.delete(n);
return returnVal; return returnVal;
} }
export function peekPort(n: PortNumber): any { export function peekPort(n: PortNumber): unknown {
const port = NetscriptPorts.get(n); const port = NetscriptPorts.get(n);
if (!port || !port.data.length) return emptyPortData; if (!port || !port.data.length) return emptyPortData;
// Needed to avoid exposing internal objects. // Needed to avoid exposing internal objects.

@ -26,6 +26,7 @@ import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork"; import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork"; import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
import { canAccessBitNodeFeature } from "../../../BitNode/BitNodeUtils"; import { canAccessBitNodeFeature } from "../../../BitNode/BitNodeUtils";
import { getKeyFromReactElements } from "../../../utils/StringHelperFunctions";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle; const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@ -177,7 +178,7 @@ export function EarningsElement(props: IProps): React.ReactElement {
</TableCell> </TableCell>
</TableRow> </TableRow>
{data.map(([a, b]) => ( {data.map(([a, b]) => (
<TableRow key={a.toString() + b.toString()}> <TableRow key={getKeyFromReactElements(a, b)}>
<TableCell classes={{ root: classes.cellNone }}> <TableCell classes={{ root: classes.cellNone }}>
<Typography>{a}</Typography> <Typography>{a}</Typography>
</TableCell> </TableCell>

@ -231,16 +231,16 @@ function parseOnlyRamCalculate(
prefix: string, prefix: string,
obj: object, obj: object,
ref: string, ref: string,
): { func: () => number | number; refDetail: string } | undefined => { ): { func: (() => number) | number; refDetail: string } | undefined => {
if (!obj) { if (!obj) {
return; return;
} }
const elem = Object.entries(obj).find(([key]) => key === ref); const elem = Object.entries(obj).find(([key]) => key === ref);
if (elem !== undefined && (typeof elem[1] === "function" || typeof elem[1] === "number")) { if (elem !== undefined && (typeof elem[1] === "function" || typeof elem[1] === "number")) {
return { func: elem[1], refDetail: `${prefix}${ref}` }; return { func: elem[1] as (() => number) | number, refDetail: `${prefix}${ref}` };
} }
for (const [key, value] of Object.entries(obj)) { for (const [key, value] of Object.entries(obj)) {
const found = findFunc(`${key}.`, value, ref); const found = findFunc(`${key}.`, value as object, ref);
if (found) { if (found) {
return found; return found;
} }

@ -76,7 +76,7 @@ export class Script implements ContentFile {
const ramCalc = calculateRamUsage(this.code, this.filename, this.server, otherScripts); const ramCalc = calculateRamUsage(this.code, this.filename, this.server, otherScripts);
if (ramCalc.cost && ramCalc.cost >= RamCostConstants.Base) { if (ramCalc.cost && ramCalc.cost >= RamCostConstants.Base) {
this.ramUsage = roundToTwo(ramCalc.cost); this.ramUsage = roundToTwo(ramCalc.cost);
this.ramUsageEntries = ramCalc.entries as RamUsageEntry[]; this.ramUsageEntries = ramCalc.entries;
this.ramCalculationError = null; this.ramCalculationError = null;
return; return;
} }

@ -1,5 +1,4 @@
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
// @ts-expect-error This library does not have types.
import * as MonacoVim from "monaco-vim"; import * as MonacoVim from "monaco-vim";
import type { editor } from "monaco-editor"; import type { editor } from "monaco-editor";
type IStandaloneCodeEditor = editor.IStandaloneCodeEditor; type IStandaloneCodeEditor = editor.IStandaloneCodeEditor;
@ -18,8 +17,7 @@ interface IProps {
} }
export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, onSave }: IProps) { export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, onSave }: IProps) {
// monaco-vim does not have types, so this is an any const [vimEditor, setVimEditor] = useState<ReturnType<typeof MonacoVim.initVimMode> | null>(null);
const [vimEditor, setVimEditor] = useState<any>(null);
const statusBarRef = useRef<React.ReactElement | null>(null); const statusBarRef = useRef<React.ReactElement | null>(null);
const rerender = useRerender(); const rerender = useRerender();
@ -30,7 +28,6 @@ export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, on
useEffect(() => { useEffect(() => {
// setup monaco-vim // setup monaco-vim
if (vim && editor && !vimEditor) { if (vim && editor && !vimEditor) {
// Using try/catch because MonacoVim does not have types.
try { try {
setVimEditor(MonacoVim.initVimMode(editor, statusBarRef, StatusBar, rerender)); setVimEditor(MonacoVim.initVimMode(editor, statusBarRef, StatusBar, rerender));
MonacoVim.VimMode.Vim.defineEx("write", "w", function () { MonacoVim.VimMode.Vim.defineEx("write", "w", function () {
@ -65,8 +62,7 @@ export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, on
MonacoVim.VimMode.Vim.mapCommand("gT", "action", "prevTabs", {}, { context: "normal" }); MonacoVim.VimMode.Vim.mapCommand("gT", "action", "prevTabs", {}, { context: "normal" });
editor.focus(); editor.focus();
} catch (e) { } catch (e) {
console.error("An error occurred while loading monaco-vim:"); console.error("An error occurred while loading monaco-vim:", e);
console.error(e);
} }
} else if (!vim) { } else if (!vim) {
// When vim mode is disabled // When vim mode is disabled

@ -2,6 +2,7 @@ import { GetServer } from "../../Server/AllServers";
import { editor, Uri } from "monaco-editor"; import { editor, Uri } from "monaco-editor";
import { OpenScript } from "./OpenScript"; import { OpenScript } from "./OpenScript";
import { getFileType, FileType } from "../../utils/ScriptTransformer"; import { getFileType, FileType } from "../../utils/ScriptTransformer";
import { throwIfReachable } from "../../utils/helpers/throwIfReachable";
function getServerCode(scripts: OpenScript[], index: number): string | null { function getServerCode(scripts: OpenScript[], index: number): string | null {
const openScript = scripts[index]; const openScript = scripts[index];
@ -48,7 +49,7 @@ function makeModel(hostname: string, filename: string, code: string) {
language = "javascript"; language = "javascript";
break; break;
default: default:
throw new Error(`Invalid file type: ${fileType}. Filename: ${filename}.`); throwIfReachable(fileType);
} }
//if somehow a model already exist return it //if somehow a model already exist return it
return editor.getModel(uri) ?? editor.createModel(code, language, uri); return editor.getModel(uri) ?? editor.createModel(code, language, uri);

@ -8,8 +8,9 @@ import Typography from "@mui/material/Typography";
import ExpandLessIcon from "@mui/icons-material/ExpandLess"; import ExpandLessIcon from "@mui/icons-material/ExpandLess";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { SidebarItem, ICreateProps as IItemProps } from "./SidebarItem"; import { SidebarItem, ICreateProps as IItemProps, type SidebarItemProps } from "./SidebarItem";
import type { Page } from "../../ui/Router"; import type { Page } from "../../ui/Router";
import type { PartialRecord } from "../../Types/Record";
type SidebarAccordionProps = { type SidebarAccordionProps = {
key_: string; key_: string;
@ -19,9 +20,12 @@ type SidebarAccordionProps = {
items: (IItemProps | boolean)[]; items: (IItemProps | boolean)[];
icon: React.ReactElement["type"]; icon: React.ReactElement["type"];
sidebarOpen: boolean; sidebarOpen: boolean;
classes: any; classes: Record<"listitem" | "active", string>;
}; };
type ClickFnCacheKeyType = (page: Page) => void;
type ClickFnCacheValueType = PartialRecord<Page, SidebarItemProps["clickFn"]>;
// We can't useCallback for this, because in the items map it would be // We can't useCallback for this, because in the items map it would be
// called a changing number of times, and hooks can't be called in loops. So // called a changing number of times, and hooks can't be called in loops. So
// we set up this explicit cache of function objects instead. // we set up this explicit cache of function objects instead.
@ -30,7 +34,7 @@ type SidebarAccordionProps = {
// WeakMap prevents memory leaks. We won't drop slices of the cache too soon, // WeakMap prevents memory leaks. We won't drop slices of the cache too soon,
// because the fn keys are themselves memoized elsewhere, which keeps them // because the fn keys are themselves memoized elsewhere, which keeps them
// alive and thus keeps the WeakMap entries alive. // alive and thus keeps the WeakMap entries alive.
const clickFnCache = new WeakMap(); const clickFnCache = new WeakMap<ClickFnCacheKeyType, ClickFnCacheValueType>();
function getClickFn(toWrap: (page: Page) => void, page: Page) { function getClickFn(toWrap: (page: Page) => void, page: Page) {
let first = clickFnCache.get(toWrap); let first = clickFnCache.get(toWrap);
if (first === undefined) { if (first === undefined) {

@ -18,7 +18,7 @@ export interface ICreateProps {
export interface SidebarItemProps extends ICreateProps { export interface SidebarItemProps extends ICreateProps {
clickFn: () => void; clickFn: () => void;
flash: boolean; flash: boolean;
classes: any; classes: Record<"listitem" | "active", string>;
sidebarOpen: boolean; sidebarOpen: boolean;
} }

@ -17,6 +17,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import * as React from "react"; import * as React from "react";
import { throwIfReachable } from "../utils/helpers/throwIfReachable";
export interface IProcessOrderRefs { export interface IProcessOrderRefs {
stockMarket: IStockMarket; stockMarket: IStockMarket;
@ -50,8 +51,8 @@ export function processOrders(
return; // Newly created, so no orders to process return; // Newly created, so no orders to process
} }
let stockOrders = orderBook[stock.symbol]; let stockOrders = orderBook[stock.symbol];
if (stockOrders == null || !(stockOrders.constructor === Array)) { if (stockOrders == null || !Array.isArray(stockOrders)) {
console.error(`Invalid Order book for ${stock.symbol} in processOrders(): ${stockOrders}`); console.error(`Invalid Order book for ${stock.symbol} in processOrders(). stockOrders: ${stockOrders}`);
stockOrders = []; stockOrders = [];
return; return;
} }
@ -88,8 +89,7 @@ export function processOrders(
} }
break; break;
default: default:
console.warn(`Invalid order type: ${order.type}`); throwIfReachable(order.type);
return;
} }
} }
} }
@ -137,8 +137,7 @@ function executeOrder(order: Order, refs: IProcessOrderRefs): void {
} }
break; break;
default: default:
console.warn(`Invalid order type: ${order.type}`); throwIfReachable(order.type);
return;
} }
// Position type, for logging/message purposes // Position type, for logging/message purposes

@ -21,6 +21,9 @@ export function check(args: (string | number | boolean)[], server: BaseServer):
Terminal.error(`No script named ${scriptName} is running on the server`); Terminal.error(`No script named ${scriptName} is running on the server`);
return; return;
} }
runningScripts.values().next().value.displayLog(); const next = runningScripts.values().next();
if (!next.done) {
next.value.displayLog();
}
} }
} }

@ -9,11 +9,11 @@ export function expr(args: (string | number | boolean)[]): void {
// Sanitize the math expression // Sanitize the math expression
const sanitizedExpr = expr.replace(/[^-()\d/*+.%]/g, ""); const sanitizedExpr = expr.replace(/[^-()\d/*+.%]/g, "");
let result; let result: string;
try { try {
result = eval?.(sanitizedExpr); result = String(eval?.(sanitizedExpr));
} catch (e) { } catch (e) {
Terminal.error(`Could not evaluate expression: ${sanitizedExpr}`); Terminal.error(`Could not evaluate expression: ${sanitizedExpr}. Error: ${e}.`);
return; return;
} }
Terminal.print(result); Terminal.print(result);

@ -86,7 +86,7 @@ export function TerminalInput(): React.ReactElement {
function getSearchSuggestionPrespace() { function getSearchSuggestionPrespace() {
const currentPrefix = `[${Player.getCurrentServer().hostname} /${Terminal.cwd()}]> `; const currentPrefix = `[${Player.getCurrentServer().hostname} /${Terminal.cwd()}]> `;
const prefixLength = `${currentPrefix}${value}`.length; const prefixLength = `${currentPrefix}${value}`.length;
return Array(prefixLength).fill(" "); return Array<string>(prefixLength).fill(" ");
} }
function modifyInput(mod: Modification): void { function modifyInput(mod: Modification): void {
@ -206,7 +206,7 @@ export function TerminalInput(): React.ReactElement {
return () => document.removeEventListener("keydown", keyDown); return () => document.removeEventListener("keydown", keyDown);
}); });
async function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): Promise<void> { async function onKeyDown(event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>): Promise<void> {
const ref = terminalInput.current; const ref = terminalInput.current;
// Run command or insert newline // Run command or insert newline
@ -427,7 +427,11 @@ export function TerminalInput(): React.ReactElement {
setPossibilities([]); setPossibilities([]);
resetSearch(); resetSearch();
}, },
onKeyDown: onKeyDown, onKeyDown: (event) => {
onKeyDown(event).catch((error) => {
console.error(error);
});
},
}} }}
></TextField> ></TextField>
<Popper <Popper

@ -41,7 +41,7 @@ export function TerminalRoot(): React.ReactElement {
const [key, setKey] = useState(0); const [key, setKey] = useState(0);
useEffect(() => { useEffect(() => {
const debounced = _.debounce(async () => rerender(), 25, { maxWait: 50 }); const debounced = _.debounce(() => rerender(), 25, { maxWait: 50 });
const unsubscribe = TerminalEvents.subscribe(debounced); const unsubscribe = TerminalEvents.subscribe(debounced);
return () => { return () => {
debounced.cancel(); debounced.cancel();
@ -51,7 +51,7 @@ export function TerminalRoot(): React.ReactElement {
useEffect(() => { useEffect(() => {
const clear = () => setKey((key) => key + 1); const clear = () => setKey((key) => key + 1);
const debounced = _.debounce(async () => clear(), 25, { maxWait: 50 }); const debounced = _.debounce(() => clear(), 25, { maxWait: 50 });
const unsubscribe = TerminalClearEvents.subscribe(debounced); const unsubscribe = TerminalClearEvents.subscribe(debounced);
return () => { return () => {
debounced.cancel(); debounced.cancel();

@ -917,7 +917,7 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
desc: (data: number[][]): string => { desc: (data: number[][]): string => {
return [ return [
"You are located in the top-left corner of the following grid:\n\n", "You are located in the top-left corner of the following grid:\n\n",
`&nbsp;&nbsp;[${data.map((line) => "[" + line + "]").join(",\n&nbsp;&nbsp;&nbsp;")}]\n\n`, `&nbsp;&nbsp;[${data.map((line) => `[${line}]`).join(",\n&nbsp;&nbsp;&nbsp;")}]\n\n`,
"You are trying to find the shortest path to the bottom-right corner of the grid,", "You are trying to find the shortest path to the bottom-right corner of the grid,",
"but there are obstacles on the grid that you cannot move onto.", "but there are obstacles on the grid that you cannot move onto.",
"These obstacles are denoted by '1', while empty spaces are denoted by 0.\n\n", "These obstacles are denoted by '1', while empty spaces are denoted by 0.\n\n",
@ -945,8 +945,8 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
const dstX = width - 1; const dstX = width - 1;
const minPathLength = dstY + dstX; // Math.abs(dstY - srcY) + Math.abs(dstX - srcX) const minPathLength = dstY + dstX; // Math.abs(dstY - srcY) + Math.abs(dstX - srcX)
const grid: number[][] = new Array(height); const grid: number[][] = new Array<number[]>(height);
for (let y = 0; y < height; y++) grid[y] = new Array(width).fill(0); for (let y = 0; y < height; y++) grid[y] = new Array<number>(width).fill(0);
for (let y = 0; y < height; y++) { for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) { for (let x = 0; x < width; x++) {
@ -969,12 +969,12 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
const dstY = height - 1; const dstY = height - 1;
const dstX = width - 1; const dstX = width - 1;
const distance: [number][] = new Array(height); const distance: number[][] = new Array<number[]>(height);
//const prev: [[number, number] | undefined][] = new Array(height); //const prev: [[number, number] | undefined][] = new Array(height);
const queue: [number, number][] = []; const queue: [number, number][] = [];
for (let y = 0; y < height; y++) { for (let y = 0; y < height; y++) {
distance[y] = new Array(width).fill(Infinity) as [number]; distance[y] = new Array<number>(width).fill(Infinity);
//prev[y] = new Array(width).fill(undefined) as [undefined]; //prev[y] = new Array(width).fill(undefined) as [undefined];
} }
@ -1422,7 +1422,7 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
//Attempt to construct one to check if this is correct. //Attempt to construct one to check if this is correct.
if (sanitizedPlayerAns === "") { if (sanitizedPlayerAns === "") {
//Verify that there is no solution by attempting to create a proper 2-coloring. //Verify that there is no solution by attempting to create a proper 2-coloring.
const coloring: (number | undefined)[] = Array(data[0]).fill(undefined); const coloring: (number | undefined)[] = Array<number | undefined>(data[0]).fill(undefined);
while (coloring.some((val) => val === undefined)) { while (coloring.some((val) => val === undefined)) {
//Color a vertex in the graph //Color a vertex in the graph
const initialVertex: number = coloring.findIndex((val) => val === undefined); const initialVertex: number = coloring.findIndex((val) => val === undefined);
@ -1435,9 +1435,7 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
const neighbors: number[] = neighbourhood(v); const neighbors: number[] = neighbourhood(v);
//For each vertex u adjacent to v //For each vertex u adjacent to v
for (const id in neighbors) { for (const u of neighbors) {
const u: number = neighbors[id];
//Set the color of u to the opposite of v's color if it is new, //Set the color of u to the opposite of v's color if it is new,
//then add u to the frontier to continue the algorithm. //then add u to the frontier to continue the algorithm.
if (coloring[u] === undefined) { if (coloring[u] === undefined) {

@ -46,7 +46,7 @@ export const A = (props: React.PropsWithChildren<{ href?: string }>): React.Reac
cursor: "pointer", cursor: "pointer",
}} }}
> >
<CorruptableText content={props.children + ""} spoiler={true} /> <CorruptableText content={String(props.children)} spoiler={true} />
</span> </span>
); );
return ( return (

@ -38,7 +38,7 @@ type RowName = SkillRowName | "HP" | "Money";
const OverviewEventEmitter = new EventEmitter(); const OverviewEventEmitter = new EventEmitter();
// These values aren't displayed, they're just used for comparison to check if state has changed // These values aren't displayed, they're just used for comparison to check if state has changed
const valUpdaters: Record<RowName, () => any> = { const valUpdaters: Record<RowName, () => unknown> = {
HP: () => Player.hp.current + "|" + Player.hp.max, // This isn't displayed, it's just compared for updates. HP: () => Player.hp.current + "|" + Player.hp.max, // This isn't displayed, it's just compared for updates.
Money: () => Player.money, Money: () => Player.money,
Hack: () => Player.skills.hacking, Hack: () => Player.skills.hacking,

@ -26,9 +26,11 @@ export function CinematicLine(props: IProps): React.ReactElement {
return; return;
} }
let cancel = false; let cancel = false;
(async () => { sleep(10)
await sleep(10).then(() => !cancel && advance()); .then(() => !cancel && advance())
})(); .catch((error) => {
console.error(error);
});
return () => { return () => {
cancel = true; cancel = true;
}; };

@ -399,7 +399,11 @@ export const ImportSave = (props: { saveData: SaveData; automatic: boolean }): J
<ConfirmationModal <ConfirmationModal
open={isImportModalOpen} open={isImportModalOpen}
onClose={closeImportModal} onClose={closeImportModal}
onConfirm={handleImport} onConfirm={() => {
handleImport().catch((error) => {
console.error(error);
});
}}
confirmationText={ confirmationText={
<> <>
Importing new save game data will <strong>completely wipe</strong> the current game data! Importing new save game data will <strong>completely wipe</strong> the current game data!

@ -298,7 +298,16 @@ function LogWindow({ hidden, script, onClose }: LogWindowProps): React.ReactElem
return !(bounds.right < 0 || bounds.bottom < 0 || bounds.left > innerWidth || bounds.top > outerWidth); return !(bounds.right < 0 || bounds.bottom < 0 || bounds.left > innerWidth || bounds.top > outerWidth);
}; };
const onDrag = (e: DraggableEvent): void | false => { /**
* The returned type of onDrag is a bit weird here. The Draggable component expects an onDrag that returns "void | false".
* In that component's internal code, it checks for the explicit "false" value. If onDrag returns false, the component
* cancels the dragging.
*
* That's why they use "void | false" as the returned type. However, in TypeScript, "void" is not supposed to be used
* like that. ESLint will complain "void is not valid as a constituent in a union type". Please check its documentation
* for the reason. In order to solve this problem, I changed the returned type to "undefined | false".
*/
const onDrag = (e: DraggableEvent): undefined | false => {
e.preventDefault(); e.preventDefault();
// bound to body // bound to body
if ( if (

@ -98,7 +98,7 @@ export function RecoveryRoot({ softReset, errorData, resetError }: IProps): Reac
{sourceError && ( {sourceError && (
<Box> <Box>
<Typography variant="h6" color={Settings.theme.error}> <Typography variant="h6" color={Settings.theme.error}>
Error: {sourceError.toString()} Error: {String(sourceError)}
</Typography> </Typography>
<br /> <br />
</Box> </Box>

@ -14,7 +14,7 @@ interface IProps {
const useStyles = makeStyles()({ const useStyles = makeStyles()({
snackbar: { snackbar: {
// Log popup z-index increments, so let's add a padding to be well above them. // Log popup z-index increments, so let's add a padding to be well above them.
zIndex: `${logBoxBaseZIndex + 1000} !important` as any, zIndex: `${logBoxBaseZIndex + 1000} !important`,
"& .MuiAlert-icon": { "& .MuiAlert-icon": {
alignSelf: "center", alignSelf: "center",

@ -1,4 +1,4 @@
// choose random character for generating plaintexts to compress // choose random characters for generating plaintext to compress
export function comprGenChar(): string { export function comprGenChar(): string {
const r = Math.random(); const r = Math.random();
if (r < 0.4) { if (r < 0.4) {
@ -34,13 +34,13 @@ export function comprLZGenerate(): string {
return plain.substring(0, length); return plain.substring(0, length);
} }
// compress plaintest string // compress plaintext string
export function comprLZEncode(plain: string): string { export function comprLZEncode(plain: string): string {
// for state[i][j]: // for state[i][j]:
// if i is 0, we're adding a literal of length j // if i is 0, we're adding a literal of length j
// else, we're adding a backreference of offset i and length j // else, we're adding a backreference of offset i and length j
let cur_state: (string | null)[][] = Array.from(Array(10), () => Array(10).fill(null)); let cur_state: (string | null)[][] = Array.from(Array(10), () => Array<string | null>(10).fill(null));
let new_state: (string | null)[][] = Array.from(Array(10), () => Array(10)); let new_state: (string | null)[][] = Array.from(Array(10), () => Array<string | null>(10));
function set(state: (string | null)[][], i: number, j: number, str: string): void { function set(state: (string | null)[][], i: number, j: number, str: string): void {
const current = state[i][j]; const current = state[i][j];

@ -3,7 +3,7 @@ import { Terminal } from "../Terminal";
const deprecatedWarningsGiven = new Set(); const deprecatedWarningsGiven = new Set();
export function setDeprecatedProperties( export function setDeprecatedProperties(
obj: object, obj: object,
properties: Record<string, { identifier: string; message: string; value: any }>, properties: Record<string, { identifier: string; message: string; value: unknown }>,
) { ) {
for (const [name, info] of Object.entries(properties)) { for (const [name, info] of Object.entries(properties)) {
Object.defineProperty(obj, name, { Object.defineProperty(obj, name, {
@ -11,7 +11,7 @@ export function setDeprecatedProperties(
deprecationWarning(info.identifier, info.message); deprecationWarning(info.identifier, info.message);
return info.value; return info.value;
}, },
set: (value: any) => (info.value = value), set: (value: unknown) => (info.value = value),
enumerable: true, enumerable: true,
}); });
} }

@ -5,6 +5,7 @@ import * as allEnums from "../Enums";
import { assertString } from "../Netscript/TypeAssertion"; import { assertString } from "../Netscript/TypeAssertion";
import { errorMessage } from "../Netscript/ErrorMessages"; import { errorMessage } from "../Netscript/ErrorMessages";
import { getRandomIntInclusive } from "./helpers/getRandomIntInclusive"; import { getRandomIntInclusive } from "./helpers/getRandomIntInclusive";
import { getRecordValues } from "../Types/Record";
interface GetMemberOptions { interface GetMemberOptions {
/** Whether to use fuzzy matching on the input (case insensitive, ignore spaces and dashes) */ /** Whether to use fuzzy matching on the input (case insensitive, ignore spaces and dashes) */
@ -22,7 +23,7 @@ class EnumHelper<EnumObj extends object, EnumMember extends Member<EnumObj> & st
constructor(obj: EnumObj, name: string) { constructor(obj: EnumObj, name: string) {
this.name = name; this.name = name;
this.defaultArgName = name.charAt(0).toLowerCase() + name.slice(1); this.defaultArgName = name.charAt(0).toLowerCase() + name.slice(1);
this.valueArray = Object.values(obj); this.valueArray = getRecordValues(obj);
this.valueSet = new Set(this.valueArray); this.valueSet = new Set(this.valueArray);
this.fuzzMap = new Map(this.valueArray.map((val) => [val.toLowerCase().replace(/[ -]+/g, ""), val])); this.fuzzMap = new Map(this.valueArray.map((val) => [val.toLowerCase().replace(/[ -]+/g, ""), val]));
} }

@ -1,10 +1,10 @@
export function HammingEncode(data: number): string { export function HammingEncode(data: number): string {
const enc: number[] = [0]; const enc: number[] = [0];
const data_bits: any[] = data.toString(2).split("").reverse(); const data_bits: number[] = data
.toString(2)
data_bits.forEach((e, i, a) => { .split("")
a[i] = parseInt(e); .reverse()
}); .map((value) => parseInt(value));
let k = data_bits.length; let k = data_bits.length;
@ -18,35 +18,36 @@ export function HammingEncode(data: number): string {
} }
} }
let parity: any = 0; let parityNumber = 0;
/* Figure out the subsection parities */ /* Figure out the subsection parities */
for (let i = 0; i < enc.length; i++) { for (let i = 0; i < enc.length; i++) {
if (enc[i]) { if (enc[i]) {
parity ^= i; parityNumber ^= i;
} }
} }
parity = parity.toString(2).split("").reverse(); const parityArray = parityNumber
parity.forEach((e: any, i: any, a: any) => { .toString(2)
a[i] = parseInt(e); .split("")
}); .reverse()
.map((value) => parseInt(value));
/* Set the parity bits accordingly */ /* Set the parity bits accordingly */
for (let i = 0; i < parity.length; i++) { for (let i = 0; i < parityArray.length; i++) {
enc[2 ** i] = parity[i] ? 1 : 0; enc[2 ** i] = parityArray[i] ? 1 : 0;
} }
parity = 0; parityNumber = 0;
/* Figure out the overall parity for the entire block */ /* Figure out the overall parity for the entire block */
for (let i = 0; i < enc.length; i++) { for (let i = 0; i < enc.length; i++) {
if (enc[i]) { if (enc[i]) {
parity++; parityNumber++;
} }
} }
/* Finally set the overall parity bit */ /* Finally set the overall parity bit */
enc[0] = parity % 2 == 0 ? 0 : 1; enc[0] = parityNumber % 2 == 0 ? 0 : 1;
return enc.join(""); return enc.join("");
} }
@ -68,11 +69,11 @@ export function HammingEncodeProperly(data: number): string {
const k: number = 2 ** m - m - 1; const k: number = 2 ** m - m - 1;
const enc: number[] = [0]; const enc: number[] = [0];
const data_bits: any[] = data.toString(2).split("").reverse(); const data_bits: number[] = data
.toString(2)
data_bits.forEach((e, i, a) => { .split("")
a[i] = parseInt(e); .reverse()
}); .map((value) => parseInt(value));
/* Flip endianness as in the original implementation by Hedrauta /* Flip endianness as in the original implementation by Hedrauta
* and write the data back to front * and write the data back to front
@ -83,35 +84,36 @@ export function HammingEncodeProperly(data: number): string {
} }
} }
let parity: any = 0; let parityNumber = 0;
/* Figure out the subsection parities */ /* Figure out the subsection parities */
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
if (enc[i]) { if (enc[i]) {
parity ^= i; parityNumber ^= i;
} }
} }
parity = parity.toString(2).split("").reverse(); const parityArray = parityNumber
parity.forEach((e: any, i: any, a: any) => { .toString(2)
a[i] = parseInt(e); .split("")
}); .reverse()
.map((value) => parseInt(value));
/* Set the parity bits accordingly */ /* Set the parity bits accordingly */
for (let i = 0; i < m; i++) { for (let i = 0; i < m; i++) {
enc[2 ** i] = parity[i] ? 1 : 0; enc[2 ** i] = parityArray[i] ? 1 : 0;
} }
parity = 0; parityNumber = 0;
/* Figure out the overall parity for the entire block */ /* Figure out the overall parity for the entire block */
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
if (enc[i]) { if (enc[i]) {
parity++; parityNumber++;
} }
} }
/* Finally set the overall parity bit */ /* Finally set the overall parity bit */
enc[0] = parity % 2 == 0 ? 0 : 1; enc[0] = parityNumber % 2 == 0 ? 0 : 1;
return enc.join(""); return enc.join("");
} }
@ -121,8 +123,9 @@ export function HammingDecode(data: string): number {
const bits: number[] = []; const bits: number[] = [];
/* TODO why not just work with an array of digits from the start? */ /* TODO why not just work with an array of digits from the start? */
for (const i in data.split("")) { const bitStringArray = data.split("");
const bit = parseInt(data[i]); for (let i = 0; i < bitStringArray.length; ++i) {
const bit = parseInt(bitStringArray[i]);
bits[i] = bit; bits[i] = bit;
if (bit) { if (bit) {

@ -125,3 +125,9 @@ export function getNsApiDocumentationUrl(isDevBranch: boolean = CONSTANTS.isDevB
isDevBranch ? "dev" : "stable" isDevBranch ? "dev" : "stable"
}/markdown/bitburner.ns.md`; }/markdown/bitburner.ns.md`;
} }
export function getKeyFromReactElements(a: string | React.JSX.Element, b: string | React.JSX.Element): string {
const keyOfA = typeof a === "string" ? a : a.key ?? "";
const keyOfb = typeof b === "string" ? b : b.key ?? "";
return keyOfA + keyOfb;
}

@ -0,0 +1,3 @@
export function throwIfReachable(missingCase: never) {
throw new Error(`The case of ${missingCase} was not handled.`);
}