mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-17 13:13:49 +01:00
CODEBASE: Fix lint errors 3 (#1758)
This is a really big refactor because it actually *fixes* a lot of the lint errors instead of disabling them.
This commit is contained in:
parent
97ca8c5f5e
commit
75cf9c88b5
14
.eslintrc.js
14
.eslintrc.js
@ -42,4 +42,18 @@ module.exports = {
|
|||||||
version: "detect",
|
version: "detect",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
overrides: [
|
||||||
|
/**
|
||||||
|
* Some enums are subsets of other enums. For example, UniversityLocationName contains locations of 3 universities.
|
||||||
|
* With each member, we refer to the respective LocationName's member instead of using a literal string. This usage
|
||||||
|
* is okay, but it triggers the "prefer-literal-enum-member" rule. This rule is not useful in this case, so we
|
||||||
|
* suppress it in NetscriptDefinitions.d.ts.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
files: ["src/ScriptEditor/NetscriptDefinitions.d.ts"],
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/prefer-literal-enum-member": ["off"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@ -69,6 +69,7 @@
|
|||||||
"@types/react-beautiful-dnd": "^13.1.5",
|
"@types/react-beautiful-dnd": "^13.1.5",
|
||||||
"@types/react-dom": "^17.0.21",
|
"@types/react-dom": "^17.0.21",
|
||||||
"@types/react-resizable": "^3.0.5",
|
"@types/react-resizable": "^3.0.5",
|
||||||
|
"@types/sprintf-js": "^1.1.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||||
"@typescript-eslint/parser": "^6.9.1",
|
"@typescript-eslint/parser": "^6.9.1",
|
||||||
"babel-jest": "^29.7.0",
|
"babel-jest": "^29.7.0",
|
||||||
@ -5124,6 +5125,13 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/sprintf-js": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/sprintf-js/-/sprintf-js-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-aWK1reDYWxcjgcIIPmQi3u+OQDuYa9b+lr6eIsGWrekJ9vr1NSjr4Eab8oQ1iKuH1ltFHpXGyerAv1a3FMKxzQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/stack-utils": {
|
"node_modules/@types/stack-utils": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
"@types/react-beautiful-dnd": "^13.1.5",
|
"@types/react-beautiful-dnd": "^13.1.5",
|
||||||
"@types/react-dom": "^17.0.21",
|
"@types/react-dom": "^17.0.21",
|
||||||
"@types/react-resizable": "^3.0.5",
|
"@types/react-resizable": "^3.0.5",
|
||||||
|
"@types/sprintf-js": "^1.1.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||||
"@typescript-eslint/parser": "^6.9.1",
|
"@typescript-eslint/parser": "^6.9.1",
|
||||||
"babel-jest": "^29.7.0",
|
"babel-jest": "^29.7.0",
|
||||||
|
@ -51,6 +51,7 @@ import { Sleeve } from "../PersonObjects/Sleeve/Sleeve";
|
|||||||
import { autoCompleteTypeShorthand } from "./utils/terminalShorthands";
|
import { autoCompleteTypeShorthand } from "./utils/terminalShorthands";
|
||||||
import { resolveTeamCasualties, type OperationTeam } from "./Actions/TeamCasualties";
|
import { resolveTeamCasualties, type OperationTeam } from "./Actions/TeamCasualties";
|
||||||
import { shuffleArray } from "../Infiltration/ui/BribeGame";
|
import { shuffleArray } from "../Infiltration/ui/BribeGame";
|
||||||
|
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||||
|
|
||||||
export const BladeburnerPromise: PromisePair<number> = { promise: null, resolve: null };
|
export const BladeburnerPromise: PromisePair<number> = { promise: null, resolve: null };
|
||||||
|
|
||||||
@ -1403,9 +1404,10 @@ export class Bladeburner implements OperationTeam {
|
|||||||
|
|
||||||
/** Initializes a Bladeburner object from a JSON save state. */
|
/** Initializes a Bladeburner object from a JSON save state. */
|
||||||
static fromJSON(value: IReviverValue): Bladeburner {
|
static fromJSON(value: IReviverValue): Bladeburner {
|
||||||
|
objectAssert(value.data);
|
||||||
// operations and contracts are not loaded directly from the save, we load them in using a different method
|
// operations and contracts are not loaded directly from the save, we load them in using a different method
|
||||||
const contractsData = value.data?.contracts;
|
const contractsData = value.data.contracts;
|
||||||
const operationsData = value.data?.operations;
|
const operationsData = value.data.operations;
|
||||||
const bladeburner = Generic_fromJSON(Bladeburner, value.data, Bladeburner.keysToLoad);
|
const bladeburner = Generic_fromJSON(Bladeburner, value.data, Bladeburner.keysToLoad);
|
||||||
// Loading this way allows better typesafety and also allows faithfully reconstructing contracts/operations
|
// Loading this way allows better typesafety and also allows faithfully reconstructing contracts/operations
|
||||||
// even from save data that is missing a lot of static info about the objects.
|
// even from save data that is missing a lot of static info about the objects.
|
||||||
|
@ -4,6 +4,7 @@ import { codingContractTypesMetadata } from "./data/codingcontracttypes";
|
|||||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "./utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "./utils/JSONReviver";
|
||||||
import { CodingContractEvent } from "./ui/React/CodingContractModal";
|
import { CodingContractEvent } from "./ui/React/CodingContractModal";
|
||||||
import { ContractFilePath, resolveContractFilePath } from "./Paths/ContractFilePath";
|
import { ContractFilePath, resolveContractFilePath } from "./Paths/ContractFilePath";
|
||||||
|
import { objectAssert } from "./utils/helpers/typeAssertion";
|
||||||
|
|
||||||
/* Contract Types */
|
/* Contract Types */
|
||||||
export const CodingContractTypes = Object.fromEntries(codingContractTypesMetadata.map((x) => [x.name, x]));
|
export const CodingContractTypes = Object.fromEntries(codingContractTypesMetadata.map((x) => [x.name, x]));
|
||||||
@ -127,6 +128,7 @@ export class CodingContract {
|
|||||||
|
|
||||||
/** Initializes a CodingContract from a JSON save state. */
|
/** Initializes a CodingContract from a JSON save state. */
|
||||||
static fromJSON(value: IReviverValue): CodingContract {
|
static fromJSON(value: IReviverValue): CodingContract {
|
||||||
|
objectAssert(value.data);
|
||||||
// In previous versions, there was a data field instead of a state field.
|
// In previous versions, there was a data field instead of a state field.
|
||||||
if ("data" in value.data) {
|
if ("data" in value.data) {
|
||||||
value.data.state = value.data.data;
|
value.data.state = value.data.data;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { CorpStateName } from "@nsdefs";
|
import { CorpStateName } from "@nsdefs";
|
||||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||||
import { stateNames } from "./data/Constants";
|
import { stateNames } from "./data/Constants";
|
||||||
|
|
||||||
export class CorporationState {
|
export class CorporationState {
|
||||||
// Number representing what state the Corporation is in. The number
|
// Number representing what state the Corporation is in. The number
|
||||||
// is an index for the array that holds all Corporation States
|
// is an index for the array that holds all Corporation States
|
||||||
|
@ -100,12 +100,6 @@ export class GangMember {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTask(): GangMemberTask {
|
getTask(): GangMemberTask {
|
||||||
// TODO unplanned: transfer that to a save file migration function
|
|
||||||
// Backwards compatibility
|
|
||||||
if ((this.task as any) instanceof GangMemberTask) {
|
|
||||||
this.task = (this.task as any).name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.hasOwn(GangMemberTasks, this.task)) {
|
if (Object.hasOwn(GangMemberTasks, this.task)) {
|
||||||
return GangMemberTasks[this.task];
|
return GangMemberTasks[this.task];
|
||||||
}
|
}
|
||||||
|
@ -165,9 +165,9 @@ function fieldEquals(a: boolean[][], b: boolean[][]): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generateEmptyField(difficulty: Difficulty): boolean[][] {
|
function generateEmptyField(difficulty: Difficulty): boolean[][] {
|
||||||
const field = [];
|
const field: boolean[][] = [];
|
||||||
for (let i = 0; i < Math.round(difficulty.height); i++) {
|
for (let i = 0; i < Math.round(difficulty.height); i++) {
|
||||||
field.push(new Array(Math.round(difficulty.width)).fill(false));
|
field.push(new Array<boolean>(Math.round(difficulty.width)).fill(false));
|
||||||
}
|
}
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,12 @@ import {
|
|||||||
GymType,
|
GymType,
|
||||||
JobName,
|
JobName,
|
||||||
JobField,
|
JobField,
|
||||||
LiteratureName,
|
type LiteratureName,
|
||||||
LocationName,
|
LocationName,
|
||||||
ToastVariant,
|
ToastVariant,
|
||||||
UniversityClassType,
|
UniversityClassType,
|
||||||
CompanyName,
|
CompanyName,
|
||||||
|
type MessageFilename,
|
||||||
} from "@enums";
|
} from "@enums";
|
||||||
import { PromptEvent } from "./ui/React/PromptManager";
|
import { PromptEvent } from "./ui/React/PromptManager";
|
||||||
import { GetServer, DeleteServer, AddToAllServers, createUniqueRandomIp } from "./Server/AllServers";
|
import { GetServer, DeleteServer, AddToAllServers, createUniqueRandomIp } from "./Server/AllServers";
|
||||||
@ -145,8 +146,19 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
stock: NetscriptStockMarket(),
|
stock: NetscriptStockMarket(),
|
||||||
grafting: NetscriptGrafting(),
|
grafting: NetscriptGrafting(),
|
||||||
hacknet: NetscriptHacknet(),
|
hacknet: NetscriptHacknet(),
|
||||||
sprintf: () => sprintf,
|
sprintf:
|
||||||
vsprintf: () => vsprintf,
|
(ctx) =>
|
||||||
|
(_format, ...args) => {
|
||||||
|
const format = helpers.string(ctx, "format", _format);
|
||||||
|
return sprintf(format, ...(args as unknown[]));
|
||||||
|
},
|
||||||
|
vsprintf: (ctx) => (_format, _args) => {
|
||||||
|
const format = helpers.string(ctx, "format", _format);
|
||||||
|
if (!Array.isArray(_args)) {
|
||||||
|
throw helpers.errorMessage(ctx, `args must be an array.`);
|
||||||
|
}
|
||||||
|
return vsprintf(format, _args);
|
||||||
|
},
|
||||||
scan: (ctx) => (_hostname) => {
|
scan: (ctx) => (_hostname) => {
|
||||||
const hostname = _hostname ? helpers.string(ctx, "hostname", _hostname) : ctx.workerScript.hostname;
|
const hostname = _hostname ? helpers.string(ctx, "hostname", _hostname) : ctx.workerScript.hostname;
|
||||||
const server = helpers.getServer(ctx, hostname);
|
const server = helpers.getServer(ctx, hostname);
|
||||||
@ -1163,7 +1175,8 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
if (!path) return false;
|
if (!path) return false;
|
||||||
if (hasScriptExtension(path)) return server.scripts.has(path);
|
if (hasScriptExtension(path)) return server.scripts.has(path);
|
||||||
if (hasTextExtension(path)) return server.textFiles.has(path);
|
if (hasTextExtension(path)) return server.textFiles.has(path);
|
||||||
if (path.endsWith(".lit") || path.endsWith(".msg")) return server.messages.includes(path as any);
|
if (path.endsWith(".lit") || path.endsWith(".msg"))
|
||||||
|
return server.messages.includes(path as LiteratureName | MessageFilename);
|
||||||
if (hasContractExtension(path)) return !!server.contracts.find(({ fn }) => fn === path);
|
if (hasContractExtension(path)) return !!server.contracts.find(({ fn }) => fn === path);
|
||||||
const lowerPath = path.toLowerCase();
|
const lowerPath = path.toLowerCase();
|
||||||
return server.programs.map((programName) => programName.toLowerCase()).includes(lowerPath);
|
return server.programs.map((programName) => programName.toLowerCase()).includes(lowerPath);
|
||||||
|
@ -3,6 +3,7 @@ import { toNative } from "./toNative";
|
|||||||
import libarg from "arg";
|
import libarg from "arg";
|
||||||
import { NetscriptContext } from "../Netscript/APIWrapper";
|
import { NetscriptContext } from "../Netscript/APIWrapper";
|
||||||
|
|
||||||
|
export type Schema = [string, string | number | boolean | string[]][];
|
||||||
type FlagType = StringConstructor | NumberConstructor | BooleanConstructor | StringConstructor[];
|
type FlagType = StringConstructor | NumberConstructor | BooleanConstructor | StringConstructor[];
|
||||||
type FlagsRet = Record<string, ScriptArg | string[]>;
|
type FlagsRet = Record<string, ScriptArg | string[]>;
|
||||||
export function Flags(ctx: NetscriptContext | string[]): (data: unknown) => FlagsRet {
|
export function Flags(ctx: NetscriptContext | string[]): (data: unknown) => FlagsRet {
|
||||||
@ -12,7 +13,7 @@ export function Flags(ctx: NetscriptContext | string[]): (data: unknown) => Flag
|
|||||||
if (!Array.isArray(schema)) throw new Error("flags schema passed in is invalid.");
|
if (!Array.isArray(schema)) throw new Error("flags schema passed in is invalid.");
|
||||||
const args: Record<string, FlagType> = {};
|
const args: Record<string, FlagType> = {};
|
||||||
|
|
||||||
for (const d of schema) {
|
for (const d of schema as Schema) {
|
||||||
let t: FlagType = String;
|
let t: FlagType = String;
|
||||||
if (typeof d[1] === "number") {
|
if (typeof d[1] === "number") {
|
||||||
t = Number;
|
t = Number;
|
||||||
@ -24,13 +25,15 @@ export function Flags(ctx: NetscriptContext | string[]): (data: unknown) => Flag
|
|||||||
const numDashes = d[0].length > 1 ? 2 : 1;
|
const numDashes = d[0].length > 1 ? 2 : 1;
|
||||||
args["-".repeat(numDashes) + d[0]] = t;
|
args["-".repeat(numDashes) + d[0]] = t;
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||||
const ret: FlagsRet = libarg(args, { argv: vargs });
|
const ret: FlagsRet = libarg(args, { argv: vargs });
|
||||||
for (const d of schema) {
|
for (const d of schema as Schema) {
|
||||||
if (!Object.hasOwn(ret, "--" + d[0]) || !Object.hasOwn(ret, "-" + d[0])) ret[d[0]] = d[1];
|
if (!Object.hasOwn(ret, "--" + d[0]) || !Object.hasOwn(ret, "-" + d[0])) ret[d[0]] = d[1];
|
||||||
}
|
}
|
||||||
for (const key of Object.keys(ret)) {
|
for (const key of Object.keys(ret)) {
|
||||||
if (!key.startsWith("-")) continue;
|
if (!key.startsWith("-")) continue;
|
||||||
const value = ret[key];
|
const value = ret[key];
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete ret[key];
|
delete ret[key];
|
||||||
const numDashes = key.length === 2 ? 1 : 2;
|
const numDashes = key.length === 2 ? 1 : 2;
|
||||||
ret[key.slice(numDashes)] = value;
|
ret[key.slice(numDashes)] = value;
|
||||||
|
@ -82,14 +82,14 @@ export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
|
|||||||
setStyles: (ctx) => (newStyles) => {
|
setStyles: (ctx) => (newStyles) => {
|
||||||
const styleValidator: Record<string, string | number | undefined> = {};
|
const styleValidator: Record<string, string | number | undefined> = {};
|
||||||
assertObjectType(ctx, "newStyles", newStyles, styleValidator);
|
assertObjectType(ctx, "newStyles", newStyles, styleValidator);
|
||||||
const currentStyles = { ...Settings.styles };
|
const currentStyles: Record<string, unknown> = { ...Settings.styles };
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
for (const key of Object.keys(newStyles)) {
|
for (const key of Object.keys(newStyles)) {
|
||||||
if (!(currentStyles as any)[key]) {
|
if (!currentStyles[key]) {
|
||||||
// Invalid key
|
// Invalid key
|
||||||
errors.push(`Invalid key "${key}"`);
|
errors.push(`Invalid key "${key}"`);
|
||||||
} else {
|
} else {
|
||||||
(currentStyles as any)[key] = newStyles[key];
|
currentStyles[key] = newStyles[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +203,7 @@ export class PlayerObject extends Person implements IPlayer {
|
|||||||
// Remove any invalid jobs
|
// Remove any invalid jobs
|
||||||
for (const [loadedCompanyName, loadedJobName] of Object.entries(player.jobs)) {
|
for (const [loadedCompanyName, loadedJobName] of Object.entries(player.jobs)) {
|
||||||
if (!isMember("CompanyName", loadedCompanyName) || !isMember("JobName", loadedJobName)) {
|
if (!isMember("CompanyName", loadedCompanyName) || !isMember("JobName", loadedJobName)) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete player.jobs[loadedCompanyName as CompanyName];
|
delete player.jobs[loadedCompanyName as CompanyName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,6 +377,7 @@ export function quitJob(this: PlayerObject, company: CompanyName, suppressDialog
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete this.jobs[company];
|
delete this.jobs[company];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import { scaleWorkStats } from "../../../Work/WorkStats";
|
|||||||
import { getKeyList } from "../../../utils/helpers/getKeyList";
|
import { getKeyList } from "../../../utils/helpers/getKeyList";
|
||||||
import { loadActionIdentifier } from "../../../Bladeburner/utils/loadActionIdentifier";
|
import { loadActionIdentifier } from "../../../Bladeburner/utils/loadActionIdentifier";
|
||||||
import { invalidWork } from "../../../Work/InvalidWork";
|
import { invalidWork } from "../../../Work/InvalidWork";
|
||||||
|
import { objectAssert } from "../../../utils/helpers/typeAssertion";
|
||||||
|
|
||||||
interface SleeveBladeburnerWorkParams {
|
interface SleeveBladeburnerWorkParams {
|
||||||
actionId: ActionIdentifier & { type: BladeburnerActionType.General | BladeburnerActionType.Contract };
|
actionId: ActionIdentifier & { type: BladeburnerActionType.General | BladeburnerActionType.Contract };
|
||||||
@ -98,6 +99,7 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
|
|||||||
|
|
||||||
/** Initializes a BladeburnerWork object from a JSON save state. */
|
/** Initializes a BladeburnerWork object from a JSON save state. */
|
||||||
static fromJSON(value: IReviverValue): SleeveBladeburnerWork {
|
static fromJSON(value: IReviverValue): SleeveBladeburnerWork {
|
||||||
|
objectAssert(value.data);
|
||||||
const actionId = loadActionIdentifier(value.data?.actionId);
|
const actionId = loadActionIdentifier(value.data?.actionId);
|
||||||
if (!actionId) return invalidWork();
|
if (!actionId) return invalidWork();
|
||||||
value.data.actionId = actionId;
|
value.data.actionId = actionId;
|
||||||
|
@ -7,6 +7,7 @@ import { Sleeve } from "../Sleeve";
|
|||||||
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
|
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
|
||||||
import { Locations } from "../../../Locations/Locations";
|
import { Locations } from "../../../Locations/Locations";
|
||||||
import { isMember } from "../../../utils/EnumHelper";
|
import { isMember } from "../../../utils/EnumHelper";
|
||||||
|
import { objectAssert } from "../../../utils/helpers/typeAssertion";
|
||||||
|
|
||||||
export const isSleeveClassWork = (w: SleeveWorkClass | null): w is SleeveClassWork =>
|
export const isSleeveClassWork = (w: SleeveWorkClass | null): w is SleeveClassWork =>
|
||||||
w !== null && w.type === SleeveWorkType.CLASS;
|
w !== null && w.type === SleeveWorkType.CLASS;
|
||||||
@ -54,8 +55,13 @@ export class SleeveClassWork extends SleeveWorkClass {
|
|||||||
|
|
||||||
/** Initializes a ClassWork object from a JSON save state. */
|
/** Initializes a ClassWork object from a JSON save state. */
|
||||||
static fromJSON(value: IReviverValue): SleeveClassWork {
|
static fromJSON(value: IReviverValue): SleeveClassWork {
|
||||||
if (!(value.data.classType in Classes)) value.data.classType = "Computer Science";
|
objectAssert(value.data);
|
||||||
if (!(value.data.location in Locations)) value.data.location = LocationName.Sector12RothmanUniversity;
|
if (typeof value.data.classType !== "string" || !(value.data.classType in Classes)) {
|
||||||
|
value.data.classType = "Computer Science";
|
||||||
|
}
|
||||||
|
if (typeof value.data.location !== "string" || !(value.data.location in Locations)) {
|
||||||
|
value.data.location = LocationName.Sector12RothmanUniversity;
|
||||||
|
}
|
||||||
return Generic_fromJSON(SleeveClassWork, value.data);
|
return Generic_fromJSON(SleeveClassWork, value.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,12 @@ class BitburnerSaveObject {
|
|||||||
|
|
||||||
let parsedSaveData;
|
let parsedSaveData;
|
||||||
try {
|
try {
|
||||||
parsedSaveData = JSON.parse(decodedSaveData);
|
parsedSaveData = JSON.parse(decodedSaveData) as {
|
||||||
|
ctor: string;
|
||||||
|
data: {
|
||||||
|
PlayerSave: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error); // We'll handle below
|
console.error(error); // We'll handle below
|
||||||
}
|
}
|
||||||
@ -632,6 +637,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
|||||||
if (isNaN(intExp)) intExp = 0;
|
if (isNaN(intExp)) intExp = 0;
|
||||||
anyPlayer.exp.intelligence += intExp;
|
anyPlayer.exp.intelligence += intExp;
|
||||||
for (const field of removePlayerFields) {
|
for (const field of removePlayerFields) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete anyPlayer[field];
|
delete anyPlayer[field];
|
||||||
}
|
}
|
||||||
for (const sleeve of anyPlayer.sleeves) {
|
for (const sleeve of anyPlayer.sleeves) {
|
||||||
@ -640,6 +646,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
|||||||
if (isNaN(intExp)) intExp = 0;
|
if (isNaN(intExp)) intExp = 0;
|
||||||
anySleeve.exp.intelligence += intExp;
|
anySleeve.exp.intelligence += intExp;
|
||||||
for (const field of removeSleeveFields) {
|
for (const field of removeSleeveFields) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete sleeve[field];
|
delete sleeve[field];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,10 @@ export const sanitizeTheme = (theme: IScriptEditorTheme): void => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const themeKey of getRecordKeys(theme)) {
|
for (const themeKey of getRecordKeys(theme)) {
|
||||||
if (typeof theme[themeKey] !== "object") delete theme[themeKey];
|
if (typeof theme[themeKey] !== "object") {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
|
delete theme[themeKey];
|
||||||
|
}
|
||||||
switch (themeKey) {
|
switch (themeKey) {
|
||||||
case "base":
|
case "base":
|
||||||
if (!["vs-dark", "vs"].includes(theme.base)) theme.base = "vs-dark";
|
if (!["vs-dark", "vs"].includes(theme.base)) theme.base = "vs-dark";
|
||||||
|
@ -13,6 +13,7 @@ import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
|
|||||||
import { IPAddress, isIPAddress } from "../Types/strings";
|
import { IPAddress, isIPAddress } from "../Types/strings";
|
||||||
|
|
||||||
import "../Script/RunningScript"; // For reviver side-effect
|
import "../Script/RunningScript"; // For reviver side-effect
|
||||||
|
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of all Servers that exist in the game
|
* Map of all Servers that exist in the game
|
||||||
@ -63,6 +64,7 @@ export function DeleteServer(serverkey: string): void {
|
|||||||
for (const key of Object.keys(AllServers)) {
|
for (const key of Object.keys(AllServers)) {
|
||||||
const server = AllServers[key];
|
const server = AllServers[key];
|
||||||
if (server.ip !== serverkey && server.hostname !== serverkey) continue;
|
if (server.ip !== serverkey && server.hostname !== serverkey) continue;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete AllServers[key];
|
delete AllServers[key];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -100,6 +102,7 @@ export function AddToAllServers(server: Server | HacknetServer): void {
|
|||||||
|
|
||||||
export const renameServer = (hostname: string, newName: string): void => {
|
export const renameServer = (hostname: string, newName: string): void => {
|
||||||
AllServers[newName] = AllServers[hostname];
|
AllServers[newName] = AllServers[hostname];
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete AllServers[hostname];
|
delete AllServers[hostname];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -188,13 +191,22 @@ export function initForeignServers(homeComputer: Server): void {
|
|||||||
|
|
||||||
export function prestigeAllServers(): void {
|
export function prestigeAllServers(): void {
|
||||||
for (const member of Object.keys(AllServers)) {
|
for (const member of Object.keys(AllServers)) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete AllServers[member];
|
delete AllServers[member];
|
||||||
}
|
}
|
||||||
AllServers = {};
|
AllServers = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadAllServers(saveString: string): void {
|
export function loadAllServers(saveString: string): void {
|
||||||
AllServers = JSON.parse(saveString, Reviver);
|
const allServersData: unknown = JSON.parse(saveString, Reviver);
|
||||||
|
objectAssert(allServersData);
|
||||||
|
for (const [serverName, server] of Object.entries(allServersData)) {
|
||||||
|
if (!(server instanceof Server) && !(server instanceof HacknetServer)) {
|
||||||
|
throw new Error(`Server ${serverName} is not an instance of Server or HacknetServer.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We validated the data above, so it's safe to typecast here.
|
||||||
|
AllServers = allServersData as typeof AllServers;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveAllServers(): string {
|
export function saveAllServers(): string {
|
||||||
|
@ -24,6 +24,7 @@ import lodash from "lodash";
|
|||||||
import { Settings } from "../Settings/Settings";
|
import { Settings } from "../Settings/Settings";
|
||||||
|
|
||||||
import type { ScriptKey } from "../utils/helpers/scriptKey";
|
import type { ScriptKey } from "../utils/helpers/scriptKey";
|
||||||
|
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||||
|
|
||||||
interface IConstructorParams {
|
interface IConstructorParams {
|
||||||
adminRights?: boolean;
|
adminRights?: boolean;
|
||||||
@ -293,6 +294,7 @@ export abstract class BaseServer implements IServer {
|
|||||||
// RunningScripts are stored as a simple array, both for backward compatibility,
|
// RunningScripts are stored as a simple array, both for backward compatibility,
|
||||||
// compactness, and ease of filtering them here.
|
// compactness, and ease of filtering them here.
|
||||||
const result = Generic_toJSON(ctorName, this, keys);
|
const result = Generic_toJSON(ctorName, this, keys);
|
||||||
|
objectAssert(result.data);
|
||||||
if (Settings.ExcludeRunningScriptsFromSave) {
|
if (Settings.ExcludeRunningScriptsFromSave) {
|
||||||
result.data.runningScripts = [];
|
result.data.runningScripts = [];
|
||||||
return result;
|
return result;
|
||||||
@ -313,8 +315,11 @@ export abstract class BaseServer implements IServer {
|
|||||||
// Initializes a Server Object from a JSON save state
|
// Initializes a Server Object from a JSON save state
|
||||||
// Called by subclasses, not Reviver.
|
// Called by subclasses, not Reviver.
|
||||||
static fromJSONBase<T extends BaseServer>(value: IReviverValue, ctor: new () => T, keys: readonly (keyof T)[]): T {
|
static fromJSONBase<T extends BaseServer>(value: IReviverValue, ctor: new () => T, keys: readonly (keyof T)[]): T {
|
||||||
|
objectAssert(value.data);
|
||||||
const server = Generic_fromJSON(ctor, value.data, keys);
|
const server = Generic_fromJSON(ctor, value.data, keys);
|
||||||
|
if (value.data.runningScripts != null && Array.isArray(value.data.runningScripts)) {
|
||||||
server.savedScripts = value.data.runningScripts;
|
server.savedScripts = value.data.runningScripts;
|
||||||
|
}
|
||||||
// If textFiles is not an array, we've already done the 2.3 migration to textFiles and scripts as maps + path changes.
|
// If textFiles is not an array, we've already done the 2.3 migration to textFiles and scripts as maps + path changes.
|
||||||
if (!Array.isArray(server.textFiles)) return server;
|
if (!Array.isArray(server.textFiles)) return server;
|
||||||
|
|
||||||
@ -333,7 +338,7 @@ export abstract class BaseServer implements IServer {
|
|||||||
// In case somehow there are previously valid filenames that can't be sanitized, they will go in a new directory with a note.
|
// In case somehow there are previously valid filenames that can't be sanitized, they will go in a new directory with a note.
|
||||||
for (const script of oldScripts) {
|
for (const script of oldScripts) {
|
||||||
// We're about to do type validation on the filename anyway.
|
// We're about to do type validation on the filename anyway.
|
||||||
if (script.filename.endsWith(".ns")) script.filename = (script.filename + ".js") as any;
|
if (script.filename.endsWith(".ns")) script.filename = (script.filename + ".js") as ScriptFilePath;
|
||||||
let newFilePath = resolveScriptFilePath(script.filename);
|
let newFilePath = resolveScriptFilePath(script.filename);
|
||||||
if (!newFilePath) {
|
if (!newFilePath) {
|
||||||
newFilePath = `${newDirectory}script${++invalidScriptCount}.js` as ScriptFilePath;
|
newFilePath = `${newDirectory}script${++invalidScriptCount}.js` as ScriptFilePath;
|
||||||
|
@ -151,6 +151,7 @@ export function deleteStockMarket(): void {
|
|||||||
|
|
||||||
export function initStockMarket(): void {
|
export function initStockMarket(): void {
|
||||||
for (const stockName of Object.getOwnPropertyNames(StockMarket)) {
|
for (const stockName of Object.getOwnPropertyNames(StockMarket)) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete StockMarket[stockName];
|
delete StockMarket[stockName];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export function exportScripts(pattern: string, server: BaseServer, currDir = roo
|
|||||||
// Return an error if no files matched, rather than an empty zip folder
|
// Return an error if no files matched, rather than an empty zip folder
|
||||||
if (Object.keys(zip.files).length == 0) throw new Error(`No files match the pattern ${pattern}`);
|
if (Object.keys(zip.files).length == 0) throw new Error(`No files match the pattern ${pattern}`);
|
||||||
const filename = `bitburner${
|
const filename = `bitburner${
|
||||||
hasScriptExtension(pattern) ? "Scripts" : pattern.endsWith(".txt") ? "Texts" : "Files"
|
hasScriptExtension(pattern) ? "Scripts" : hasTextExtension(pattern) ? "Texts" : "Files"
|
||||||
}.zip`;
|
}.zip`;
|
||||||
zip
|
zip
|
||||||
.generateAsync({ type: "blob" })
|
.generateAsync({ type: "blob" })
|
||||||
|
@ -33,6 +33,7 @@ export function ls(args: (string | number | boolean)[], server: BaseServer): voi
|
|||||||
}
|
}
|
||||||
let flags: LSFlags;
|
let flags: LSFlags;
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||||
flags = libarg(
|
flags = libarg(
|
||||||
{
|
{
|
||||||
"-l": Boolean,
|
"-l": Boolean,
|
||||||
|
@ -4,8 +4,11 @@ import { matchScriptPathUnanchored } from "../../utils/helpers/scriptKey";
|
|||||||
import libarg from "arg";
|
import libarg from "arg";
|
||||||
|
|
||||||
export function ps(args: (string | number | boolean)[], server: BaseServer): void {
|
export function ps(args: (string | number | boolean)[], server: BaseServer): void {
|
||||||
let flags;
|
let flags: {
|
||||||
|
"--grep": string;
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||||
flags = libarg(
|
flags = libarg(
|
||||||
{
|
{
|
||||||
"--grep": String,
|
"--grep": String,
|
||||||
|
@ -18,8 +18,14 @@ export function runScript(path: ScriptFilePath, commandArgs: (string | number |
|
|||||||
if (!script) return Terminal.error(`Script ${path} does not exist on this server.`);
|
if (!script) return Terminal.error(`Script ${path} does not exist on this server.`);
|
||||||
|
|
||||||
const runArgs = { "--tail": Boolean, "-t": Number, "--ram-override": Number };
|
const runArgs = { "--tail": Boolean, "-t": Number, "--ram-override": Number };
|
||||||
let flags;
|
let flags: {
|
||||||
|
_: ScriptArg[];
|
||||||
|
"--tail": boolean;
|
||||||
|
"-t": string;
|
||||||
|
"--ram-override": string;
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||||
flags = libarg(runArgs, {
|
flags = libarg(runArgs, {
|
||||||
permissive: true,
|
permissive: true,
|
||||||
argv: commandArgs,
|
argv: commandArgs,
|
||||||
@ -42,7 +48,7 @@ export function runScript(path: ScriptFilePath, commandArgs: (string | number |
|
|||||||
if (!server.hasAdminRights) return Terminal.error("Need root access to run script");
|
if (!server.hasAdminRights) return Terminal.error("Need root access to run script");
|
||||||
|
|
||||||
// Todo: Switch out arg for something with typescript support
|
// Todo: Switch out arg for something with typescript support
|
||||||
const args = flags._ as ScriptArg[];
|
const args = flags._;
|
||||||
|
|
||||||
const singleRamUsage = ramOverride ?? script.getRamUsage(server.scripts);
|
const singleRamUsage = ramOverride ?? script.getRamUsage(server.scripts);
|
||||||
if (!singleRamUsage) {
|
if (!singleRamUsage) {
|
||||||
|
@ -23,7 +23,10 @@ export function tail(commandArray: (string | number | boolean)[], server: BaseSe
|
|||||||
}
|
}
|
||||||
// Just use the first one (if there are multiple with the same
|
// Just use the first one (if there are multiple with the same
|
||||||
// arguments, they can't be distinguished except by pid).
|
// arguments, they can't be distinguished except by pid).
|
||||||
LogBoxEvents.emit(candidates.values().next().value);
|
const next = candidates.values().next();
|
||||||
|
if (!next.done) {
|
||||||
|
LogBoxEvents.emit(next.value);
|
||||||
|
}
|
||||||
} else if (typeof commandArray[0] === "number") {
|
} else if (typeof commandArray[0] === "number") {
|
||||||
const runningScript = findRunningScriptByPid(commandArray[0]);
|
const runningScript = findRunningScriptByPid(commandArray[0]);
|
||||||
if (runningScript == null) {
|
if (runningScript == null) {
|
||||||
|
@ -5,7 +5,7 @@ import { GetAllServers } from "../Server/AllServers";
|
|||||||
import { parseCommand, parseCommands } from "./Parser";
|
import { parseCommand, parseCommands } from "./Parser";
|
||||||
import { HelpTexts } from "./HelpText";
|
import { HelpTexts } from "./HelpText";
|
||||||
import { compile } from "../NetscriptJSEvaluator";
|
import { compile } from "../NetscriptJSEvaluator";
|
||||||
import { Flags } from "../NetscriptFunctions/Flags";
|
import { Flags, type Schema } from "../NetscriptFunctions/Flags";
|
||||||
import { AutocompleteData } from "@nsdefs";
|
import { AutocompleteData } from "@nsdefs";
|
||||||
import libarg from "arg";
|
import libarg from "arg";
|
||||||
import { getAllDirectories, resolveDirectory, root } from "../Paths/Directory";
|
import { getAllDirectories, resolveDirectory, root } from "../Paths/Directory";
|
||||||
@ -280,6 +280,7 @@ export async function getTabCompletionPossibilities(terminalText: string, baseDi
|
|||||||
_: [],
|
_: [],
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||||
flags = libarg(runArgs, {
|
flags = libarg(runArgs, {
|
||||||
permissive: true,
|
permissive: true,
|
||||||
argv: command.slice(2),
|
argv: command.slice(2),
|
||||||
@ -299,9 +300,9 @@ export async function getTabCompletionPossibilities(terminalText: string, baseDi
|
|||||||
scripts: [...currServ.scripts.keys()],
|
scripts: [...currServ.scripts.keys()],
|
||||||
txts: [...currServ.textFiles.keys()],
|
txts: [...currServ.textFiles.keys()],
|
||||||
enums: enums,
|
enums: enums,
|
||||||
flags: (schema: unknown) => {
|
flags: (schema: Schema) => {
|
||||||
if (!Array.isArray(schema)) throw new Error("flags require an array of array");
|
if (!Array.isArray(schema)) throw new Error("flags require an array of array");
|
||||||
pos2 = schema.map((f: unknown) => {
|
pos2 = schema.map((f) => {
|
||||||
if (!Array.isArray(f)) throw new Error("flags require an array of array");
|
if (!Array.isArray(f)) throw new Error("flags require an array of array");
|
||||||
if (f[0].length === 1) return "-" + f[0];
|
if (f[0].length === 1) return "-" + f[0];
|
||||||
return "--" + f[0];
|
return "--" + f[0];
|
||||||
|
4
src/ThirdParty/acorn-jsx-walk.d.ts
vendored
4
src/ThirdParty/acorn-jsx-walk.d.ts
vendored
@ -1 +1,3 @@
|
|||||||
declare module "acorn-jsx-walk";
|
declare module "acorn-jsx-walk" {
|
||||||
|
export function extend(base: any): void;
|
||||||
|
}
|
||||||
|
1
src/ThirdParty/sprintf-js.d.ts
vendored
1
src/ThirdParty/sprintf-js.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
declare module "sprintf-js";
|
|
@ -1,3 +1,4 @@
|
|||||||
|
import { arrayAssert } from "../utils/helpers/typeAssertion";
|
||||||
import type { IReviverValue } from "../utils/JSONReviver";
|
import type { IReviverValue } from "../utils/JSONReviver";
|
||||||
// Versions of js builtin classes that can be converted to and from JSON for use in save files
|
// Versions of js builtin classes that can be converted to and from JSON for use in save files
|
||||||
|
|
||||||
@ -6,6 +7,7 @@ export class JSONSet<T> extends Set<T> {
|
|||||||
return { ctor: "JSONSet", data: Array.from(this) };
|
return { ctor: "JSONSet", data: Array.from(this) };
|
||||||
}
|
}
|
||||||
static fromJSON(value: IReviverValue): JSONSet<any> {
|
static fromJSON(value: IReviverValue): JSONSet<any> {
|
||||||
|
arrayAssert(value.data);
|
||||||
return new JSONSet(value.data);
|
return new JSONSet(value.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16,6 +18,15 @@ export class JSONMap<K, __V> extends Map<K, __V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON(value: IReviverValue): JSONMap<any, any> {
|
static fromJSON(value: IReviverValue): JSONMap<any, any> {
|
||||||
return new JSONMap(value.data);
|
arrayAssert(value.data);
|
||||||
|
for (const item of value.data) {
|
||||||
|
arrayAssert(item);
|
||||||
|
if (item.length !== 2) {
|
||||||
|
console.error("Invalid data passed to JSONMap.fromJSON(). Value:", value);
|
||||||
|
throw new Error(`An item is not an array with exactly 2 items. Its length is ${item.length}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We validated the data above, so it's safe to typecast here.
|
||||||
|
return new JSONMap(value.data as [unknown, unknown][]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
import { ObjectValidator, validateObject } from "./Validator";
|
import { ObjectValidator, validateObject } from "./Validator";
|
||||||
import { JSONMap, JSONSet } from "../Types/Jsonable";
|
import { JSONMap, JSONSet } from "../Types/Jsonable";
|
||||||
import { loadActionIdentifier } from "../Bladeburner/utils/loadActionIdentifier";
|
import { loadActionIdentifier } from "../Bladeburner/utils/loadActionIdentifier";
|
||||||
|
import { objectAssert } from "./helpers/typeAssertion";
|
||||||
|
|
||||||
type JsonableClass = (new () => { toJSON: () => IReviverValue }) & {
|
type JsonableClass = (new () => { toJSON: () => IReviverValue }) & {
|
||||||
fromJSON: (value: IReviverValue) => any;
|
fromJSON: (value: IReviverValue) => unknown;
|
||||||
validationData?: ObjectValidator<any>;
|
validationData?: ObjectValidator<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IReviverValue<T = any> {
|
export interface IReviverValue<T = unknown> {
|
||||||
ctor: string;
|
ctor: string;
|
||||||
data: T;
|
data: T;
|
||||||
}
|
}
|
||||||
@ -87,16 +88,23 @@ export function Generic_toJSON<T extends Record<string, any>>(
|
|||||||
* @returns The object */
|
* @returns The object */
|
||||||
export function Generic_fromJSON<T extends Record<string, any>>(
|
export function Generic_fromJSON<T extends Record<string, any>>(
|
||||||
ctor: new () => T,
|
ctor: new () => T,
|
||||||
// data can actually be anything. We're just pretending it has the right keys for T. Save data is not type validated.
|
data: unknown,
|
||||||
data: Record<keyof T, any>,
|
|
||||||
keys?: readonly (keyof T)[],
|
keys?: readonly (keyof T)[],
|
||||||
): T {
|
): T {
|
||||||
|
objectAssert(data);
|
||||||
const obj = new ctor();
|
const obj = new ctor();
|
||||||
// If keys were provided, just load the provided keys (if they are in the data)
|
// If keys were provided, just load the provided keys (if they are in the data)
|
||||||
if (keys) {
|
if (keys) {
|
||||||
for (const key of keys) {
|
/**
|
||||||
|
* The type of key is "keyof T", but the type of data is Record<string, unknown>. TypeScript won't allow us to use
|
||||||
|
* key as the index of data, so we need to typecast here.
|
||||||
|
*/
|
||||||
|
for (const key of keys as string[]) {
|
||||||
const val = data[key];
|
const val = data[key];
|
||||||
if (val !== undefined) obj[key] = val;
|
if (val !== undefined) {
|
||||||
|
// @ts-expect-error -- TypeScript won't allow this action: Type 'T' is generic and can only be indexed for reading.
|
||||||
|
obj[key] = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
// Various functions for asserting types.
|
// Various functions for asserting types.
|
||||||
|
|
||||||
|
export class TypeAssertionError extends Error {
|
||||||
|
friendlyType: string;
|
||||||
|
|
||||||
|
constructor(message: string, friendlyType: string, options?: ErrorOptions) {
|
||||||
|
super(message, options);
|
||||||
|
this.name = this.constructor.name;
|
||||||
|
this.friendlyType = friendlyType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Function for providing custom error message to throw for a type assertion.
|
/** Function for providing custom error message to throw for a type assertion.
|
||||||
* @param v: Value to assert type of
|
* @param v: Value to assert type of
|
||||||
* @param assertFn: Typechecking function to use for asserting type of v.
|
* @param assertFn: Typechecking function to use for asserting type of v.
|
||||||
@ -12,31 +22,47 @@ export function assert<T>(
|
|||||||
try {
|
try {
|
||||||
assertFn(v);
|
assertFn(v);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (e instanceof TypeAssertionError) {
|
||||||
|
throw msgFn(e.friendlyType);
|
||||||
|
}
|
||||||
const type = typeof e === "string" ? e : "unknown";
|
const type = typeof e === "string" ? e : "unknown";
|
||||||
throw msgFn(type);
|
throw msgFn(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the friendlyType of v. arrays are "array" and null is "null". */
|
/** Returns the friendlyType of v. arrays are "array" and null is "null". */
|
||||||
export function getFriendlyType(v: unknown): string {
|
function getFriendlyType(v: unknown): string {
|
||||||
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
||||||
}
|
}
|
||||||
|
|
||||||
//All assertion functions used here should return the friendlyType of the input.
|
//All assertion functions used here should return the friendlyType of the input.
|
||||||
|
|
||||||
/** For non-objects, and for array/null, throws the friendlyType of v. */
|
/** For non-objects, and for array/null, throws an error with the friendlyType of v. */
|
||||||
export function objectAssert(v: unknown): asserts v is Partial<Record<string, unknown>> {
|
export function objectAssert(v: unknown): asserts v is Record<string, unknown> {
|
||||||
const type = getFriendlyType(v);
|
const type = getFriendlyType(v);
|
||||||
if (type !== "object") throw type;
|
if (type !== "object") {
|
||||||
|
console.error("The value is not an object. Value:", v);
|
||||||
|
throw new TypeAssertionError(
|
||||||
|
`The value is not an object. Its type is ${type}. Its string value is ${String(v)}.`,
|
||||||
|
type,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For non-string, throws the friendlyType of v. */
|
/** For non-string, throws an error with the friendlyType of v. */
|
||||||
export function stringAssert(v: unknown): asserts v is string {
|
export function stringAssert(v: unknown): asserts v is string {
|
||||||
const type = getFriendlyType(v);
|
const type = getFriendlyType(v);
|
||||||
if (type !== "string") throw type;
|
if (type !== "string") {
|
||||||
|
console.error("The value is not a string. Value:", v);
|
||||||
|
throw new TypeAssertionError(`The value is not an string. Its type is ${type}.`, type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For non-array, throws the friendlyType of v. */
|
/** For non-array, throws an error with the friendlyType of v. */
|
||||||
export function arrayAssert(v: unknown): asserts v is unknown[] {
|
export function arrayAssert(v: unknown): asserts v is unknown[] {
|
||||||
if (!Array.isArray(v)) throw getFriendlyType(v);
|
if (!Array.isArray(v)) {
|
||||||
|
console.error("The value is not an array. Value:", v);
|
||||||
|
const type = getFriendlyType(v);
|
||||||
|
throw new TypeAssertionError(`The value is not an array. Its type is ${type}.`, type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user