Merge pull request #1491 from danielyxie/dev

Added atExit
This commit is contained in:
hydroflame 2021-10-14 20:13:43 -04:00 committed by GitHub
commit 1c92e0cffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2502 additions and 14236 deletions

22
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -0,0 +1,18 @@
atExit() Netscript Function
============================
.. js:function:: atExit(f)
:RAM cost: 0 GB
:param function f: function to call when the script dies.
Runs when the script dies.
Example:
.. code-block:: javascript
function onDeath() {
console.log('I died!!!')
}
atExit(onDeath);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14356
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -14,7 +14,6 @@
"@emotion/styled": "^11.3.0", "@emotion/styled": "^11.3.0",
"@monaco-editor/react": "^4.2.2", "@monaco-editor/react": "^4.2.2",
"@mui/icons-material": "^5.0.3", "@mui/icons-material": "^5.0.3",
"@mui/lab": "^5.0.0-alpha.46",
"@mui/material": "^5.0.3", "@mui/material": "^5.0.3",
"@mui/styles": "^5.0.1", "@mui/styles": "^5.0.1",
"@types/escodegen": "^0.0.7", "@types/escodegen": "^0.0.7",
@ -25,47 +24,23 @@
"@types/react-resizable": "^1.7.3", "@types/react-resizable": "^1.7.3",
"acorn": "^8.4.1", "acorn": "^8.4.1",
"acorn-walk": "^8.1.1", "acorn-walk": "^8.1.1",
"ajv": "^5.1.5",
"ajv-keywords": "^2.0.0",
"arg": "^5.0.0", "arg": "^5.0.0",
"async": "^2.6.1",
"autosize": "^4.0.2",
"better-react-mathjax": "^1.0.3", "better-react-mathjax": "^1.0.3",
"brace": "^0.11.1", "clsx": "^1.1.1",
"codemirror": "^5.58.2",
"decimal.js": "7.2.3", "decimal.js": "7.2.3",
"enhanced-resolve": "^4.0.0",
"escodegen": "^1.11.0", "escodegen": "^1.11.0",
"escope": "^3.6.0",
"file-saver": "^1.3.8", "file-saver": "^1.3.8",
"interpret": "^1.0.0",
"jquery": "^3.5.0", "jquery": "^3.5.0",
"jshint": "^2.10.2",
"json-loader": "^0.5.4",
"jsplumb": "^2.6.8",
"jszip": "^3.7.0", "jszip": "^3.7.0",
"loader-runner": "^2.3.0",
"loader-utils": "^1.1.0",
"material-ui-color": "^1.2.0", "material-ui-color": "^1.2.0",
"mathjax-full": "^3.2.0",
"memory-fs": "~0.4.1",
"monaco-editor": "^0.27.0", "monaco-editor": "^0.27.0",
"node-sass": "^6.0.1",
"normalize.css": "^8.0.0",
"notistack": "^2.0.2", "notistack": "^2.0.2",
"numeral": "2.0.6", "numeral": "2.0.6",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-draggable": "^4.4.4", "react-draggable": "^4.4.4",
"react-modal": "^3.12.1",
"react-resizable": "^3.0.4", "react-resizable": "^3.0.4",
"sprintf-js": "^1.1.1", "sprintf-js": "^1.1.1"
"tapable": "^1.0.0",
"treant-js": "^1.0.1",
"unused-webpack-plugin": "^2.4.0",
"uuid": "^3.2.1",
"w3c-blob": "0.0.1",
"webpack-deadcode-plugin": "^0.1.15"
}, },
"description": "A cyberpunk-themed incremental game", "description": "A cyberpunk-themed incremental game",
"devDependencies": { "devDependencies": {
@ -76,64 +51,33 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
"@testing-library/cypress": "^8.0.1", "@testing-library/cypress": "^8.0.1",
"@types/file-saver": "^2.0.3", "@types/file-saver": "^2.0.3",
"@types/jest": "^27.0.1",
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node": "^16.9.1",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0", "@typescript-eslint/parser": "^4.22.0",
"babel-jest": "^27.0.6", "babel-jest": "^27.0.6",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.5",
"beautify-lint": "^1.0.3",
"benchmark": "^2.1.1",
"bundle-loader": "~0.5.0",
"css-loader": "^0.28.11",
"cypress": "^8.3.1", "cypress": "^8.3.1",
"electron": "^14.0.1", "electron": "^14.0.1",
"electron-packager": "^15.4.0", "electron-packager": "^15.4.0",
"es6-promise-polyfill": "^1.1.1",
"eslint": "^7.24.0", "eslint": "^7.24.0",
"eslint-plugin-node": "^11.1.0",
"file-loader": "^1.1.11",
"fork-ts-checker-webpack-plugin": "^6.3.3", "fork-ts-checker-webpack-plugin": "^6.3.3",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"http-server": "^13.0.1", "http-server": "^13.0.1",
"i18n-webpack-plugin": "^1.0.0",
"istanbul": "^0.4.5",
"jest": "^27.1.0", "jest": "^27.1.0",
"js-beautify": "^1.5.10", "js-beautify": "^1.5.10",
"jsdom": "^15.0.0", "jsdom": "^15.0.0",
"jsdom-global": "^3.0.2",
"json5": "^1.0.1",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mini-css-extract-plugin": "^0.4.1", "mini-css-extract-plugin": "^0.4.1",
"mkdirp": "^0.5.1",
"null-loader": "^1.0.0",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"raw-loader": "~0.5.0",
"react-refresh": "^0.10.0", "react-refresh": "^0.10.0",
"regenerator-runtime": "^0.13.9", "regenerator-runtime": "^0.13.9",
"sass-loader": "^7.0.3",
"script-loader": "~0.7.0",
"should": "^11.1.1",
"simple-git": "^1.96.0",
"sinon": "^2.3.2",
"source-map": "^0.7.3", "source-map": "^0.7.3",
"start-server-and-test": "^1.14.0", "start-server-and-test": "^1.14.0",
"style-loader": "^0.21.0",
"stylelint": "^9.2.1",
"stylelint-order": "^0.8.1",
"typescript": "^4.2.4", "typescript": "^4.2.4",
"uglify-es": "^3.3.9",
"uglifyjs-webpack-plugin": "^1.3.0",
"url-loader": "^1.0.1",
"watchpack": "^1.6.0",
"webpack": "^4.46.0", "webpack": "^4.46.0",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-middleware": "^3.7.3", "webpack-dev-middleware": "^3.7.3",
"webpack-dev-server": "^3.11.2", "webpack-dev-server": "^3.11.2"
"worker-loader": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">=8 || <=9" "node": ">=8 || <=9"
@ -157,7 +101,6 @@
"build:dev": "webpack --mode development", "build:dev": "webpack --mode development",
"lint": "npm run lint:jsts & npm run lint:style", "lint": "npm run lint:jsts & npm run lint:style",
"lint:jsts": "eslint --fix . --ext js,jsx,ts,tsx", "lint:jsts": "eslint --fix . --ext js,jsx,ts,tsx",
"lint:style": "stylelint --fix ./css/*",
"preinstall": "node ./scripts/engines-check.js", "preinstall": "node ./scripts/engines-check.js",
"test": "jest", "test": "jest",
"test:watch": "jest --watch", "test:watch": "jest --watch",

@ -2,25 +2,25 @@ const numSpaces = 4;
const maxLineLength = 160; const maxLineLength = 160;
module.exports = { module.exports = {
env: { "env": {
es6: true, "es6": true,
node: true, "node": true
}, },
extends: "eslint:recommended", "extends": "eslint:recommended",
parserOptions: { "parserOptions": {
ecmaFeatures: { "ecmaFeatures": {
experimentalObjectRestSpread: true, "experimentalObjectRestSpread": true
}, },
ecmaVersion: 8, "ecmaVersion": 8,
sourceType: "module", "sourceType": "module"
}, },
rules: { "rules": {
"accessor-pairs": [ "accessor-pairs": [
"error", "error",
{ {
getWithoutSet: false, "getWithoutSet": false,
setWithoutGet: true, "setWithoutGet": true
}, }
], ],
"array-bracket-newline": ["error"], "array-bracket-newline": ["error"],
"array-bracket-spacing": ["error"], "array-bracket-spacing": ["error"],
@ -33,35 +33,50 @@ module.exports = {
"block-spacing": ["error"], "block-spacing": ["error"],
"brace-style": ["error"], "brace-style": ["error"],
"callback-return": ["error"], "callback-return": ["error"],
camelcase: ["error"], "camelcase": ["error"],
"capitalized-comments": ["error"], "capitalized-comments": ["error"],
"class-methods-use-this": ["error"], "class-methods-use-this": ["error"],
"comma-dangle": ["error"], "comma-dangle": ["error"],
"comma-spacing": ["error"], "comma-spacing": ["error"],
"comma-style": ["error", "last"], "comma-style": [
complexity: ["error"], "error",
"computed-property-spacing": ["error", "never"], "last"
],
"complexity": ["error"],
"computed-property-spacing": [
"error",
"never"
],
"consistent-return": ["error"], "consistent-return": ["error"],
"consistent-this": ["error"], "consistent-this": ["error"],
"constructor-super": ["error"], "constructor-super": ["error"],
curly: ["error"], "curly": ["error"],
"default-case": ["error"], "default-case": ["error"],
"dot-location": ["error", "property"], "dot-location": [
"error",
"property"
],
"dot-notation": ["error"], "dot-notation": ["error"],
"eol-last": ["error"], "eol-last": ["error"],
eqeqeq: ["error"], "eqeqeq": ["error"],
"for-direction": ["error"], "for-direction": ["error"],
"func-call-spacing": ["error"], "func-call-spacing": ["error"],
"func-name-matching": ["error"], "func-name-matching": ["error"],
"func-names": ["error", "never"], "func-names": [
"error",
"never"
],
"func-style": ["error"], "func-style": ["error"],
"function-paren-newline": ["error"], "function-paren-newline": ["error"],
"generator-star-spacing": ["error", "before"], "generator-star-spacing": [
"error",
"before"
],
"getter-return": [ "getter-return": [
"error", "error",
{ {
allowImplicit: false, "allowImplicit": false
}, }
], ],
"global-require": ["error"], "global-require": ["error"],
"guard-for-in": ["error"], "guard-for-in": ["error"],
@ -69,37 +84,52 @@ module.exports = {
"id-blacklist": ["error"], "id-blacklist": ["error"],
"id-length": ["error"], "id-length": ["error"],
"id-match": ["error"], "id-match": ["error"],
"implicit-arrow-linebreak": ["error", "beside"], "implicit-arrow-linebreak": [
indent: [ "error",
"beside"
],
"indent": [
"error", "error",
numSpaces, numSpaces,
{ {
SwitchCase: 1, "SwitchCase": 1
}, }
], ],
"init-declarations": ["error"], "init-declarations": ["error"],
"jsx-quotes": ["error"], "jsx-quotes": ["error"],
"key-spacing": ["error"], "key-spacing": ["error"],
"keyword-spacing": ["error"], "keyword-spacing": ["error"],
"line-comment-position": ["error"], "line-comment-position": ["error"],
"linebreak-style": ["error", "windows"], "linebreak-style": [
"error",
"windows"
],
"lines-around-comment": ["error"], "lines-around-comment": ["error"],
"lines-between-class-members": ["error"], "lines-between-class-members": ["error"],
"max-depth": ["error"], "max-depth": ["error"],
"max-len": ["error", maxLineLength], "max-len": [
"error",
maxLineLength
],
"max-lines": [ "max-lines": [
"error", "error",
{ {
skipBlankLines: true, "skipBlankLines": true,
skipComments: true, "skipComments": true
}, }
], ],
"max-nested-callbacks": ["error"], "max-nested-callbacks": ["error"],
"max-params": ["error"], "max-params": ["error"],
"max-statements": ["error"], "max-statements": ["error"],
"max-statements-per-line": ["error"], "max-statements-per-line": ["error"],
"multiline-comment-style": ["off", "starred-block"], "multiline-comment-style": [
"multiline-ternary": ["error", "never"], "off",
"starred-block"
],
"multiline-ternary": [
"error",
"never"
],
"new-cap": ["error"], "new-cap": ["error"],
"new-parens": ["error"], "new-parens": ["error"],
// TODO: configure this... // TODO: configure this...
@ -115,15 +145,18 @@ module.exports = {
"no-catch-shadow": ["error"], "no-catch-shadow": ["error"],
"no-class-assign": ["error"], "no-class-assign": ["error"],
"no-compare-neg-zero": ["error"], "no-compare-neg-zero": ["error"],
"no-cond-assign": ["error", "except-parens"], "no-cond-assign": [
"error",
"except-parens"
],
"no-confusing-arrow": ["error"], "no-confusing-arrow": ["error"],
"no-console": ["error"], "no-console": ["error"],
"no-const-assign": ["error"], "no-const-assign": ["error"],
"no-constant-condition": [ "no-constant-condition": [
"error", "error",
{ {
checkLoops: false, "checkLoops": false
}, }
], ],
"no-continue": ["off"], "no-continue": ["off"],
"no-control-regex": ["error"], "no-control-regex": ["error"],
@ -137,15 +170,15 @@ module.exports = {
"no-duplicate-imports": [ "no-duplicate-imports": [
"error", "error",
{ {
includeExports: true, "includeExports": true
}, }
], ],
"no-else-return": ["error"], "no-else-return": ["error"],
"no-empty": [ "no-empty": [
"error", "error",
{ {
allowEmptyCatch: false, "allowEmptyCatch": false
}, }
], ],
"no-empty-character-class": ["error"], "no-empty-character-class": ["error"],
"no-empty-function": ["error"], "no-empty-function": ["error"],
@ -161,8 +194,8 @@ module.exports = {
"error", "error",
"all", "all",
{ {
conditionalAssign: false, "conditionalAssign": false
}, }
], ],
"no-extra-semi": ["error"], "no-extra-semi": ["error"],
"no-fallthrough": ["error"], "no-fallthrough": ["error"],
@ -173,17 +206,20 @@ module.exports = {
"no-implicit-globals": ["error"], "no-implicit-globals": ["error"],
"no-implied-eval": ["error"], "no-implied-eval": ["error"],
"no-inline-comments": ["error"], "no-inline-comments": ["error"],
"no-inner-declarations": ["error", "both"], "no-inner-declarations": [
"error",
"both"
],
"no-invalid-regexp": ["error"], "no-invalid-regexp": ["error"],
"no-invalid-this": ["error"], "no-invalid-this": ["error"],
"no-irregular-whitespace": [ "no-irregular-whitespace": [
"error", "error",
{ {
skipComments: false, "skipComments": false,
skipRegExps: false, "skipRegExps": false,
skipStrings: false, "skipStrings": false,
skipTemplates: false, "skipTemplates": false
}, }
], ],
"no-iterator": ["error"], "no-iterator": ["error"],
"no-label-var": ["error"], "no-label-var": ["error"],
@ -194,9 +230,13 @@ module.exports = {
"no-magic-numbers": [ "no-magic-numbers": [
"error", "error",
{ {
ignore: [-1, 0, 1], "ignore": [
ignoreArrayIndexes: true, -1,
}, 0,
1
],
"ignoreArrayIndexes": true
}
], ],
"no-mixed-operators": ["error"], "no-mixed-operators": ["error"],
"no-mixed-requires": ["error"], "no-mixed-requires": ["error"],
@ -207,8 +247,8 @@ module.exports = {
"no-multiple-empty-lines": [ "no-multiple-empty-lines": [
"error", "error",
{ {
max: 1, "max": 1
}, }
], ],
"no-native-reassign": ["error"], "no-native-reassign": ["error"],
"no-negated-condition": ["error"], "no-negated-condition": ["error"],
@ -228,8 +268,8 @@ module.exports = {
"no-plusplus": [ "no-plusplus": [
"error", "error",
{ {
allowForLoopAfterthoughts: true, "allowForLoopAfterthoughts": true
}, }
], ],
"no-process-env": ["error"], "no-process-env": ["error"],
"no-process-exit": ["error"], "no-process-exit": ["error"],
@ -243,10 +283,10 @@ module.exports = {
"no-restricted-properties": [ "no-restricted-properties": [
"error", "error",
{ {
message: "'log' is too general, use an appropriate level when logging.", "message": "'log' is too general, use an appropriate level when logging.",
object: "console", "object": "console",
property: "log", "property": "log"
}, }
], ],
"no-restricted-syntax": ["error"], "no-restricted-syntax": ["error"],
"no-return-assign": ["error"], "no-return-assign": ["error"],
@ -255,8 +295,8 @@ module.exports = {
"no-self-assign": [ "no-self-assign": [
"error", "error",
{ {
props: false, "props": false
}, }
], ],
"no-self-compare": ["error"], "no-self-compare": ["error"],
"no-sequences": ["error"], "no-sequences": ["error"],
@ -293,10 +333,10 @@ module.exports = {
"no-useless-rename": [ "no-useless-rename": [
"error", "error",
{ {
ignoreDestructuring: false, "ignoreDestructuring": false,
ignoreExport: false, "ignoreExport": false,
ignoreImport: false, "ignoreImport": false
}, }
], ],
"no-useless-return": ["error"], "no-useless-return": ["error"],
"no-var": ["error"], "no-var": ["error"],
@ -304,7 +344,10 @@ module.exports = {
"no-warning-comments": ["error"], "no-warning-comments": ["error"],
"no-whitespace-before-property": ["error"], "no-whitespace-before-property": ["error"],
"no-with": ["error"], "no-with": ["error"],
"nonblock-statement-body-position": ["error", "below"], "nonblock-statement-body-position": [
"error",
"below"
],
"object-curly-newline": ["error"], "object-curly-newline": ["error"],
"object-curly-spacing": ["error"], "object-curly-spacing": ["error"],
"object-property-newline": ["error"], "object-property-newline": ["error"],
@ -312,7 +355,10 @@ module.exports = {
"one-var": ["off"], "one-var": ["off"],
"one-var-declaration-per-line": ["error"], "one-var-declaration-per-line": ["error"],
"operator-assignment": ["error"], "operator-assignment": ["error"],
"operator-linebreak": ["error", "none"], "operator-linebreak": [
"error",
"none"
],
"padded-blocks": ["off"], "padded-blocks": ["off"],
"padding-line-between-statements": ["error"], "padding-line-between-statements": ["error"],
"prefer-arrow-callback": ["error"], "prefer-arrow-callback": ["error"],
@ -325,15 +371,24 @@ module.exports = {
"prefer-spread": ["error"], "prefer-spread": ["error"],
"prefer-template": ["error"], "prefer-template": ["error"],
"quote-props": ["error"], "quote-props": ["error"],
quotes: ["error"], "quotes": ["error"],
radix: ["error", "as-needed"], "radix": [
"error",
"as-needed"
],
"require-await": ["error"], "require-await": ["error"],
"require-jsdoc": ["off"], "require-jsdoc": ["off"],
"require-yield": ["error"], "require-yield": ["error"],
"rest-spread-spacing": ["error", "never"], "rest-spread-spacing": [
semi: ["error"], "error",
"never"
],
"semi": ["error"],
"semi-spacing": ["error"], "semi-spacing": ["error"],
"semi-style": ["error", "last"], "semi-style": [
"error",
"last"
],
"sort-imports": ["error"], "sort-imports": ["error"],
"sort-keys": ["error"], "sort-keys": ["error"],
"sort-vars": ["error"], "sort-vars": ["error"],
@ -343,25 +398,37 @@ module.exports = {
"space-infix-ops": ["error"], "space-infix-ops": ["error"],
"space-unary-ops": ["error"], "space-unary-ops": ["error"],
"spaced-comment": ["error"], "spaced-comment": ["error"],
strict: ["error"], "strict": ["error"],
"switch-colon-spacing": [ "switch-colon-spacing": [
"error", "error",
{ {
after: true, "after": true,
before: false, "before": false
}, }
], ],
"symbol-description": ["error"], "symbol-description": ["error"],
"template-curly-spacing": ["error"], "template-curly-spacing": ["error"],
"template-tag-spacing": ["error"], "template-tag-spacing": ["error"],
"unicode-bom": ["error", "never"], "unicode-bom": [
"error",
"never"
],
"use-isnan": ["error"], "use-isnan": ["error"],
"valid-jsdoc": ["error"], "valid-jsdoc": ["error"],
"valid-typeof": ["error"], "valid-typeof": ["error"],
"vars-on-top": ["error"], "vars-on-top": ["error"],
"wrap-iife": ["error", "any"], "wrap-iife": [
"error",
"any"
],
"wrap-regex": ["error"], "wrap-regex": ["error"],
"yield-star-spacing": ["error", "before"], "yield-star-spacing": [
yoda: ["error", "never"], "error",
}, "before"
],
"yoda": [
"error",
"never"
]
}
}; };

@ -8,8 +8,7 @@ const path = require("path");
const exec = require("child_process").exec; const exec = require("child_process").exec;
const semver = require("./semver"); const semver = require("./semver");
const getPackageJson = () => const getPackageJson = () => new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
try { try {
/* eslint-disable-next-line global-require */ /* eslint-disable-next-line global-require */
resolve(require(path.resolve(process.cwd(), "package.json"))); resolve(require(path.resolve(process.cwd(), "package.json")));
@ -18,8 +17,7 @@ const getPackageJson = () =>
} }
}); });
const getEngines = (data) => const getEngines = (data) => new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
let versions = null; let versions = null;
if (data.engines) { if (data.engines) {
@ -33,8 +31,7 @@ const getEngines = (data) =>
} }
}); });
const checkNpmVersion = (engines) => const checkNpmVersion = (engines) => new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
exec("npm -v", (error, stdout, stderr) => { exec("npm -v", (error, stdout, stderr) => {
if (error) { if (error) {
reject(`Unable to find NPM version\n${stderr}`); reject(`Unable to find NPM version\n${stderr}`);
@ -46,23 +43,18 @@ const checkNpmVersion = (engines) =>
if (semver.satisfies(npmVersion, engineVersion)) { if (semver.satisfies(npmVersion, engineVersion)) {
resolve(); resolve();
} else { } else {
reject( reject(`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`);
`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`,
);
} }
}); });
}); });
const checkNodeVersion = (engines) => const checkNodeVersion = (engines) => new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
const nodeVersion = process.version.substring(1); const nodeVersion = process.version.substring(1);
if (semver.satisfies(nodeVersion, engines.node)) { if (semver.satisfies(nodeVersion, engines.node)) {
resolve(engines); resolve(engines);
} else { } else {
reject( reject(`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`);
`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`,
);
} }
}); });
@ -77,5 +69,5 @@ getPackageJson()
/* eslint-disable no-console, no-process-exit */ /* eslint-disable no-console, no-process-exit */
console.error(error); console.error(error);
process.exit(1); process.exit(1);
}, }
); );

@ -444,6 +444,7 @@ function parseComparator(comp, loose) {
} }
class SemVer { class SemVer {
/** /**
* A semantic version. * A semantic version.
* @param {string} version The version. * @param {string} version The version.
@ -487,7 +488,7 @@ class SemVer {
// Numberify any prerelease numeric ids // Numberify any prerelease numeric ids
if (matches[4]) { if (matches[4]) {
this.prerelease = matches[4].split(".").map((id) => { this.prerelease = matches[4].split(".").map((id) => {
if (/^[0-9]+$/.test(id)) { if ((/^[0-9]+$/).test(id)) {
const num = Number(id); const num = Number(id);
if (num >= 0 && num < MAX_SAFE_INTEGER) { if (num >= 0 && num < MAX_SAFE_INTEGER) {
return num; return num;
@ -531,9 +532,7 @@ class SemVer {
} }
return ( return (
compareIdentifiers(this.major, other.major) || compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch)
compareIdentifiers(this.minor, other.minor) ||
compareIdentifiers(this.patch, other.patch)
); );
} }
@ -573,8 +572,7 @@ class SemVer {
} }
} }
const compare = (leftVersion, rightVersion, loose) => const compare = (leftVersion, rightVersion, loose) => new SemVer(leftVersion, loose).compare(new SemVer(rightVersion, loose));
new SemVer(leftVersion, loose).compare(new SemVer(rightVersion, loose));
const gt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) > 0; const gt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) > 0;
const lt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) < 0; const lt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) < 0;
const eq = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) === 0; const eq = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) === 0;

@ -1,6 +1,6 @@
import { IBladeburner } from "./IBladeburner"; import { IBladeburner } from "./IBladeburner";
export interface IStatsMultiplier { interface IStatsMultiplier {
[key: string]: number; [key: string]: number;
hack: number; hack: number;

@ -1,4 +1,4 @@
export interface RNG { interface RNG {
random(): number; random(): number;
} }

@ -45,7 +45,7 @@ export function generateRandomContractOnHome(): void {
serv.addContract(contract); serv.addContract(contract);
} }
export interface IGenerateContractParams { interface IGenerateContractParams {
problemType?: string; problemType?: string;
server?: string; server?: string;
fn?: string; fn?: string;

@ -8,7 +8,7 @@ import { CodingContractEvent } from "./ui/React/CodingContractModal";
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */ /* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
/* Represents different types of problems that a Coding Contract can have */ /* Represents different types of problems that a Coding Contract can have */
export class CodingContractType { class CodingContractType {
/** /**
* Function that generates a description of the problem * Function that generates a description of the problem
*/ */

@ -45,8 +45,3 @@ export function initCompanies(): void {
export function loadCompanies(saveString: string): void { export function loadCompanies(saveString: string): void {
Companies = JSON.parse(saveString, Reviver); Companies = JSON.parse(saveString, Reviver);
} }
// Utility function to check if a string is valid company name
export function companyExists(name: string): boolean {
return Companies.hasOwnProperty(name);
}

@ -20,7 +20,7 @@ export function IndustryProductEquation(props: IProps): React.ReactElement {
return ( return (
<MathJaxContext> <MathJaxContext>
<MathJax>{"\\(" + reqs.join("+") + `\\Rightarrow` + prod.map((p) => `1\\text{ }${p}`).join("+") + "\\)"}</MathJax> <MathJax>{"\\(" + reqs.join("+") + `\\Rightarrow` + prod.map((p) => `1 \\text{${p}}`).join("+") + "\\)"}</MathJax>
</MathJaxContext> </MathJaxContext>
); );
} }

@ -76,11 +76,6 @@ function MarketTA2(props: IMarketTA2Props): React.ReactElement {
</Tooltip> </Tooltip>
} }
/> />
<Typography>
Note that Market-TA.II overrides Market-TA.I. This means that if both are enabled, then Market-TA.II will take
effect, not Market-TA.I
</Typography>
</> </>
); );
} }
@ -93,6 +88,7 @@ interface IProps {
// Create a popup that lets the player use the Market TA research for Materials // Create a popup that lets the player use the Market TA research for Materials
export function MaterialMarketTaModal(props: IProps): React.ReactElement { export function MaterialMarketTaModal(props: IProps): React.ReactElement {
const division = useDivision();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
@ -106,11 +102,13 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
{!division.hasResearch("Market-TA.II") && (
<>
<Typography variant="h4">Market-TA.I</Typography> <Typography variant="h4">Market-TA.I</Typography>
<Typography> <Typography>
The maximum sale price you can mark this up to is {numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}. The maximum sale price you can mark this up to is{" "}
This means that if you set the sale price higher than this, you will begin to experience a loss in number of {numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}. This means that if you set the sale price
sales higher than this, you will begin to experience a loss in number of sales
</Typography> </Typography>
<FormControlLabel <FormControlLabel
@ -119,8 +117,8 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I If this is enabled, then this Material will automatically be sold at the price identified by
(i.e. the price shown above) Market-TA.I (i.e. the price shown above)
</Typography> </Typography>
} }
> >
@ -128,6 +126,8 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
</Tooltip> </Tooltip>
} }
/> />
</>
)}
<MarketTA2 mat={props.mat} /> <MarketTA2 mat={props.mat} />
</Modal> </Modal>
); );

@ -66,11 +66,6 @@ function MarketTA2(props: ITa2Props): React.ReactElement {
</Tooltip> </Tooltip>
} }
/> />
<Typography>
Note that Market-TA.II overrides Market-TA.I. This means that if both are enabled, then Market-TA.II will take
effect, not Market-TA.I
</Typography>
</> </>
); );
} }
@ -83,6 +78,7 @@ interface IProps {
// Create a popup that lets the player use the Market TA research for Products // Create a popup that lets the player use the Market TA research for Products
export function ProductMarketTaModal(props: IProps): React.ReactElement { export function ProductMarketTaModal(props: IProps): React.ReactElement {
const division = useDivision();
const markupLimit = props.product.rat / props.product.mku; const markupLimit = props.product.rat / props.product.mku;
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -96,6 +92,8 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
{!division.hasResearch("Market-TA.II") && (
<>
<Typography variant="h4">Market-TA.I</Typography> <Typography variant="h4">Market-TA.I</Typography>
<Typography> <Typography>
The maximum sale price you can mark this up to is{" "} The maximum sale price you can mark this up to is{" "}
@ -109,8 +107,8 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I If this is enabled, then this Material will automatically be sold at the price identified by
(i.e. the price shown above) Market-TA.I (i.e. the price shown above)
</Typography> </Typography>
} }
> >
@ -118,7 +116,8 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
</Tooltip> </Tooltip>
} }
/> />
</>
)}
<MarketTA2 product={props.product} /> <MarketTA2 product={props.product} />
</Modal> </Modal>
); );

@ -4,7 +4,7 @@ import { IPlayerOrSleeve } from "../PersonObjects/IPlayerOrSleeve";
import { IRouter } from "../ui/Router"; import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
export interface IConstructorParams { interface IConstructorParams {
hacking_success_weight?: number; hacking_success_weight?: number;
strength_success_weight?: number; strength_success_weight?: number;
defense_success_weight?: number; defense_success_weight?: number;

@ -24,7 +24,7 @@ export function loadFactions(saveString: string): void {
} }
} }
export function AddToFactions(faction: Faction): void { function AddToFactions(faction: Faction): void {
const name: string = faction.name; const name: string = faction.name;
Factions[name] = faction; Factions[name] = faction;
} }
@ -42,7 +42,7 @@ export function initFactions(): void {
//Resets a faction during (re-)initialization. Saves the favor in the new //Resets a faction during (re-)initialization. Saves the favor in the new
//Faction object and deletes the old Faction Object from "Factions". Then //Faction object and deletes the old Faction Object from "Factions". Then
//reinserts the new Faction object //reinserts the new Faction object
export function resetFaction(newFactionObject: Faction): void { function resetFaction(newFactionObject: Faction): void {
if (!(newFactionObject instanceof Faction)) { if (!(newFactionObject instanceof Faction)) {
throw new Error("Invalid argument 'newFactionObject' passed into resetFaction()"); throw new Error("Invalid argument 'newFactionObject' passed into resetFaction()");
} }

@ -5,7 +5,7 @@ import { ITaskParams } from "../ITaskParams";
* Defines the parameters that can be used to initialize and describe a GangMemberTask * Defines the parameters that can be used to initialize and describe a GangMemberTask
* (defined in Gang.js) * (defined in Gang.js)
*/ */
export interface IGangMemberTaskMetadata { interface IGangMemberTaskMetadata {
/** /**
* Description of the task * Description of the task
*/ */

@ -19,7 +19,7 @@ export enum UpgradeType {
* Defines the parameters that can be used to initialize and describe a GangMemberUpgrade * Defines the parameters that can be used to initialize and describe a GangMemberUpgrade
* (defined in Gang.js) * (defined in Gang.js)
*/ */
export interface IGangMemberUpgradeMetadata { interface IGangMemberUpgradeMetadata {
cost: number; cost: number;
mults: IMults; mults: IMults;
name: string; name: string;

@ -69,6 +69,7 @@ export function TechVendorLocation(props: IProps): React.ReactElement {
return ( return (
<> <>
<br />
{purchaseServerButtons} {purchaseServerButtons}
<br /> <br />
<Typography> <Typography>

@ -217,4 +217,4 @@ function initMessages(): void {
); );
} }
export { Messages, checkForMessagesToSend, sendMessage, showMessage, loadMessages, initMessages, Message }; export { Messages, checkForMessagesToSend, showMessage, loadMessages, initMessages };

@ -1,11 +0,0 @@
import { Milestone } from "./Milestone";
export class Quest {
title: string;
milestones: Milestone[];
constructor(title: string, milestones: Milestone[]) {
this.title = title;
this.milestones = milestones;
}
}

@ -106,6 +106,11 @@ export class WorkerScript {
*/ */
hostname: string; hostname: string;
/**
* Function called when the script ends.
*/
atExit: any;
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => any) { constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => any) {
this.name = runningScriptObj.filename; this.name = runningScriptObj.filename;
this.hostname = runningScriptObj.server; this.hostname = runningScriptObj.server;

@ -10,6 +10,7 @@ import { RunningScript } from "../Script/RunningScript";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { compareArrays } from "../utils/helpers/compareArrays"; import { compareArrays } from "../utils/helpers/compareArrays";
import { dialogBoxCreate } from "../ui/React/DialogBox";
export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean; export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean;
export function killWorkerScript(workerScript: WorkerScript): boolean; export function killWorkerScript(workerScript: WorkerScript): boolean;
@ -67,6 +68,16 @@ function killWorkerScriptByPid(pid: number, rerenderUi = true): boolean {
function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = true): void { function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = true): void {
workerScript.env.stopFlag = true; workerScript.env.stopFlag = true;
killNetscriptDelay(workerScript); killNetscriptDelay(workerScript);
if (typeof workerScript.atExit === "function") {
try {
workerScript.atExit();
} catch (e: any) {
dialogBoxCreate(
`Error trying to call atExit for script ${workerScript.name} on ${workerScript.hostname} ${workerScript.scriptRef.args} ${e}`,
);
}
workerScript.atExit = undefined;
}
removeWorkerScript(workerScript, rerenderUi); removeWorkerScript(workerScript, rerenderUi);
} }

@ -86,6 +86,7 @@ import { INetscriptStockMarket, NetscriptStockMarket } from "./NetscriptFunction
import { dialogBoxCreate } from "./ui/React/DialogBox"; import { dialogBoxCreate } from "./ui/React/DialogBox";
import { SnackbarEvents } from "./ui/React/Snackbar"; import { SnackbarEvents } from "./ui/React/Snackbar";
import { Locations } from "./Locations/Locations";
const defaultInterpreter = new Interpreter("", () => undefined); const defaultInterpreter = new Interpreter("", () => undefined);
@ -2211,6 +2212,19 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
}, },
/* Singularity Functions */ /* Singularity Functions */
goToLocation: function (locationName: any): boolean {
const location = Object.values(Locations).find((l) => l.name === locationName);
if (!location) {
workerScript.log("goToLocation", `No location named ${locationName}`);
return false;
}
if (Player.city !== location.city) {
workerScript.log("goToLocation", `No location named ${locationName} in ${Player.city}`);
return false;
}
Router.toLocation(location);
return true;
},
universityCourse: function (universityName: any, className: any): any { universityCourse: function (universityName: any, className: any): any {
updateDynamicRam("universityCourse", getRamCost("universityCourse")); updateDynamicRam("universityCourse", getRamCost("universityCourse"));
checkSingularityAccess("universityCourse", 1); checkSingularityAccess("universityCourse", 1);
@ -3263,6 +3277,13 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
corporation: corporation, corporation: corporation,
formulas: formulas, formulas: formulas,
atExit: function (f: any): void {
if (typeof f !== "function") {
throw makeRuntimeErrorMsg("atExit", "argument should be function");
}
workerScript.atExit = f;
},
flags: function (data: any): any { flags: function (data: any): any {
data = toNative(data); data = toNative(data);
// We always want the help flag. // We always want the help flag.

@ -469,11 +469,7 @@ export function startWorkerScript(runningScript: RunningScript, server: BaseServ
* @param {Server} server - Server on which the script is to be run * @param {Server} server - Server on which the script is to be run
* returns {boolean} indicating whether or not the workerScript was successfully added * returns {boolean} indicating whether or not the workerScript was successfully added
*/ */
export function createAndAddWorkerScript( function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseServer, parent?: WorkerScript): boolean {
runningScriptObj: RunningScript,
server: BaseServer,
parent?: WorkerScript,
): boolean {
// Update server's ram usage // Update server's ram usage
let threads = 1; let threads = 1;
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) { if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {

@ -1,3 +0,0 @@
import { EventEmitter } from "../utils/EventEmitter";
export const PlayerEvents = new EventEmitter<[]>();

@ -1,100 +0,0 @@
import * as React from "react";
export const SleeveFaq = (
<>
<strong>
<u>How do Duplicate Sleeves work?</u>
</strong>
<br />
Duplicate Sleeves are essentially clones. You can use them to perform any work type action, such as working for a
company/faction or committing a crime. Having sleeves perform these tasks earns you money, experience, and
reputation.
<br />
<br />
Sleeves are their own individuals, which means they each have their own experience and stats.
<br />
<br />
When a sleeve earns experience, it earns experience for itself, the player's original 'consciousness', as well as
all of the player's other sleeves.
<br />
<br />
<strong>
<u>What is Synchronization (Sync)?</u>
</strong>
<br />
Synchronization is a measure of how aligned your consciousness is with that of your Duplicate Sleeves. It is a
numerical value between 1 and 100, and it affects how much experience is earned when the sleeve is performing a
task.
<br />
<br />
Let N be the sleeve's synchronization. When the sleeve earns experience by performing a task, both the sleeve and
the player's original host consciousness earn N% of the amount of experience normally earned by the task. All of the
player's other sleeves earn ((N/100)^2 * 100)% of the experience.
<br />
<br />
Synchronization can be increased by assigning sleeves to the 'Synchronize' task.
<br />
<br />
<strong>
<u>What is Shock?</u>
</strong>
<br />
Sleeve shock is a measure of how much trauma the sleeve has due to being placed in a new body. It is a numerical
value between 0 and 99, where 99 indicates full shock and 0 indicates no shock. Shock affects the amount of
experience earned by the sleeve.
<br />
<br />
Sleeve shock slowly decreases over time. You can further increase the rate at which it decreases by assigning
sleeves to the 'Shock Recovery' task.
<br />
<br />
<strong>
<u>Why can't I work for this company or faction?</u>
</strong>
<br />
Only one of your sleeves can work for a given company/faction a time. To clarify further, if you have two sleeves
they can work for two different companies, but they cannot both work for the same company.
<br />
<br />
<strong>
<u>Why did my Sleeve stop working?</u>
</strong>
<br />
Sleeves are subject to the same time restrictions as you. This means that they automatically stop working at a
company after 8 hours, and stop working for a faction after 20 hours.
<br />
<br />
<strong>
<u>How do I buy Augmentations for my Sleeves?</u>
</strong>
<br />
Your Sleeve needs to have a Shock of 0 in order for you to buy Augmentations for it.
<br />
<br />
<strong>
<u>Why can't I buy the X Augmentation for my sleeve?</u>
</strong>
<br />
Certain Augmentations, like Bladeburner-specific ones and NeuroFlux Governor, are not available for sleeves.
<br />
<br />
<strong>
<u>Do sleeves get reset when installing Augmentations or switching BitNodes?</u>
</strong>
<br />
Sleeves are reset when switching BitNodes, but not when installing Augmentations.
<br />
<br />
<strong>
<u>What is Memory?</u>
</strong>
<br />
Sleeve memory dictates what a sleeve's synchronization will be when its reset by switching BitNodes. For example, if
a sleeve has a memory of 25, then when you switch BitNodes its synchronization will initially be set to 25, rather
than 1.
<br />
<br />
Memory can only be increased by purchasing upgrades from The Covenant. It is a persistent stat, meaning it never
gets resets back to 1. The maximum possible value for a sleeve's memory is 100.
</>
);

@ -24,7 +24,7 @@ function bitFlumeRequirements() {
}; };
} }
export interface IProgramCreationParams { interface IProgramCreationParams {
key: string; key: string;
name: string; name: string;
create: IProgramCreate | null; create: IProgramCreate | null;

@ -1,7 +1,7 @@
export type Position = { interface Position {
row: number; row: number;
column: number; column: number;
}; }
class PositionTracker { class PositionTracker {
positions: Map<string, Position>; positions: Map<string, Position>;

@ -197,7 +197,7 @@ export function initSymbolToStockMap(): void {
} }
} }
export function stockMarketCycle(): void { function stockMarketCycle(): void {
for (const name in StockMarket) { for (const name in StockMarket) {
const stock = StockMarket[name]; const stock = StockMarket[name];
if (!(stock instanceof Stock)) { if (!(stock instanceof Stock)) {

@ -1,14 +1,6 @@
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
export function getStockMarketAccountCost(): number {
return CONSTANTS.WSEAccountCost;
}
export function getStockMarketTixApiCost(): number {
return CONSTANTS.TIXAPICost;
}
export function getStockMarket4SDataCost(): number { export function getStockMarket4SDataCost(): number {
return CONSTANTS.MarketData4SCost * BitNodeMultipliers.FourSigmaMarketDataCost; return CONSTANTS.MarketData4SCost * BitNodeMultipliers.FourSigmaMarketDataCost;
} }

@ -15,7 +15,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
type txFn = (stock: Stock, shares: number) => boolean; type txFn = (stock: Stock, shares: number) => boolean;
export type placeOrderFn = ( type placeOrderFn = (
stock: Stock, stock: Stock,
shares: number, shares: number,
price: number, price: number,

@ -39,8 +39,8 @@ enum SelectorOrderType {
Stop = "Stop Order", Stop = "Stop Order",
} }
export type txFn = (stock: Stock, shares: number) => boolean; type txFn = (stock: Stock, shares: number) => boolean;
export type placeOrderFn = ( type placeOrderFn = (
stock: Stock, stock: Stock,
shares: number, shares: number,
price: number, price: number,

@ -47,7 +47,7 @@ function LongPosition(props: IProps): React.ReactElement {
</Box> </Box>
<Typography>Shares: {numeralWrapper.formatShares(stock.playerShares)}</Typography> <Typography>Shares: {numeralWrapper.formatShares(stock.playerShares)}</Typography>
<Typography> <Typography>
Average Price: <Money money={stock.playerAvgPx} /> (Total Cost: <Money money={totalCost} /> Average Price: <Money money={stock.playerAvgPx} /> (Total Cost: <Money money={totalCost} />)
</Typography> </Typography>
<Typography> <Typography>
Profit: <Money money={gains} /> ({numeralWrapper.formatPercentage(percentageGains)}) Profit: <Money money={gains} /> ({numeralWrapper.formatPercentage(percentageGains)})

@ -16,8 +16,8 @@ import { PositionTypes } from "../data/PositionTypes";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
export type txFn = (stock: Stock, shares: number) => boolean; type txFn = (stock: Stock, shares: number) => boolean;
export type placeOrderFn = ( type placeOrderFn = (
stock: Stock, stock: Stock,
shares: number, shares: number,
price: number, price: number,

@ -35,7 +35,7 @@ export function removeTrailingSlash(s: string): string {
* Checks whether a string is a valid filename. Only used for the filename itself, * Checks whether a string is a valid filename. Only used for the filename itself,
* not the entire filepath * not the entire filepath
*/ */
export function isValidFilename(filename: string): boolean { function isValidFilename(filename: string): boolean {
// Allows alphanumerics, hyphens, underscores, and percentage signs // Allows alphanumerics, hyphens, underscores, and percentage signs
// Must have a file extension // Must have a file extension
const regex = /^[.a-zA-Z0-9_-]+[.][a-zA-Z0-9]+(?:-\d+(?:\.\d*)?%-INC)?$/; const regex = /^[.a-zA-Z0-9_-]+[.][a-zA-Z0-9]+(?:-\d+(?:\.\d*)?%-INC)?$/;
@ -48,7 +48,7 @@ export function isValidFilename(filename: string): boolean {
* Checks whether a string is a valid directory name. Only used for the directory itself, * Checks whether a string is a valid directory name. Only used for the directory itself,
* not an entire path * not an entire path
*/ */
export function isValidDirectoryName(name: string): boolean { function isValidDirectoryName(name: string): boolean {
// Allows alphanumerics, hyphens, underscores, and percentage signs. // Allows alphanumerics, hyphens, underscores, and percentage signs.
// Name can begin with a single period, but otherwise cannot have any // Name can begin with a single period, but otherwise cannot have any
const regex = /^.?[a-zA-Z0-9_-]+$/; const regex = /^.?[a-zA-Z0-9_-]+$/;

@ -12,7 +12,7 @@ export type SolverFunc = (data: any, answer: string) => boolean;
Requires the 'data' of a Contract as input */ Requires the 'data' of a Contract as input */
export type DescriptionFunc = (data: any) => string; export type DescriptionFunc = (data: any) => string;
export interface ICodingContractTypeMetadata { interface ICodingContractTypeMetadata {
desc: DescriptionFunc; desc: DescriptionFunc;
difficulty: number; difficulty: number;
gen: GeneratorFunc; gen: GeneratorFunc;

@ -10,11 +10,6 @@ export interface IMap<T> {
[key: string]: T; [key: string]: T;
} }
/**
* Performs some action, with no returned value.
*/
export type Action = () => void;
/** /**
* Contains a method to initialize itself to a known state. * Contains a method to initialize itself to a known state.
*/ */

@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../utils/helpers/keyCodes";
import { CodingContract, CodingContractType, CodingContractTypes } from "../../CodingContracts"; import { CodingContract, CodingContractTypes } from "../../CodingContracts";
import { CopyableText } from "./CopyableText"; import { CopyableText } from "./CopyableText";
import { Modal } from "./Modal"; import { Modal } from "./Modal";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
@ -49,7 +49,7 @@ export function CodingContractModal(): React.ReactElement {
setProps(null); setProps(null);
} }
const contractType: CodingContractType = CodingContractTypes[props.c.type]; const contractType = CodingContractTypes[props.c.type];
const description = []; const description = [];
for (const [i, value] of contractType.desc(props.c.data).split("\n").entries()) for (const [i, value] of contractType.desc(props.c.data).split("\n").entries())
description.push(<span key={i} dangerouslySetInnerHTML={{ __html: value + "<br />" }}></span>); description.push(<span key={i} dangerouslySetInnerHTML={{ __html: value + "<br />" }}></span>);

@ -1,32 +0,0 @@
/**
* React component for a popup content container
*
* Takes in a prop for rendering the content inside the popup
*/
import React, { useEffect } from "react";
interface IProps<T> {
content: (props: T) => React.ReactElement;
id: string;
props: T;
removePopup: () => void;
}
export function Popup<T>(props: IProps<T>): React.ReactElement {
function keyDown(event: KeyboardEvent): void {
if (event.key === "Escape") props.removePopup();
}
useEffect(() => {
document.addEventListener("keydown", keyDown);
return () => {
document.removeEventListener("keydown", keyDown);
};
});
return (
<div className={"popup-box-content"} id={`${props.id}-content`}>
{React.createElement(props.content, props.props)}
</div>
);
}

@ -4,7 +4,7 @@
type cbFn = (...args: any[]) => any; type cbFn = (...args: any[]) => any;
export interface ISubscriber { interface ISubscriber {
/** /**
* Callback function that will be run when an event is emitted * Callback function that will be run when an event is emitted
*/ */

@ -1,6 +1,6 @@
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */ /* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
export interface IReviverValue { interface IReviverValue {
ctor: string; ctor: string;
data: any; data: any;
} }

@ -1,13 +1,6 @@
import { EqualityFunc } from "../types"; import { EqualityFunc } from "../types";
import { isString } from "./helpers/isString"; import { isString } from "./helpers/isString";
// Netburner String helper functions
// Replaces the character at an index with a new character
function replaceAt(base: string, index: number, character: string): string {
return base.substr(0, index) + character + base.substr(index + character.length);
}
/* /*
Converts a date representing time in milliseconds to a string with the format H hours M minutes and S seconds Converts a date representing time in milliseconds to a string with the format H hours M minutes and S seconds
e.g. 10000 -> "10 seconds" e.g. 10000 -> "10 seconds"
@ -92,20 +85,6 @@ function formatNumber(num: number, numFractionDigits = 0): string {
}); });
} }
// Checks if a string contains HTML elements
function isHTML(str: string): boolean {
const element: HTMLDivElement = document.createElement("div");
element.innerHTML = str;
const c: NodeListOf<Node & ChildNode> = element.childNodes;
for (let i: number = c.length - 1; i >= 0; i--) {
if (c[i].nodeType === 1) {
return true;
}
}
return false;
}
// Generates a random alphanumeric string with N characters // Generates a random alphanumeric string with N characters
function generateRandomString(n: number): string { function generateRandomString(n: number): string {
let str = ""; let str = "";
@ -118,12 +97,4 @@ function generateRandomString(n: number): string {
return str; return str;
} }
export { export { convertTimeMsToTimeElapsedString, longestCommonStart, containsAllStrings, formatNumber, generateRandomString };
convertTimeMsToTimeElapsedString,
longestCommonStart,
containsAllStrings,
formatNumber,
isHTML,
generateRandomString,
replaceAt,
};

@ -4,9 +4,7 @@ const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const UnusedWebpackPlugin = require("unused-webpack-plugin");
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const DeadCodePlugin = require("webpack-deadcode-plugin");
module.exports = (env, argv) => { module.exports = (env, argv) => {
const isDevServer = (env || {}).devServer === true; const isDevServer = (env || {}).devServer === true;
@ -107,14 +105,6 @@ module.exports = (env, argv) => {
}, },
}, },
}), }),
new UnusedWebpackPlugin({
// Source directories
directories: [path.join(__dirname, "src"), path.join(__dirname, "utils")],
// Exclude patterns
exclude: ["*.test.js"],
// Root directory (optional)
root: __dirname,
}),
// In dev mode, use a faster method of create sourcemaps // In dev mode, use a faster method of create sourcemaps
// while keeping lines/columns accurate // while keeping lines/columns accurate
isDevServer && isDevServer &&
@ -132,10 +122,6 @@ module.exports = (env, argv) => {
module: true, module: true,
}), }),
isFastRefresh && new ReactRefreshWebpackPlugin(), isFastRefresh && new ReactRefreshWebpackPlugin(),
new DeadCodePlugin({
patterns: ["src/**/*.(js|jsx|css|ts|tsx)"],
exclude: ["**/*.(stories|spec).(js|jsx)"],
}),
].filter(Boolean), ].filter(Boolean),
target: "web", target: "web",
entry: entry, entry: entry,