Refactor lib/ to be native Typescript (#5217)

This commit is contained in:
Kirk Scheibelhut 2019-03-02 09:12:24 -08:00 committed by Guangcong Luo
parent 8c25d8dad6
commit 6e122d5d74
51 changed files with 576 additions and 730 deletions

View File

@ -2,4 +2,4 @@ server/tournaments/lib/
logs/
dev-tools/globals.js
node_modules/
.sim-dist/
.*-dist/

2
.gitignore vendored
View File

@ -10,7 +10,7 @@ npm-debug.log
package-lock.json
# Typescript build artifacts
.sim-dist/
.*-dist/
# visual studio live share
.vs

3
.lib-dist/README.md Normal file
View File

@ -0,0 +1,3 @@
**NOTE**: This folder contains the compiled output of the `lib/` directory.
You should be editing the `.ts` files there and then running `npm run build` or
`./pokemon-showdown` to force these `.js` files to be recreated.

View File

@ -1,3 +1,3 @@
**NOTE**: This folder contains the compiled output of the `sim/` directory.
You should be editting the `.ts` files there and then running `npm run build` or
You should be editing the `.ts` files there and then running `npm run build` or
`./pokemon-showdown` to force these `.js` files to be recreated.

7
build
View File

@ -12,6 +12,9 @@ var fs = require('fs');
var path = require('path');
function shell(cmd) { child_process.execSync(cmd, {stdio: 'inherit', cwd: __dirname}); }
function sucrase(src, out) {
shell(`npx sucrase -q ${src} -d ${out} --transforms typescript,imports --enable-legacy-typescript-module-interop`);
}
try {
require.resolve('sucrase');
@ -20,7 +23,9 @@ try {
shell('npm install');
}
shell('npx sucrase -q ./sim -d ./.sim-dist --transforms typescript,imports --enable-legacy-typescript-module-interop');
sucrase('./sim', './.sim-dist');
sucrase('./lib', './.lib-dist');
shell('npx replace --silent \'(require\\\(.*?)(lib)(.*?\\\))\' \'$1.lib-dist$3\' .sim-dist/*');
// Make sure config.js exists. If not, copy it over synchronously from
// config-example.js, since it's needed before we can start the server

View File

@ -17,7 +17,7 @@ class RandomGen3Teams extends RandomGen4Teams {
template = this.getTemplate('unown');
let err = new Error('Template incompatible with random battles: ' + species);
require('../../../lib/crashlogger')(err, 'The gen 3 randbat set generator');
Monitor.crashlog(err, 'The gen 3 randbat set generator');
}
if (template.battleOnly) species = template.baseSpecies;

View File

@ -18,7 +18,7 @@ class RandomGen4Teams extends RandomGen5Teams {
template = this.getTemplate('unown');
let err = new Error('Template incompatible with random battles: ' + species);
require('../../../lib/crashlogger')(err, 'The gen 4 randbat set generator');
Monitor.crashlog(err, 'The gen 4 randbat set generator');
}
if (template.battleOnly) species = template.baseSpecies;

View File

@ -19,7 +19,7 @@ class RandomGen5Teams extends RandomGen6Teams {
template = this.getTemplate('unown');
let err = new Error('Template incompatible with random battles: ' + species);
require('../../../lib/crashlogger')(err, 'The gen 5 randbat set generator');
Monitor.crashlog(err, 'The gen 5 randbat set generator');
}
if (template.battleOnly) {

View File

@ -30,7 +30,7 @@ class RandomGen6Teams extends RandomTeams {
template = this.getTemplate('unown');
let err = new Error('Template incompatible with random battles: ' + species);
require('../../../lib/crashlogger')(err, 'The gen 6 randbat set generator');
Monitor.crashlog(err, 'The gen 6 randbat set generator');
}
if (template.battleOnly) {

View File

@ -17,7 +17,7 @@ class RandomLetsGoTeams extends RandomTeams {
template = this.getTemplate('bulbasaur');
let err = new Error('Template incompatible with random battles: ' + species);
require('../../../lib/crashlogger')(err, 'The Let\'s Go randbat set generator');
Monitor.crashlog(err, 'The Let\'s Go randbat set generator');
}
if (template.battleOnly) {

View File

@ -611,7 +611,7 @@ class RandomTeams extends Dex.ModdedDex {
template = this.getTemplate('unown');
let err = new Error('Template incompatible with random battles: ' + species);
require('../lib/crashlogger')(err, 'The randbat set generator');
Monitor.crashlog(err, 'The randbat set generator');
}
if (template.battleOnly) {

View File

@ -18,7 +18,7 @@ import UsersType = require('./../server/users');
import PunishmentsType = require('./../server/punishments');
import ChatType = require('./../server/chat');
import StreamsType = require('./../lib/streams');
import * as StreamsType from './../lib/streams';
declare global {
// modules

View File

@ -8,45 +8,38 @@
* @license MIT
*/
'use strict';
const fs = require('fs');
const path = require('path');
import * as fs from 'fs';
import * as path from 'path';
const CRASH_EMAIL_THROTTLE = 5 * 60 * 1000; // 5 minutes
const LOCKDOWN_PERIOD = 30 * 60 * 1000; // 30 minutes
const logPath = path.resolve(__dirname, '../logs/errors.txt');
let lastCrashLog = 0;
/** @type {any} */
let transport;
let transport: any;
/**
* Logs when a crash happens to console, then e-mails those who are configured
* to receive them.
*
* @param {Error | string} err
* @param {string} description
* @param {?Object} [data = null]
* @return {?string}
*/
module.exports = function crashlogger(err, description, data = null) {
export = function crashlogger(error: Error | string, description: string, data: object | null = null): string | null {
const datenow = Date.now();
let stack = typeof err === 'string' ? err : err.stack;
let stack = typeof error === 'string' ? error : error.stack;
if (data) {
stack += `\n\nAdditional information:\n`;
for (let k in data) {
// @ts-ignore
stack += ` ${k} = ${data[k]}\n`;
}
}
console.error(`\nCRASH: ${stack}\n`);
let out = fs.createWriteStream(logPath, {'flags': 'a'});
let out = fs.createWriteStream(logPath, {flags: 'a'});
out.on('open', () => {
out.write(`\n${stack}\n`);
out.end();
}).on('error', /** @param {Error} err */ err => {
}).on('error', (err: Error) => {
console.error(`\nSUBCRASH: ${err.stack}\n`);
});
@ -69,6 +62,7 @@ module.exports = function crashlogger(err, description, data = null) {
text += `again with this stack trace:\n${stack}`;
} else {
try {
// tslint:disable-next-line:no-implicit-dependencies
transport = require('nodemailer').createTransport(Config.crashguardemail.options);
} catch (e) {
throw new Error("Failed to start nodemailer; are you sure you've configured Config.crashguardemail correctly?");
@ -82,7 +76,7 @@ module.exports = function crashlogger(err, description, data = null) {
to: Config.crashguardemail.to,
subject: Config.crashguardemail.subject,
text,
}, /** @param {?Error} err */ err => {
}, (err: Error | null) => {
if (err) console.error(`Error sending email: ${err}`);
});
}

View File

@ -12,8 +12,6 @@
* @license MIT
*/
'use strict';
const CODE_MAP = "23456789abcdefghijkmnpqrstuvwxyz";
const UNSAFE_MAP = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
@ -31,19 +29,14 @@ const UNSAFE_MAP = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
* An object representing a Dashycode bitstream.
* The stream can be either a read stream or a write stream, but not
* both simultaneously.
*
* @typedef {Object} DashyStream
* @property {string} codeBuf
* @property {number} buf
* @property {number} bufLength
*/
interface DashyStream {
codeBuf: string;
buf: number;
bufLength: number;
}
/**
* @param {DashyStream} stream
* @param {number} writeBufLength in bits
* @param {number} writeBuf
*/
function streamWrite(stream, writeBufLength, writeBuf) {
function streamWrite(stream: DashyStream, writeBufLength: number, writeBuf: number) {
stream.buf += (writeBuf << stream.bufLength);
stream.bufLength += writeBufLength;
while (stream.bufLength >= 5) {
@ -52,10 +45,8 @@ function streamWrite(stream, writeBufLength, writeBuf) {
stream.bufLength -= 5;
}
}
/**
* @param {DashyStream} stream
*/
function streamGetCode(stream) {
function streamGetCode(stream: DashyStream) {
const buf = stream.codeBuf + CODE_MAP.charAt(stream.buf);
// truncate trailing `2`s (0b00000 chunks)
@ -64,12 +55,7 @@ function streamGetCode(stream) {
return end2Len ? buf.slice(0, -end2Len) : buf;
}
/**
* @param {DashyStream} stream
* @param {number} readLength
* @param {number} readMask passed for "perf"
*/
function streamPeek(stream, readLength, readMask = 0xFFFF >> (16 - readLength)) {
function streamPeek(stream: DashyStream, readLength: number, readMask: number = 0xFFFF >> (16 - readLength)) {
while (stream.bufLength < readLength && stream.codeBuf.length) {
const next5Bits = CODE_MAP.indexOf(stream.codeBuf.charAt(0));
if (next5Bits < 0) throw new Error("Invalid character in coded buffer");
@ -79,12 +65,8 @@ function streamPeek(stream, readLength, readMask = 0xFFFF >> (16 - readLength))
}
return stream.buf & readMask;
}
/**
* @param {DashyStream} stream
* @param {number} readLength
* @param {number} readMask passed for "perf"
*/
function streamRead(stream, readLength, readMask = 0xFFFF >> (16 - readLength)) {
function streamRead(stream: DashyStream, readLength: number, readMask: number = 0xFFFF >> (16 - readLength)) {
const output = streamPeek(stream, readLength, readMask);
// Note: bufLength can go negative! Streams have infinite trailing 0s
stream.buf >>= readLength;
@ -92,17 +74,14 @@ function streamRead(stream, readLength, readMask = 0xFFFF >> (16 - readLength))
return output;
}
/**
* @param {string} str
*/
function encode(str, allowCaps = false) {
export function encode(str: string, allowCaps: boolean = false) {
if (!str) return '0--0';
let safePart = '';
let unsafeStream = /** @type {DashyStream} */ ({
let unsafeStream: DashyStream = {
codeBuf: '',
buf: 0x0,
bufLength: 0,
});
};
let isSafe = true;
let alphaIndex = 0;
let capBuffer = 0x0;
@ -169,6 +148,7 @@ function encode(str, allowCaps = false) {
streamWrite(unsafeStream, 2, 0x0);
} else if (curCharCode === 32) { // space
streamWrite(unsafeStream, 3, 0x3);
// tslint:disable-next-line:no-conditional-assignment
} else if ((unsafeMapIndex = UNSAFE_MAP.indexOf(str.charAt(i))) >= 0) {
curCharCode = (unsafeMapIndex << 2) + 0x2;
streamWrite(unsafeStream, 7, curCharCode);
@ -194,10 +174,7 @@ function encode(str, allowCaps = false) {
return safePart + '--' + unsafePart;
}
/**
* @param {string} codedStr
*/
function decode(codedStr) {
export function decode(codedStr: string) {
let str = '';
let lastDashIndex = codedStr.lastIndexOf('--');
if (lastDashIndex < 0) {
@ -216,11 +193,11 @@ function decode(codedStr) {
codedStr = '-' + codedStr.slice(0, -1);
lastDashIndex += 1;
}
let unsafeStream = /** @type {DashyStream} */ ({
let unsafeStream: DashyStream = {
codeBuf: codedStr.slice(lastDashIndex + 2),
buf: 0x0,
bufLength: 0,
});
};
/**
* Status:
* 1 : awaiting next read
@ -289,10 +266,7 @@ function decode(codedStr) {
return str;
}
/**
* @param {string} codeBuf
*/
function vizStream(codeBuf, translate = true) {
export function vizStream(codeBuf: string, translate: boolean = true) {
let spacedStream = '';
if (codeBuf.charAt(0) === '0') {
codeBuf = codeBuf.slice(1);
@ -302,16 +276,18 @@ function vizStream(codeBuf, translate = true) {
codeBuf = codeBuf.slice(0, -1);
spacedStream = ' [start unsafe]' + spacedStream;
}
let stream = /** @type {DashyStream} */ ({
let stream: DashyStream = {
codeBuf,
buf: 0x0,
bufLength: 0,
});
function vizBlock(/** @type {DashyStream} */ stream, /** @type {number} */ bufLen) {
const buf = streamRead(stream, bufLen);
};
function vizBlock(s: DashyStream, bufLen: number) {
const buf = streamRead(s, bufLen);
// @ts-ignore
return buf.toString(2).padStart(bufLen, '0');
}
while (stream.bufLength > 0 || stream.codeBuf) {
switch (streamRead(stream, 2)) {
case 0x0:
@ -338,9 +314,3 @@ function vizStream(codeBuf, translate = true) {
}
return spacedStream;
}
module.exports = {
encode,
decode,
vizStream,
};

View File

@ -23,71 +23,64 @@
'use strict';
const pathModule = require('path');
const fs = require('fs');
const Streams = require('./streams');
const WriteStream = Streams.WriteStream;
import * as fs from 'fs';
import * as pathModule from 'path';
import { ReadStream, WriteStream } from './streams';
const ROOT_PATH = pathModule.resolve(__dirname, '..');
/*eslint no-unused-expressions: ["error", { "allowTernary": true }]*/
interface PendingUpdate {
isWriting: boolean; // true: waiting on a call to FS.write, false: waiting on a throttle
pendingDataFetcher: (() => string | Buffer) | null;
pendingOptions: object | null;
throttleTime: number; // throttling until time (0 for no throttle)
throttleTimer: NodeJS.Timer | null;
}
const pendingUpdates: Map<string, PendingUpdate> = new Map();
class FSPath {
/**
* @param {string} path
*/
constructor(path) {
path: string;
constructor(path: string) {
this.path = pathModule.resolve(ROOT_PATH, path);
}
parentDir() {
return new FSPath(pathModule.dirname(this.path));
}
/**
* @param {AnyObject | string} [options]
* @return {Promise<string>}
*/
read(options = 'utf8') {
read(options: AnyObject | string = 'utf8'): Promise<string> {
if (typeof options !== 'string' && options.encoding === undefined) {
options.encoding = 'utf8';
}
return new Promise((resolve, reject) => {
fs.readFile(this.path, options, (err, data) => {
err ? reject(err) : resolve(/** @type {string} */ (data));
err ? reject(err) : resolve(data as string);
});
});
}
/**
* @param {AnyObject | string} [options]
* @return {string}
*/
readSync(/** @type {AnyObject | string} */ options = 'utf8') {
readSync(options: AnyObject | string = 'utf8'): string {
if (typeof options !== 'string' && options.encoding === undefined) {
options.encoding = 'utf8';
}
return /** @type {string} */ (fs.readFileSync(this.path, options));
return fs.readFileSync(this.path, options) as string;
}
/**
* @param {AnyObject | string} [options]
* @return {Promise<Buffer>}
*/
readBuffer(/** @type {AnyObject | string} */ options = {}) {
readBuffer(options: AnyObject | string = {}): Promise<Buffer> {
return new Promise((resolve, reject) => {
fs.readFile(this.path, options, (err, data) => {
err ? reject(err) : resolve(/** @type {Buffer} */ (data));
err ? reject(err) : resolve(data as Buffer);
});
});
}
/**
* @param {AnyObject | string} [options]
* @return {Buffer}
*/
readBufferSync(/** @type {AnyObject | string} */ options = {}) {
return /** @type {Buffer} */ (fs.readFileSync(this.path, options));
readBufferSync(options: AnyObject | string = {}) {
return fs.readFileSync(this.path, options) as Buffer;
}
/**
* @return {Promise<string>}
*/
readIfExists() {
readIfExists(): Promise<string> {
return new Promise((resolve, reject) => {
fs.readFile(this.path, 'utf8', (err, data) => {
if (err && err.code === 'ENOENT') return resolve('');
@ -95,6 +88,7 @@ class FSPath {
});
});
}
readIfExistsSync() {
try {
return fs.readFileSync(this.path, 'utf8');
@ -103,11 +97,8 @@ class FSPath {
}
return '';
}
/**
* @param {string | Buffer} data
* @param {Object} options
*/
write(data, options = {}) {
write(data: string | Buffer, options: object = {}) {
if (Config.nofswriting) return Promise.resolve();
return new Promise((resolve, reject) => {
fs.writeFile(this.path, data, options, err => {
@ -115,44 +106,34 @@ class FSPath {
});
});
}
/**
* @param {string | Buffer} data
* @param {Object} options
*/
writeSync(data, options = {}) {
writeSync(data: string | Buffer, options: object = {}) {
if (Config.nofswriting) return;
return fs.writeFileSync(this.path, data, options);
}
/**
* Writes to a new file before renaming to replace an old file. If
* the process crashes while writing, the old file won't be lost.
* Does not protect against simultaneous writing; use writeUpdate
* for that.
*
* @param {string | Buffer} data
* @param {Object} options
*/
async safeWrite(data, options = {}) {
async safeWrite(data: string | Buffer, options: object = {}) {
await FS(this.path + '.NEW').write(data, options);
await FS(this.path + '.NEW').rename(this.path);
}
/**
* @param {string | Buffer} data
* @param {Object} options
*/
safeWriteSync(data, options = {}) {
safeWriteSync(data: string | Buffer, options: object = {}) {
FS(this.path + '.NEW').writeSync(data, options);
FS(this.path + '.NEW').renameSync(this.path);
}
/**
* @param {number} time
* @return {Promise<void>}
*/
waitUntil(time) {
waitUntil(time: number): Promise<void> {
return new Promise(resolve => {
setTimeout(() => resolve(), time - Date.now());
});
}
/**
* Safest way to update a file with in-memory state. Pass a callback
* that fetches the data to be written. It will write an update,
@ -164,15 +145,12 @@ class FSPath {
*
* No synchronous version because there's no risk of race conditions
* with synchronous code; just use `safeWriteSync`.
*
* @param {() => string | Buffer} dataFetcher
* @param {Object} options
*/
writeUpdate(dataFetcher, options = {}) {
writeUpdate(dataFetcher: () => string | Buffer, options: object = {}) {
if (Config.nofswriting) return;
/** @type {PendingUpdate | undefined} */
let pendingUpdate = FS.pendingUpdates.get(this.path);
let pendingUpdate: PendingUpdate | undefined = pendingUpdates.get(this.path);
// @ts-ignore
const throttleTime = options.throttle ? Date.now() + options.throttle : 0;
if (pendingUpdate) {
@ -188,38 +166,36 @@ class FSPath {
this.writeUpdateNow(dataFetcher, options);
}
/**
* @param {() => string | Buffer} dataFetcher
* @param {Object} options
*/
writeUpdateNow(dataFetcher, options) {
writeUpdateNow(dataFetcher: () => string | Buffer, options: object) {
// @ts-ignore
const throttleTime = options.throttle ? Date.now() + options.throttle : 0;
const update = {
isWriting: true,
pendingDataFetcher: null,
pendingOptions: null,
throttleTime: throttleTime,
throttleTime,
throttleTimer: null,
};
FS.pendingUpdates.set(this.path, update);
pendingUpdates.set(this.path, update);
this.safeWrite(dataFetcher(), options).then(() => this.finishUpdate());
}
checkNextUpdate() {
let pendingUpdate = FS.pendingUpdates.get(this.path);
let pendingUpdate = pendingUpdates.get(this.path);
if (!pendingUpdate) throw new Error(`FS: Pending update not found`);
if (pendingUpdate.isWriting) throw new Error(`FS: Conflicting update`);
const {pendingDataFetcher: dataFetcher, pendingOptions: options} = pendingUpdate;
if (!dataFetcher || !options) {
// no pending update
FS.pendingUpdates.delete(this.path);
pendingUpdates.delete(this.path);
return;
}
this.writeUpdateNow(dataFetcher, options);
}
finishUpdate() {
let pendingUpdate = FS.pendingUpdates.get(this.path);
let pendingUpdate = pendingUpdates.get(this.path);
if (!pendingUpdate) throw new Error(`FS: Pending update not found`);
if (!pendingUpdate.isWriting) throw new Error(`FS: Conflicting update`);
@ -232,11 +208,8 @@ class FSPath {
pendingUpdate.throttleTimer = setTimeout(() => this.checkNextUpdate(), throttleTime - Date.now());
}
/**
* @param {string | Buffer} data
* @param {Object} options
*/
append(data, options = {}) {
append(data: string | Buffer, options: object = {}) {
if (Config.nofswriting) return Promise.resolve();
return new Promise((resolve, reject) => {
fs.appendFile(this.path, data, options, err => {
@ -244,18 +217,13 @@ class FSPath {
});
});
}
/**
* @param {string | Buffer} data
* @param {Object} options
*/
appendSync(data, options = {}) {
appendSync(data: string | Buffer, options: object = {}) {
if (Config.nofswriting) return;
return fs.appendFileSync(this.path, data, options);
}
/**
* @param {string} target
*/
symlinkTo(target) {
symlinkTo(target: string) {
if (Config.nofswriting) return Promise.resolve();
return new Promise((resolve, reject) => {
fs.symlink(target, this.path, err => {
@ -263,17 +231,13 @@ class FSPath {
});
});
}
/**
* @param {string} target
*/
symlinkToSync(target) {
symlinkToSync(target: string) {
if (Config.nofswriting) return;
return fs.symlinkSync(target, this.path);
}
/**
* @param {string} target
*/
rename(target) {
rename(target: string) {
if (Config.nofswriting) return Promise.resolve();
return new Promise((resolve, reject) => {
fs.rename(this.path, target, err => {
@ -281,30 +245,29 @@ class FSPath {
});
});
}
/**
* @param {string} target
*/
renameSync(target) {
renameSync(target: string) {
if (Config.nofswriting) return;
return fs.renameSync(this.path, target);
}
readdir() {
readdir(): Promise<string[]> {
return new Promise((resolve, reject) => {
fs.readdir(this.path, (err, data) => {
err ? reject(err) : resolve(data);
});
});
}
readdirSync() {
return fs.readdirSync(this.path);
}
createReadStream() {
return new FileReadStream(this.path);
}
/**
* @return {WriteStream}
*/
createWriteStream(options = {}) {
createWriteStream(options = {}): WriteStream {
if (Config.nofswriting) {
// @ts-ignore
return new WriteStream({write() {}});
@ -312,10 +275,8 @@ class FSPath {
// @ts-ignore
return new WriteStream(fs.createWriteStream(this.path, options));
}
/**
* @return {WriteStream}
*/
createAppendStream(options = {}) {
createAppendStream(options = {}): WriteStream {
if (Config.nofswriting) {
// @ts-ignore
return new WriteStream({write() {}});
@ -325,6 +286,7 @@ class FSPath {
// @ts-ignore
return new WriteStream(fs.createWriteStream(this.path, options));
}
unlinkIfExists() {
if (Config.nofswriting) return Promise.resolve();
return new Promise((resolve, reject) => {
@ -334,6 +296,7 @@ class FSPath {
});
});
}
unlinkIfExistsSync() {
if (Config.nofswriting) return;
try {
@ -342,10 +305,8 @@ class FSPath {
if (err.code !== 'ENOENT') throw err;
}
}
/**
* @param {string | number} mode
*/
mkdir(mode = 0o755) {
mkdir(mode: string | number = 0o755) {
if (Config.nofswriting) return Promise.resolve();
return new Promise((resolve, reject) => {
fs.mkdir(this.path, mode, err => {
@ -353,17 +314,13 @@ class FSPath {
});
});
}
/**
* @param {string | number} mode
*/
mkdirSync(mode = 0o755) {
mkdirSync(mode: string | number = 0o755) {
if (Config.nofswriting) return;
return fs.mkdirSync(this.path, mode);
}
/**
* @param {string | number} mode
*/
mkdirIfNonexistent(mode = 0o755) {
mkdirIfNonexistent(mode: string | number = 0o755) {
if (Config.nofswriting) return Promise.resolve();
return new Promise((resolve, reject) => {
fs.mkdir(this.path, mode, err => {
@ -372,10 +329,8 @@ class FSPath {
});
});
}
/**
* @param {string | number} mode
*/
mkdirIfNonexistentSync(mode = 0o755) {
mkdirIfNonexistentSync(mode: string | number = 0o755) {
if (Config.nofswriting) return;
try {
fs.mkdirSync(this.path, mode);
@ -383,12 +338,12 @@ class FSPath {
if (err.code !== 'EEXIST') throw err;
}
}
/**
* Creates the directory (and any parent directories if necessary).
* Does not throw if the directory already exists.
* @param {string | number} mode
*/
async mkdirp(mode = 0o755) {
async mkdirp(mode: string | number = 0o755) {
try {
await this.mkdirIfNonexistent(mode);
} catch (err) {
@ -397,12 +352,12 @@ class FSPath {
await this.mkdirIfNonexistent(mode);
}
}
/**
* Creates the directory (and any parent directories if necessary).
* Does not throw if the directory already exists. Synchronous.
* @param {string | number} mode
*/
mkdirpSync(mode = 0o755) {
mkdirpSync(mode: string | number = 0o755) {
try {
this.mkdirIfNonexistentSync(mode);
} catch (err) {
@ -411,36 +366,33 @@ class FSPath {
this.mkdirIfNonexistentSync(mode);
}
}
/**
* Calls the callback if the file is modified.
* @param {function (): void} callback
*/
onModify(callback) {
/** Calls the callback if the file is modified. */
onModify(callback: () => void) {
fs.watchFile(this.path, (curr, prev) => {
if (curr.mtime > prev.mtime) return callback();
});
}
/**
* Clears callbacks added with onModify()
*/
/** Clears callbacks added with onModify(). */
unwatch() {
fs.unwatchFile(this.path);
}
}
class FileReadStream extends Streams.ReadStream {
/**
* @param {string} file
*/
constructor(file) {
class FileReadStream extends ReadStream {
fd: Promise<number>;
constructor(file: string) {
super();
/** @type {Promise<number>} */
this.fd = new Promise((resolve, reject) => {
fs.open(file, 'r', (err, fd) => err ? reject(err) : resolve(fd));
});
this.atEOF = false;
}
_read(size = 16384) {
// @ts-ignore
_read(size: number = 16384) {
return new Promise((resolve, reject) => {
if (this.atEOF) return resolve(false);
this.ensureCapacity(size);
@ -460,38 +412,20 @@ class FileReadStream extends Streams.ReadStream {
});
});
}
_destroy() {
return /** @type {Promise<void>} */ (new Promise(resolve => {
return new Promise(resolve => {
this.fd.then(fd => {
fs.close(fd, () => resolve());
});
}));
});
}
}
/**
* @param {string} path
*/
function getFs(path) {
function getFs(path: string) {
return new FSPath(path);
}
/**
* @typedef {object} PendingUpdate
* @property {boolean} isWriting true: waiting on a call to FS.write, false: waiting on a throttle
* @property {(() => string | Buffer)?} pendingDataFetcher
* @property {Object?} pendingOptions
* @property {number} throttleTime throttling until time (0 for no throttle)
* @property {NodeJS.Timer?} throttleTimer
*/
const FS = Object.assign(getFs, {
export const FS = Object.assign(getFs, {
FileReadStream,
/**
* @type {Map<string, PendingUpdate>}
*/
pendingUpdates: new Map(),
});
module.exports = FS;

View File

@ -9,25 +9,23 @@
* @license MIT
*/
'use strict';
import * as child_process from 'child_process';
import * as path from 'path';
import * as Streams from './streams';
const path = require('path');
const child_process = require('child_process');
const Streams = require('./streams');
const ROOT_DIR = path.resolve(__dirname, '..');
export const processManagers: ProcessManager[] = [];
export const disabled = false;
class SubprocessStream extends Streams.ObjectReadWriteStream {
/**
* @param {ChildProcess} process
* @param {number} taskId
*/
constructor(process, taskId) {
constructor(public process: ChildProcess, public taskId: number) {
super();
this.process = process;
this.taskId = taskId;
this.process.send(`${taskId}\nNEW`);
}
_write(/** @type {string} */ message) {
_write(message: string) {
if (!this.process.connected) return;
this.process.send(`${this.taskId}\nWRITE\n${message}`);
// responses are handled in ProcessWrapper
@ -38,27 +36,27 @@ class SubprocessStream extends Streams.ObjectReadWriteStream {
}
}
/**
* @typedef {Object} ProcessWrapper
* @property {number} load
* @property {() => Promise<void>} release
*/
interface ProcessWrapper {
load: number;
release: () => Promise<void>;
}
/**
* Wraps the process object in the PARENT process
*/
class QueryProcessWrapper {
constructor(/** @type {string} */ file) {
/** Wraps the process object in the PARENT process. */
export class QueryProcessWrapper {
process: ChildProcess;
taskId: number;
pendingTasks: Map<number, (resp: string) => void>;
pendingRelease: Promise<void> | null;
resolveRelease: (() => void) | null;
constructor(file: string) {
this.process = child_process.fork(file, [], {cwd: ROOT_DIR});
this.taskId = 0;
/** @type {Map<number, (resp: string) => void>} */
this.pendingTasks = new Map();
/** @type {Promise<void>?} */
this.pendingRelease = null;
/** @type {(() => void)?} */
this.resolveRelease = null;
this.process.on('message', /** @param {string} message */ message => {
this.process.on('message', (message: string) => {
const nlLoc = message.indexOf('\n');
if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
if (message.slice(0, nlLoc) === 'THROW') {
@ -67,7 +65,7 @@ class QueryProcessWrapper {
throw error;
}
const taskId = parseInt(message.slice(0, nlLoc));
const taskId = parseInt(message.slice(0, nlLoc), 10);
const resolve = this.pendingTasks.get(taskId);
if (!resolve) throw new Error(`Invalid taskId ${message.slice(0, nlLoc)}`);
this.pendingTasks.delete(taskId);
@ -84,11 +82,7 @@ class QueryProcessWrapper {
return this.pendingTasks.size;
}
/**
* @param {any} input
* @return {Promise<any>}
*/
query(input) {
query(input: any): Promise<any> {
this.taskId++;
const taskId = this.taskId;
this.process.send(`${taskId}\n${JSON.stringify(input)}`);
@ -97,10 +91,7 @@ class QueryProcessWrapper {
});
}
/**
* @return {Promise<void>}
*/
release() {
release(): Promise<void> {
if (this.pendingRelease) return this.pendingRelease;
if (!this.load) {
this.destroy();
@ -109,7 +100,7 @@ class QueryProcessWrapper {
this.resolveRelease = resolve;
});
}
return /** @type {Promise<void>} */ (this.pendingRelease);
return this.pendingRelease as Promise<void>;
}
destroy() {
@ -132,21 +123,22 @@ class QueryProcessWrapper {
}
}
/**
* Wraps the process object in the PARENT process
*/
class StreamProcessWrapper {
constructor(/** @type {string} */ file) {
/** Wraps the process object in the PARENT process. */
export class StreamProcessWrapper {
process: ChildProcess;
taskId: number;
activeStreams: Map<number, SubprocessStream>;
pendingRelease: Promise<void> | null;
resolveRelease: (() => void) | null;
constructor(file: string) {
this.process = child_process.fork(file, [], {cwd: ROOT_DIR});
this.taskId = 0;
/** @type {Map<number, SubprocessStream>} */
this.activeStreams = new Map();
/** @type {Promise<void>?} */
this.pendingRelease = null;
/** @type {(() => void)?} */
this.resolveRelease = null;
this.process.on('message', /** @param {string} message */ message => {
this.process.on('message', (message: string) => {
let nlLoc = message.indexOf('\n');
if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
if (message.slice(0, nlLoc) === 'THROW') {
@ -155,7 +147,7 @@ class StreamProcessWrapper {
throw error;
}
const taskId = parseInt(message.slice(0, nlLoc));
const taskId = parseInt(message.slice(0, nlLoc), 10);
const stream = this.activeStreams.get(taskId);
if (!stream) throw new Error(`Invalid taskId ${message.slice(0, nlLoc)}`);
@ -183,7 +175,7 @@ class StreamProcessWrapper {
});
}
deleteStream(/** @type {number} */ taskId) {
deleteStream(taskId: number) {
this.activeStreams.delete(taskId);
// try to release
if (this.resolveRelease && !this.load) this.destroy();
@ -193,10 +185,7 @@ class StreamProcessWrapper {
return this.activeStreams.size;
}
/**
* @return {SubprocessStream}
*/
createStream() {
createStream(): SubprocessStream {
this.taskId++;
const taskId = this.taskId;
const stream = new SubprocessStream(this.process, taskId);
@ -204,10 +193,7 @@ class StreamProcessWrapper {
return stream;
}
/**
* @return {Promise<void>}
*/
release() {
release(): Promise<void> {
if (this.pendingRelease) return this.pendingRelease;
if (!this.load) {
this.destroy();
@ -216,7 +202,7 @@ class StreamProcessWrapper {
this.resolveRelease = resolve;
});
}
return /** @type {Promise<void>} */ (this.pendingRelease);
return this.pendingRelease as Promise<void>;
}
destroy() {
@ -242,19 +228,22 @@ class StreamProcessWrapper {
* A ProcessManager wraps a query function: A function that takes a
* string and returns a string or Promise<string>.
*/
class ProcessManager {
/**
* @param {NodeJS.Module} module
*/
constructor(module) {
/** @type {ProcessWrapper[]} */
export class ProcessManager {
processes: ProcessWrapper[];
releasingProcesses: ProcessWrapper[];
// @ts-ignore
module: NodeJs.Module;
filename: string;
basename: string;
isParentProcess: boolean;
// @ts-ignore
constructor(module: NodeJs.Module) {
this.processes = [];
/** @type {ProcessWrapper[]} */
this.releasingProcesses = [];
this.module = module;
this.filename = module.filename;
this.basename = path.basename(module.filename);
this.isParentProcess = (process.mainModule !== module || !process.send);
this.listen();
@ -285,48 +274,41 @@ class ProcessManager {
}
spawn(count = 1) {
if (!this.isParentProcess) return;
if (PMLib.disabled) return;
if (disabled) return;
while (this.processes.length < count) {
this.processes.push(this.createProcess());
}
}
respawn(/** @type {number?} */ count = null) {
respawn(count: number | null = null) {
if (count === null) count = this.processes.length;
this.unspawn();
this.spawn(count);
}
/**
* @return {ProcessWrapper}
*/
createProcess() {
createProcess(): ProcessWrapper {
throw new Error(`implemented by subclass`);
}
listen() {
throw new Error(`implemented by subclass`);
}
destroy() {
const index = PMLib.processManagers.indexOf(this);
if (index) PMLib.processManagers.splice(index, 1);
const index = processManagers.indexOf(this);
if (index) processManagers.splice(index, 1);
this.unspawn();
}
}
class QueryProcessManager extends ProcessManager {
/**
* @param {NodeJS.Module} module
* @param {(input: any) => any} query
*/
constructor(module, query) {
export class QueryProcessManager extends ProcessManager {
// tslint:disable-next-line:variable-name
_query: (input: any) => any;
constructor(module: NodeJS.Module, query: (input: any) => any) {
super(module);
this._query = query;
PMLib.processManagers.push(this);
processManagers.push(this);
}
/**
* @param {any} input
*/
query(input) {
const process = /** @type {QueryProcessWrapper} */ (this.acquire());
query(input: any) {
const process = this.acquire() as QueryProcessWrapper;
if (!process) return Promise.resolve(this._query(input));
return process.query(input);
}
@ -336,15 +318,17 @@ class QueryProcessManager extends ProcessManager {
listen() {
if (this.isParentProcess) return;
// child process
process.on('message', async (/** @type {string} */ message) => {
process.on('message', async (message: string) => {
let nlLoc = message.indexOf('\n');
if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
const taskId = message.slice(0, nlLoc);
message = message.slice(nlLoc + 1);
if (taskId.startsWith('EVAL')) {
/* tslint:disable:no-eval */
// @ts-ignore guaranteed to be defined here
process.send(`${taskId}\n` + eval(message));
/* tslint:enable:no-eval */
return;
}
@ -357,37 +341,36 @@ class QueryProcessManager extends ProcessManager {
});
}
}
class StreamProcessManager extends ProcessManager {
/**
* @param {NodeJS.Module} module
* @param {() => ObjectReadWriteStream} createStream
*/
constructor(module, createStream) {
export class StreamProcessManager extends ProcessManager {
/* taskid: stream used only in child process */
activeStreams: Map<string, Streams.ObjectReadWriteStream>;
// tslint:disable-next-line:variable-name
_createStream: () => Streams.ObjectReadWriteStream;
constructor(module: NodeJS.Module, createStream: () => Streams.ObjectReadWriteStream) {
super(module);
/** @type {Map<string, ObjectReadWriteStream>} taskid: stream used only in child process */
this.activeStreams = new Map();
this._createStream = createStream;
PMLib.processManagers.push(this);
processManagers.push(this);
}
createStream() {
const process = /** @type {StreamProcessWrapper} */ (this.acquire());
const process = this.acquire() as StreamProcessWrapper;
if (!process) return this._createStream();
return process.createStream();
}
createProcess() {
return new StreamProcessWrapper(this.filename);
}
/**
* @param {string} taskId
* @param {ObjectReadStream} stream
*/
async pipeStream(taskId, stream) {
async pipeStream(taskId: string, stream: Streams.ObjectReadStream) {
/* tslint:disable */
let value, done;
while (({value, done} = await stream.next(), !done)) {
// @ts-ignore Guaranteed to be a child process
process.send(`${taskId}\nPUSH\n${value}`);
}
/* tslint:enable */
// @ts-ignore Guaranteed to be a child process
process.send(`${taskId}\nEND`);
this.activeStreams.delete(taskId);
@ -395,7 +378,7 @@ class StreamProcessManager extends ProcessManager {
listen() {
if (this.isParentProcess) return;
// child process
process.on('message', async (/** @type {string} */ message) => {
process.on('message', async (message: string) => {
let nlLoc = message.indexOf('\n');
if (nlLoc <= 0) throw new Error(`Invalid request ${message}`);
const taskId = message.slice(0, nlLoc);
@ -408,8 +391,10 @@ class StreamProcessManager extends ProcessManager {
message = message.slice(nlLoc + 1);
if (taskId.startsWith('EVAL')) {
/* tslint:disable:no-eval */
// @ts-ignore guaranteed to be a child process
process.send(`${taskId}\n` + eval(message));
/* tslint:enable:no-eval */
return;
}
@ -434,17 +419,3 @@ class StreamProcessManager extends ProcessManager {
});
}
}
const PMLib = {
QueryProcessWrapper,
StreamProcessWrapper,
ProcessManager,
QueryProcessManager,
StreamProcessManager,
/** @type {ProcessManager[]} */
processManagers: [],
disabled: false,
};
// @ts-ignore Typescript bug
module.exports = PMLib;

View File

@ -8,21 +8,18 @@
* @license MIT
*/
'use strict';
import * as fs from 'fs';
import * as net from 'net';
import * as path from 'path';
import * as repl from 'repl';
const fs = require('fs');
const path = require('path');
const net = require('net');
const repl = require('repl');
const Repl = {
export const Repl = new class {
/**
* Contains the pathnames of all active REPL sockets.
* @type {Set<string>}
*/
socketPathnames: new Set(),
socketPathnames: Set<string> = new Set();
listenersSetup: false,
listenersSetup: boolean = false;
setupListeners() {
if (Repl.listenersSetup) return;
@ -44,17 +41,14 @@ const Repl = {
if (!process.listeners('SIGINT').length) {
process.once('SIGINT', () => process.exit(128 + 2));
}
},
}
/**
* Starts a REPL server, using a UNIX socket for IPC. The eval function
* parametre is passed in because there is no other way to access a file's
* non-global context.
*
* @param {string} filename
* @param {(input: string) => any} evalFunction
*/
start(filename, evalFunction) {
start(filename: string, evalFunction: (input: string) => any) {
if ('repl' in Config && !Config.repl) return;
// TODO: Windows does support the REPL when using named pipes. For now,
@ -84,14 +78,8 @@ const Repl = {
repl.start({
input: socket,
output: socket,
/**
* @param {string} cmd
* @param {any} context
* @param {string} filename
* @param {Function} callback
* @return {any}
*/
eval(cmd, context, filename, callback) {
// tslint:disable-next-line:ban-types
eval(cmd: string, context: any, unusedFilename: string, callback: Function): any {
try {
return callback(null, evalFunction(cmd));
} catch (e) {
@ -108,8 +96,9 @@ const Repl = {
Repl.socketPathnames.add(pathname);
});
server.once('error', /** @param {NodeJS.ErrnoException} err */ err => {
server.once('error', (err: NodeJS.ErrnoException) => {
if (err.code === "EADDRINUSE") {
// tslint:disable-next-line:variable-name
fs.unlink(pathname, _err => {
if (_err && _err.code !== "ENOENT") {
require('./crashlogger')(_err, `REPL: ${filename}`);
@ -126,7 +115,5 @@ const Repl = {
Repl.socketPathnames.delete(pathname);
Repl.start(filename, evalFunction);
});
},
}
};
module.exports = Repl;

View File

@ -10,33 +10,35 @@
* @license MIT
*/
'use strict';
const BUF_SIZE = 65536 * 4;
class ReadStream {
/** @param {{[k: string]: any} | NodeJS.ReadableStream | string | Buffer} optionsOrStreamLike */
constructor(optionsOrStreamLike = {}) {
export class ReadStream {
buf: Buffer;
bufStart: number;
bufEnd: number;
bufCapacity: number;
readSize: number;
atEOF: boolean;
encoding: string;
isReadable: boolean;
isWritable: boolean;
nodeReadableStream: NodeJS.ReadableStream | null;
nextPushResolver: (() => void) | null;
nextPush: Promise<void>;
awaitingPush: boolean;
constructor(optionsOrStreamLike: {[k: string]: any} | NodeJS.ReadableStream | string | Buffer = {}) {
this.buf = Buffer.allocUnsafe(BUF_SIZE);
this.bufStart = 0;
this.bufEnd = 0;
this.bufCapacity = BUF_SIZE;
// TypeScript bug: can't infer type
/** @type {number} */
this.readSize = 0;
this.atEOF = false;
this.encoding = 'utf8';
/** @type {true} */
this.isReadable = true;
this.isWritable = false;
/** @type {NodeJS.ReadableStream?} */
this.nodeReadableStream = null;
/** @type {(() => void)?} */
this.nextPushResolver = null;
/** @type {Promise<void>} */
this.nextPush = new Promise(resolve => {
this.nextPushResolver = resolve;
});
@ -47,13 +49,13 @@ class ReadStream {
options = {buffer: optionsOrStreamLike};
} else if (optionsOrStreamLike instanceof Buffer) {
options = {buffer: optionsOrStreamLike};
} else if (typeof /** @type {any} */ (optionsOrStreamLike)._readableState === 'object') {
options = {nodeStream: /** @type {NodeJS.ReadableStream} */ (optionsOrStreamLike)};
} else if (typeof (optionsOrStreamLike as any)._readableState === 'object') {
options = {nodeStream: optionsOrStreamLike as NodeJS.ReadableStream};
} else {
options = optionsOrStreamLike;
}
if (options.nodeStream) {
const nodeStream = /** @type {NodeJS.ReadableStream} */ (options.nodeStream);
const nodeStream: NodeJS.ReadableStream = options.nodeStream;
this.nodeReadableStream = nodeStream;
nodeStream.on('data', data => {
this.push(data);
@ -61,19 +63,13 @@ class ReadStream {
nodeStream.on('end', () => {
this.push(null);
});
/**
* @this {ReadStream}
* @param {number} bytes
*/
options.read = function (bytes) {
this.nodeReadableStream.resume();
options.read = function (this: ReadStream, unusedBytes: number) {
this.nodeReadableStream!.resume();
};
/**
* @this {ReadStream}
* @param {number} bytes
*/
options.pause = function (bytes) {
this.nodeReadableStream.pause();
options.pause = function (this: ReadStream, unusedBytes: number) {
this.nodeReadableStream!.pause();
};
}
@ -86,9 +82,11 @@ class ReadStream {
this.push(null);
}
}
get bufSize() {
return this.bufEnd - this.bufStart;
}
moveBuf() {
if (this.bufStart !== this.bufEnd) {
this.buf.copy(this.buf, 0, this.bufStart, this.bufEnd);
@ -96,6 +94,7 @@ class ReadStream {
this.bufEnd -= this.bufStart;
this.bufStart = 0;
}
expandBuf(newCapacity = this.bufCapacity * 2) {
const newBuf = Buffer.allocUnsafe(newCapacity);
this.buf.copy(newBuf, 0, this.bufStart, this.bufEnd);
@ -103,10 +102,8 @@ class ReadStream {
this.bufStart = 0;
this.buf = newBuf;
}
/**
* @param {number} additionalCapacity
*/
ensureCapacity(additionalCapacity) {
ensureCapacity(additionalCapacity: number) {
if (this.bufEnd + additionalCapacity <= this.bufCapacity) return;
const capacity = this.bufEnd - this.bufStart + additionalCapacity;
if (capacity <= this.bufCapacity) {
@ -116,10 +113,8 @@ class ReadStream {
while (newCapacity < capacity) newCapacity *= 2;
this.expandBuf(newCapacity);
}
/**
* @param {Buffer | string | null} buf
*/
push(buf, encoding = this.encoding) {
push(buf: Buffer | string | null, encoding: string = this.encoding) {
let size;
if (this.atEOF) return;
if (buf === null) {
@ -139,6 +134,7 @@ class ReadStream {
if (this.bufSize > this.readSize && size * 2 < this.bufSize) this._pause();
this.resolvePush();
}
resolvePush() {
if (!this.nextPushResolver) throw new Error(`Push after end of read stream`);
this.nextPushResolver();
@ -150,23 +146,18 @@ class ReadStream {
this.nextPushResolver = resolve;
});
}
/**
* @param {number} [size]
* @return {void | Promise<void>}
*/
_read(size = 0) {
_read(size: number = 0): void | Promise<void> {
throw new Error(`ReadStream needs to be subclassed and the _read function needs to be implemented.`);
}
_destroy() {}
_pause() {}
/**
* @param {number?} byteCount
*/
async loadIntoBuffer(byteCount = null) {
async loadIntoBuffer(byteCount: number | null = null) {
if (byteCount === 0) return;
this.readSize = Math.max(byteCount || (this.bufSize + 1), this.readSize);
/** @type {number?} */
let bytes = this.readSize - this.bufSize;
let bytes: number | null = this.readSize - this.bufSize;
if (bytes === Infinity || byteCount === null) bytes = null;
while (!this.atEOF && this.bufSize < this.readSize) {
if (bytes) this._read(bytes);
@ -174,10 +165,8 @@ class ReadStream {
await this.nextPush;
}
}
/**
* @param {number?} byteCount
*/
async peek(byteCount = null, encoding = this.encoding) {
async peek(byteCount: number | null = null, encoding: string = this.encoding) {
if (byteCount === null && this.bufSize) return this.buf.toString(encoding, this.bufStart, this.bufEnd);
await this.loadIntoBuffer(byteCount);
if (byteCount === null) return this.buf.toString(encoding, this.bufStart, this.bufEnd);
@ -185,10 +174,8 @@ class ReadStream {
if (!this.bufSize) return null;
return this.buf.toString(encoding, this.bufStart, this.bufStart + byteCount);
}
/**
* @param {number?} byteCount
*/
async peekBuffer(byteCount = null) {
async peekBuffer(byteCount: number | null = null) {
if (byteCount === null && this.bufSize) return this.buf.slice(this.bufStart, this.bufEnd);
await this.loadIntoBuffer(byteCount);
if (byteCount === null) return this.buf.slice(this.bufStart, this.bufEnd);
@ -196,10 +183,8 @@ class ReadStream {
if (!this.bufSize) return null;
return this.buf.slice(this.bufStart, this.bufStart + byteCount);
}
/**
* @param {number? | string} byteCount
*/
async read(byteCount = null, encoding = this.encoding) {
async read(byteCount: number | string | null = null, encoding = this.encoding) {
if (typeof byteCount === 'string') {
encoding = byteCount;
byteCount = null;
@ -213,10 +198,8 @@ class ReadStream {
}
return out;
}
/**
* @param {number?} byteCount
*/
async readBuffer(byteCount = null) {
async readBuffer(byteCount: number | null = null) {
const out = await this.peekBuffer(byteCount);
if (byteCount === null || byteCount >= this.bufSize) {
this.bufStart = 0;
@ -226,10 +209,8 @@ class ReadStream {
}
return out;
}
/**
* @param {string} symbol
*/
async indexOf(symbol, encoding = this.encoding) {
async indexOf(symbol: string, encoding: string = this.encoding) {
let idx = this.buf.indexOf(symbol, this.bufStart, encoding);
while (!this.atEOF && (idx >= this.bufEnd || idx < 0)) {
await this.loadIntoBuffer();
@ -238,16 +219,16 @@ class ReadStream {
if (idx >= this.bufEnd) return -1;
return idx - this.bufStart;
}
async readAll(encoding = this.encoding) {
return (await this.read(Infinity, encoding)) || '';
}
peekAll(encoding = this.encoding) {
return this.peek(Infinity, encoding);
}
/**
* @param {string} symbol
*/
async readDelimitedBy(symbol, encoding = this.encoding) {
async readDelimitedBy(symbol: string, encoding: string = this.encoding) {
if (this.atEOF && !this.bufSize) return null;
const idx = await this.indexOf(symbol, encoding);
if (idx < 0) {
@ -258,12 +239,14 @@ class ReadStream {
return out;
}
}
async readLine(encoding = this.encoding) {
if (!encoding) throw new Error(`readLine must have an encoding`);
let line = await this.readDelimitedBy('\n', encoding);
if (line && line.endsWith('\r')) line = line.slice(0, -1);
return line;
}
async destroy() {
this.atEOF = true;
this.bufStart = 0;
@ -271,54 +254,52 @@ class ReadStream {
if (this.nextPushResolver) this.resolvePush();
return this._destroy();
}
/**
* @param {string | number | null} [byteCount]
*/
async next(byteCount = null) {
async next(byteCount: string | number | null = null) {
const value = await this.read(byteCount);
return {value, done: value === null};
}
/**
* @param {WriteStream} outStream
* @param {{noEnd?: boolean}} [options]
*/
async pipeTo(outStream, options = {}) {
async pipeTo(outStream: WriteStream, options: {noEnd?: boolean} = {}) {
/* tslint:disable */
let value, done;
while (({value, done} = await this.next(), !done)) {
await outStream.write(value);
}
/* tslint:enable */
if (!options.noEnd) outStream.end();
}
}
class WriteStream {
/**
* @param {{_writableState?: any, nodeStream?: NodeJS.ReadableStream, write?: (this: WriteStream, data: string | Buffer) => (Promise<any> | undefined), end?: () => Promise<any>}} options
*/
constructor(options = {}) {
interface WriteStreamOptions {
writableState?: any;
nodeStream?: NodeJS.ReadableStream;
write?: (this: WriteStream, data: string | Buffer) => (Promise<any> | undefined);
end?: () => Promise<any>;
}
export class WriteStream {
isReadable: boolean;
isWritable: true;
encoding: string;
nodeWritableStream: NodeJS.ReadableStream | null;
drainListeners: (() => void)[];
constructor(options: WriteStreamOptions = {}) {
this.isReadable = false;
/** @type {true} */
this.isWritable = true;
this.encoding = 'utf8';
/** @type {NodeJS.ReadableStream?} */
this.nodeWritableStream = null;
/** @type {(() => void)[]} */
this.drainListeners = [];
if (options._writableState) {
if ((options as any)._writableState) {
// @ts-ignore
options = {nodeStream: options};
}
if (options.nodeStream) {
const nodeStream = /** @type {NodeJS.ReadableStream} */ (options.nodeStream);
const nodeStream: NodeJS.ReadableStream = options.nodeStream;
this.nodeWritableStream = nodeStream;
/**
* @this {WriteStream}
* @param {string | Buffer} data
*/
// @ts-ignore TypeScript bug
options.write = function (data) {
options.write = function (this: WriteStream, data: string | Buffer) {
// @ts-ignore
const result = this.nodeWritableStream.write(data);
if (result !== false) return undefined;
@ -344,11 +325,8 @@ class WriteStream {
if (options.write) this._write = options.write;
if (options.end) this._end = options.end;
}
/**
* @param {Buffer | string | null} chunk
* @return {Promise<boolean>}
*/
async write(chunk) {
async write(chunk: Buffer | string | null): Promise<boolean> {
if (chunk === null) {
await this.end();
return false;
@ -356,31 +334,22 @@ class WriteStream {
await this._write(chunk);
return true;
}
/**
* @param {string | null} chunk
* @return {Promise<boolean>}
*/
async writeLine(chunk) {
async writeLine(chunk: string | null): Promise<boolean> {
if (chunk === null) {
await this.end();
return false;
}
return this.write(chunk + '\n');
}
/**
* @param {Buffer | string} chunk
* @return {void | Promise<void>}
*/
_write(chunk) {
_write(chunk: Buffer | string): void | Promise<void> {
throw new Error(`WriteStream needs to be subclassed and the _write function needs to be implemented.`);
}
/** @return {void | Promise<void>} */
_end() {}
/**
* @param {string | null} chunk
* @return {Promise<void>}
*/
async end(chunk = null) {
_end(): void | Promise<void> {}
async end(chunk: string | null = null): Promise<void> {
if (chunk) {
await this.write(chunk);
}
@ -388,68 +357,54 @@ class WriteStream {
}
}
class ReadWriteStream extends ReadStream {
export class ReadWriteStream extends ReadStream {
constructor(options = {}) {
super(options);
/** @type {true} */
this.isReadable = true;
/** @type {true} */
this.isWritable = true;
}
/**
* @param {Buffer | string} chunk
* @return {Promise<void> | void}
*/
write(chunk) {
write(chunk: Buffer | string): Promise<void> | void {
return this._write(chunk);
}
/**
* @param {string} chunk
* @return {Promise<void> | void}
*/
writeLine(chunk) {
writeLine(chunk: string): Promise<void> | void {
return this.write(chunk + '\n');
}
/**
* @param {Buffer | string} chunk
* @return {Promise<void> | void}
*/
_write(chunk) {
_write(chunk: Buffer | string): Promise<void> | void {
throw new Error(`WriteStream needs to be subclassed and the _write function needs to be implemented.`);
}
/**
* In a ReadWriteStream, _read does not need to be implemented
*/
/** In a ReadWriteStream, _read does not need to be implemented. */
_read() {}
/** @return {void | Promise<void>} */
_end() {}
_end(): void | Promise<void> {}
async end() {
return this._end();
}
}
class ObjectReadStream {
/**
* @param {{[k: string]: any} | NodeJS.ReadableStream | any[]} optionsOrStreamLike
*/
constructor(optionsOrStreamLike = {}) {
/** @type {any[]} */
export class ObjectReadStream {
buf: any[];
readSize: number;
atEOF: boolean;
isReadable: boolean;
isWritable: boolean;
nodeReadableStream: NodeJS.ReadableStream | null;
nextPushResolver: (() => void) | null;
nextPush: Promise<void>;
awaitingPush: boolean;
constructor(optionsOrStreamLike: {[k: string]: any} | NodeJS.ReadableStream | any[] = {}) {
this.buf = [];
// TypeScript bug: can't infer type
/** @type {number} */
this.readSize = 0;
this.atEOF = false;
/** @type {true} */
this.isReadable = true;
this.isWritable = false;
/** @type {NodeJS.ReadableStream?} */
this.nodeReadableStream = null;
/** @type {(() => void)?} */
this.nextPushResolver = null;
/** @type {Promise<void>} */
this.nextPush = new Promise(resolve => {
this.nextPushResolver = resolve;
});
@ -458,13 +413,13 @@ class ObjectReadStream {
let options;
if (Array.isArray(optionsOrStreamLike)) {
options = {buffer: optionsOrStreamLike};
} else if (typeof /** @type {any} */ (optionsOrStreamLike)._readableState === 'object') {
options = {nodeStream: /** @type {NodeJS.ReadableStream} */ (optionsOrStreamLike)};
} else if (typeof (optionsOrStreamLike as any)._readableState === 'object') {
options = {nodeStream: optionsOrStreamLike as NodeJS.ReadableStream};
} else {
options = optionsOrStreamLike;
}
if (options.nodeStream) {
const nodeStream = /** @type {NodeJS.ReadableStream} */ (options.nodeStream);
const nodeStream: NodeJS.ReadableStream = options.nodeStream;
this.nodeReadableStream = nodeStream;
nodeStream.on('data', data => {
this.push(data);
@ -472,19 +427,13 @@ class ObjectReadStream {
nodeStream.on('end', () => {
this.push(null);
});
/**
* @this {ReadStream}
* @param {number} bytes
*/
options.read = function (bytes) {
this.nodeReadableStream.resume();
options.read = function (this: ReadStream, unusedBytes: number) {
this.nodeReadableStream!.resume();
};
/**
* @this {ReadStream}
* @param {number} bytes
*/
options.pause = function (bytes) {
this.nodeReadableStream.pause();
options.pause = function (this: ReadStream, unusedBytes: number) {
this.nodeReadableStream!.pause();
};
}
@ -496,10 +445,8 @@ class ObjectReadStream {
this.push(null);
}
}
/**
* @param {any} elem
*/
push(elem) {
push(elem: any) {
if (this.atEOF) return;
if (elem === null) {
this.atEOF = true;
@ -511,6 +458,7 @@ class ObjectReadStream {
if (this.buf.length > this.readSize && this.buf.length >= 16) this._pause();
this.resolvePush();
}
resolvePush() {
if (!this.nextPushResolver) throw new Error(`Push after end of read stream`);
this.nextPushResolver();
@ -522,19 +470,15 @@ class ObjectReadStream {
this.nextPushResolver = resolve;
});
}
/**
* @param {number} [size]
* @return {void | Promise<void>}
*/
_read(size = 0) {
_read(size: number = 0): void | Promise<void> {
throw new Error(`ReadStream needs to be subclassed and the _read function needs to be implemented.`);
}
_destroy() {}
_pause() {}
/**
* @param {number} count
*/
async loadIntoBuffer(count = 1) {
async loadIntoBuffer(count: number = 1) {
if (this.buf.length >= count) return;
this.readSize = Math.max(count, this.readSize);
while (!this.atEOF && this.buf.length < this.readSize) {
@ -547,81 +491,84 @@ class ObjectReadStream {
}
}
}
async peek() {
if (this.buf.length) return this.buf[0];
await this.loadIntoBuffer();
return this.buf[0];
}
async read() {
if (this.buf.length) return this.buf.shift();
await this.loadIntoBuffer();
if (!this.buf.length) return null;
return this.buf.shift();
}
/**
* @param {number?} [count]
*/
async peekArray(count = null) {
async peekArray(count: number | null = null) {
await this.loadIntoBuffer(count || 1);
if (count === null || count === Infinity) {
return this.buf.slice();
}
return this.buf.slice(0, count);
}
/**
* @param {number?} [count]
*/
async readArray(count = null) {
async readArray(count: number | null = null) {
let out = await this.peekArray(count);
this.buf = this.buf.slice(out.length);
return out;
}
async readAll() {
await this.loadIntoBuffer(Infinity);
let out = this.buf;
this.buf = [];
return out;
}
async peekAll() {
await this.loadIntoBuffer(Infinity);
return this.buf.slice();
}
async destroy() {
this.atEOF = true;
this.buf = [];
this.resolvePush();
return this._destroy();
}
async next() {
const value = await this.read();
return {value, done: value === null};
}
/**
* @param {WriteStream} outStream
* @param {{noEnd?: boolean}} options
*/
async pipeTo(outStream, options = {}) {
async pipeTo(outStream: WriteStream, options: {noEnd?: boolean} = {}) {
/* tslint:disable */
let value, done;
while (({value, done} = await this.next(), !done)) {
await outStream.write(value);
}
/* tslint:enable */
if (!options.noEnd) outStream.end();
}
}
/**
* @template T
*/
class ObjectWriteStream {
/**
* @param {{_writableState?: any, nodeStream?: NodeJS.ReadableStream, write?: (this: WriteStream, data: T) => Promise<any> | undefined, end?: () => Promise<any>}} options
*/
constructor(options = {}) {
this.isReadable = false;
/** @type {true} */
this.isWritable = true;
interface ObjectWriteStreamOptions<T> {
_writableState?: any;
nodeStream?: NodeJS.ReadableStream;
write?: (this: WriteStream, data: T) => Promise<any> | undefined;
end?: () => Promise<any>;
}
/** @type {NodeJS.ReadableStream?} */
export class ObjectWriteStream<T> {
isReadable: boolean;
isWritable: true;
nodeWritableStream: NodeJS.ReadableStream | null;
constructor(options: ObjectWriteStreamOptions<T> = {}) {
this.isReadable = false;
this.isWritable = true;
this.nodeWritableStream = null;
if (options._writableState) {
@ -629,15 +576,12 @@ class ObjectWriteStream {
options = {nodeStream: options};
}
if (options.nodeStream) {
const nodeStream = /** @type {NodeJS.ReadableStream} */ (options.nodeStream);
const nodeStream: NodeJS.ReadableStream = options.nodeStream;
this.nodeWritableStream = nodeStream;
/**
* @this {WriteStream}
* @param {T} data
*/
options.write = function (data) {
options.write = function (this: WriteStream, data: T) {
// @ts-ignore
const result = this.nodeWritableStream.write(data);
const result = this.nodeWritableStream!.write(data);
if (result === false) {
return new Promise(resolve => {
// @ts-ignore
@ -647,6 +591,7 @@ class ObjectWriteStream {
});
}
};
options.end = function () {
return new Promise(resolve => {
// @ts-ignore
@ -658,11 +603,8 @@ class ObjectWriteStream {
if (options.write) this._write = options.write;
if (options.end) this._end = options.end;
}
/**
* @param {T?} elem
* @return {Promise<boolean>}
*/
async write(elem) {
async write(elem: T | null): Promise<boolean> {
if (elem === null) {
await this.end();
return false;
@ -670,20 +612,14 @@ class ObjectWriteStream {
await this._write(elem);
return true;
}
/**
* @param {T} elem
* @return {void | Promise<void>}
*/
_write(elem) {
_write(elem: T): void | Promise<void> {
throw new Error(`WriteStream needs to be subclassed and the _write function needs to be implemented.`);
}
/** @return {void | Promise<void>} */
_end() {}
/**
* @param {T?} elem
* @return {Promise<void>}
*/
async end(elem = null) {
_end(): void | Promise<void> {}
async end(elem: T | null = null): Promise<void> {
if (elem) {
await this.write(elem);
}
@ -691,57 +627,40 @@ class ObjectWriteStream {
}
}
class ObjectReadWriteStream extends ObjectReadStream {
/**
* @param {{write?: (this: WriteStream, data: string) => Promise<any> | undefined | void, end?: () => Promise<any> | undefined | void}} options
*/
constructor(options = {}) {
interface ObjectReadWriteStreamOptions {
write?: (this: WriteStream, data: string) => Promise<any> | undefined | void;
end?: () => Promise<any> | undefined | void;
}
export class ObjectReadWriteStream extends ObjectReadStream {
isReadable: true;
isWritable: true;
constructor(options: ObjectReadWriteStreamOptions = {}) {
super(options);
/** @type {true} */
this.isReadable = true;
/** @type {true} */
this.isWritable = true;
if (options.write) this._write = options.write;
if (options.end) this._end = options.end;
}
/**
* @param {any} elem
* @return {void | Promise<void>}
*/
write(elem) {
write(elem: any): void | Promise<void> {
return this._write(elem);
}
/**
* @param {any} elem
* @return {void | Promise<void>}
*/
_write(elem) {
_write(elem: any): void | Promise<void> {
throw new Error(`WriteStream needs to be subclassed and the _write function needs to be implemented.`);
}
/**
* In a ReadWriteStream, _read does not need to be implemented
*/
/** In a ReadWriteStream, _read does not need to be implemented. */
_read() {}
/** @return {void | Promise<void>} */
_end() {}
_end(): void | Promise<void> {}
async end() {
return this._end();
}
}
module.exports = {
ReadStream,
WriteStream,
ReadWriteStream,
ObjectReadStream,
ObjectWriteStream,
ObjectReadWriteStream,
/**
* @param {NodeJS.ReadableStream} nodeStream
* @param {*} encoding
*/
readAll(nodeStream, encoding = undefined) {
return new ReadStream(nodeStream).readAll(encoding);
},
};
export function readAll(nodeStream: NodeJS.ReadableStream, encoding?: any) {
return new ReadStream(nodeStream).readAll(encoding);
}

View File

@ -5,6 +5,7 @@
"version": "0.11.2",
"dependencies": {
"probe-image-size": "^4.0.0",
"replace": "^1.0.1",
"sockjs": "0.3.18",
"sucrase": "^3.9.5"
},

View File

@ -75,7 +75,7 @@ if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
var Dex = require('./.sim-dist/dex');
var TeamValidator = require('./.sim-dist/team-validator');
var validator = TeamValidator(process.argv[3]);
var Streams = require('./lib/streams');
var Streams = require('./.lib-dist/streams');
var stdin = new Streams.ReadStream(process.stdin);
stdin.readLine().then(function (textTeam) {
@ -97,7 +97,7 @@ if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
case 'simulate-battle':
{
var BattleTextStream = require('./.sim-dist/battle-stream').BattleTextStream;
var Streams = require('./lib/streams');
var Streams = require('./.lib-dist/streams');
var stdin = new Streams.ReadStream(process.stdin);
var stdout = new Streams.WriteStream(process.stdout);
@ -111,7 +111,7 @@ if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
case 'unpack-team':
{
var Dex = require('./.sim-dist/dex');
var Streams = require('./lib/streams');
var Streams = require('./.lib-dist/streams');
var stdin = new Streams.ReadStream(process.stdin);
stdin.readLine().then(function (packedTeam) {
@ -129,7 +129,7 @@ if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
case 'pack-team':
{
var Dex = require('./.sim-dist/dex');
var Streams = require('./lib/streams');
var Streams = require('./.lib-dist/streams');
var stdin = new Streams.ReadStream(process.stdin);
stdin.readLine().then(function (unpackedTeam) {

View File

@ -18,7 +18,7 @@
/* eslint no-else-return: "error" */
const crypto = require('crypto');
const FS = require('../lib/fs');
const FS = require('../.lib-dist/fs').FS;
const MAX_REASON_LENGTH = 300;
const MUTE_LENGTH = 7 * 60 * 1000;
@ -3005,7 +3005,7 @@ const commands = {
Chat.destroy();
const processManagers = require('../lib/process-manager').processManagers;
const processManagers = require('../.lib-dist/process-manager').processManagers;
for (let manager of processManagers.slice()) {
if (manager.filename.startsWith(FS('server/chat-plugins').path)) {
manager.destroy();

View File

@ -1,6 +1,7 @@
'use strict';
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
const MONITOR_FILE = 'config/chat-plugins/chat-monitor.tsv';
const WRITE_THROTTLE_TIME = 5 * 60 * 1000;

View File

@ -1,6 +1,7 @@
'use strict';
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
const DAY = 24 * 60 * 60 * 1000;
const SPOTLIGHT_FILE = 'config/chat-plugins/spotlights.json';

View File

@ -1580,7 +1580,7 @@ function runSearch(query) {
* Process manager
*********************************************************/
const QueryProcessManager = require('../../lib/process-manager').QueryProcessManager;
const QueryProcessManager = require('../../.lib-dist/process-manager').QueryProcessManager;
const PM = new QueryProcessManager(module, async query => {
try {
@ -1599,7 +1599,7 @@ const PM = new QueryProcessManager(module, async query => {
return null;
}
} catch (err) {
require('../../lib/crashlogger')(err, 'A search query', query);
Monitor.crashlog(err, 'A search query', query);
}
return {error: "Sorry! Our search engine crashed on your query. We've been automatically notified and will fix this crash."};
});
@ -1607,10 +1607,22 @@ const PM = new QueryProcessManager(module, async query => {
if (!PM.isParentProcess) {
// This is a child process!
global.Config = require('../../config/config');
// @ts-ignore ???
global.Monitor = {
/**
* @param {Error} error
* @param {string} source
* @param {{}?} details
*/
crashlog(error, source = 'A datasearch process', details = null) {
const repr = JSON.stringify([error.name, error.message, source, details]);
// @ts-ignore
process.send(`THROW\n@!!@${repr}\n${error.stack}`);
},
};
if (Config.crashguard) {
process.on('uncaughtException', err => {
require('../../lib/crashlogger')(err, 'A dexsearch process');
Monitor.crashlog(err, 'A dexsearch process');
});
}
@ -1619,7 +1631,7 @@ if (!PM.isParentProcess) {
Dex.includeData();
global.TeamValidator = require('../../.sim-dist/team-validator').TeamValidator;
require('../../lib/repl').start('dexsearch', cmd => eval(cmd));
require('../../.lib-dist/repl').Repl.start('dexsearch', cmd => eval(cmd));
} else {
PM.spawn(MAX_PROCESSES);
}

View File

@ -1,6 +1,7 @@
'use strict';
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
const TICKET_FILE = 'config/tickets.json';
const TICKET_CACHE_TIME = 24 * 60 * 60 * 1000; // 24 hours
const TICKET_BAN_DURATION = 48 * 60 * 60 * 1000; // 48 hours

View File

@ -1749,8 +1749,8 @@ const commands = {
for (const worker of Sockets.workers.values()) {
buf += `<strong>${worker.pid || worker.process.pid}</strong> - Sockets ${worker.id}<br />`;
}
const processManagers = require('../../lib/process-manager').processManagers;
/** @type {typeof import('../../lib/process-manager').processManagers} */
const processManagers = require(/** @type {any} */('../../.lib-dist/process-manager')).processManagers;
for (const manager of processManagers) {
for (const [i, process] of manager.processes.entries()) {
buf += `<strong>${process.process.pid}</strong> - ${manager.basename} ${i} (load ${process.load})<br />`;

View File

@ -49,7 +49,8 @@
* @property {Object} picks
*/
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
const LOGS_FILE = 'config/chat-plugins/mafia-logs.json';
const BANS_FILE = 'config/chat-plugins/mafia-bans.json';
const MafiaData = require('./mafia-data.js');

View File

@ -15,9 +15,9 @@
'use strict';
const FS = require('../../lib/fs');
const FS = require('../../.lib-dist/fs').FS;
const path = require('path');
const Dashycode = require('../../lib/dashycode');
const Dashycode = require('../../.lib-dist/dashycode');
const util = require('util');
const execFile = util.promisify(require('child_process').execFile);
@ -559,7 +559,7 @@ exports.commands = {
* Process manager
*********************************************************/
const QueryProcessManager = require('../../lib/process-manager').QueryProcessManager;
const QueryProcessManager = require('../../.lib-dist/process-manager').QueryProcessManager;
const PM = new QueryProcessManager(module, async data => {
switch (data.cmd) {
@ -568,7 +568,7 @@ const PM = new QueryProcessManager(module, async data => {
try {
return await runModlog(roomidList, searchString, exactSearch, maxLines);
} catch (err) {
require('../../lib/crashlogger')(err, 'A modlog query', {
Monitor.crashlog(err, 'A modlog query', {
roomidList,
searchString,
exactSearch,
@ -581,7 +581,7 @@ const PM = new QueryProcessManager(module, async data => {
try {
return await runBattleSearch(userid, turnLimit, month, tierid, date);
} catch (err) {
require('../../lib/crashlogger')(err, 'A battle search query', {
Monitor.crashlog(err, 'A battle search query', {
userid,
turnLimit,
month,
@ -597,15 +597,28 @@ const PM = new QueryProcessManager(module, async data => {
if (!PM.isParentProcess) {
// This is a child process!
global.Config = require('../../config/config');
// @ts-ignore ???
global.Monitor = {
/**
* @param {Error} error
* @param {string} source
* @param {{}?} details
*/
crashlog(error, source = 'A modlog process', details = null) {
const repr = JSON.stringify([error.name, error.message, source, details]);
// @ts-ignore
process.send(`THROW\n@!!@${repr}\n${error.stack}`);
},
};
process.on('uncaughtException', err => {
if (Config.crashguard) {
require('../../lib/crashlogger')(err, 'A modlog child process');
Monitor.crashlog(err, 'A modlog child process');
}
});
global.Dex = require('../../.sim-dist/dex');
global.toId = Dex.getId;
require('../../lib/repl').start('modlog', cmd => eval(cmd));
require('../../.lib-dist/repl').Repl.start('modlog', cmd => eval(cmd));
} else {
PM.spawn(MAX_PROCESSES);
}

View File

@ -1,6 +1,7 @@
'use strict';
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
const ROOMFAQ_FILE = 'config/chat-plugins/faqs.json';

View File

@ -10,7 +10,7 @@
'use strict';
const FS = require('../../lib/fs');
const FS = require('../../.lib-dist/fs').FS;
const RATED_TYPES = ['official', 'regular', 'mini'];
const DEFAULT_POINTS = {

View File

@ -1,6 +1,7 @@
'use strict';
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
const DISHES_FILE = 'config/chat-plugins/thecafe-foodfight.json';
const FOODFIGHT_COOLDOWN = 5 * 60 * 1000;

View File

@ -1,6 +1,7 @@
'use strict';
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
const MINUTE = 60 * 1000;
const ROOMIDS = ['thestudio', 'jubilifetvfilms', 'youtube', 'thelibrary', 'prowrestling'];

View File

@ -5,7 +5,8 @@
'use strict';
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
const MAIN_CATEGORIES = {
ae: 'Arts and Entertainment',

View File

@ -6,7 +6,8 @@
'use strict';
const FS = require('../../lib/fs');
/** @type {typeof import('../../lib/fs').FS} */
const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS;
Punishments.roomPunishmentTypes.set('GIVEAWAYBAN', 'banned from giveaways');

View File

@ -55,7 +55,8 @@ const BROADCAST_TOKEN = '!';
const TRANSLATION_DIRECTORY = 'translations/';
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
/** @type {(url: string) => Promise<{width: number, height: number}>} */
// @ts-ignore ignoring until there is a ts typedef available for this module.

View File

@ -19,7 +19,8 @@
const BLOCKLISTS = ['sbl.spamhaus.org', 'rbl.efnetrbl.org'];
const dns = require('dns');
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
let Dnsbl = module.exports;

View File

@ -60,7 +60,7 @@ try {
throw new Error("Dependencies are unmet; run `node build` before launching Pokemon Showdown again.");
}
const FS = require('../lib/fs');
const FS = require('../.lib-dist/fs').FS;
/*********************************************************
* Load configuration
@ -151,4 +151,4 @@ TeamValidatorAsync.PM.spawn();
* Start up the REPL server
*********************************************************/
require('../lib/repl').start('app', cmd => eval(cmd));
require('../.lib-dist/repl').Repl.start('app', cmd => eval(cmd));

View File

@ -15,7 +15,8 @@
'use strict';
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
// ladderCaches = {formatid: ladder OR Promise(ladder)}
// Use Ladders(formatid).ladder to guarantee a Promise(ladder).

View File

@ -15,8 +15,10 @@ const LOGIN_SERVER_BATCH_TIME = 1000;
const http = Config.loginserver.startsWith('http:') ? require("http") : require("https");
const url = require('url');
const FS = require('../lib/fs');
const Streams = require('../lib/streams');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
/** @type {typeof import('../lib/streams')} */
const Streams = require(/** @type {any} */('../.lib-dist/streams'));
/**
* A custom error type used when requests to the login server take too long.

View File

@ -8,7 +8,8 @@
*/
'use strict';
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
const MONITOR_CLEAN_TIMEOUT = 2 * 60 * 60 * 1000;
@ -54,7 +55,8 @@ if (('Config' in global) &&
Config.loglevel = 2;
}
let crashlogger = require('../lib/crashlogger');
/** @type {typeof import('../lib/crashlogger')} */
let crashlogger = require(/** @type {any} */('../.lib-dist/crashlogger'));
const Monitor = module.exports = {
/*********************************************************

View File

@ -15,7 +15,8 @@
let Punishments = module.exports;
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
const PUNISHMENT_FILE = 'config/punishments.tsv';
const ROOM_PUNISHMENT_FILE = 'config/room-punishments.tsv';

View File

@ -13,7 +13,8 @@
'use strict';
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
/** 5 seconds */
const TICK_TIME = 5;
@ -1022,7 +1023,8 @@ exports.RoomBattle = Battle;
* Process manager
*********************************************************/
const StreamProcessManager = require('../lib/process-manager').StreamProcessManager;
/** @type {typeof import('../lib/process-manager').StreamProcessManager} */
const StreamProcessManager = require(/** @type {any} */('../.lib-dist/process-manager')).StreamProcessManager;
const PM = new StreamProcessManager(module, () => {
/** @type {typeof import('../sim/battle-stream').BattleStream} */
@ -1067,7 +1069,9 @@ if (!PM.isParentProcess) {
});
}
require('../lib/repl').start(`sim-${process.pid}`, cmd => eval(cmd));
/** @type {typeof import('../lib/repl').Repl} */
const Repl = require(/** @type {any} */('../.lib-dist/repl')).Repl;
Repl.start(`sim-${process.pid}`, cmd => eval(cmd));
} else {
PM.spawn(global.Config ? Config.simulatorprocesses : 1);
}

View File

@ -9,7 +9,8 @@
'use strict';
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
/**
* Most rooms have three logs:

View File

@ -22,7 +22,8 @@ const LAST_BATTLE_WRITE_THROTTLE = 10;
/** @type {null} */
const RETRY_AFTER_LOGIN = null;
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
const Roomlogs = require('./roomlogs');
/*********************************************************
@ -483,7 +484,7 @@ class GlobalRoom extends BasicRoom {
// Prevent there from being two possible hidden classes an instance
// of GlobalRoom can have.
// @ts-ignore
this.ladderIpLog = new (require('../lib/streams')).WriteStream({write() {}});
this.ladderIpLog = new (require(/** @type {any} */('../.lib-dist/streams'))).WriteStream({write() {}});
}
let lastBattle;

View File

@ -17,7 +17,12 @@ const MINUTES = 60 * 1000;
const cluster = require('cluster');
const fs = require('fs');
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
const Monitor = {
crashlog: require(/** @type {any} */('../.lib-dist/crashlogger')),
};
if (cluster.isMaster) {
cluster.setupMaster({
@ -77,7 +82,7 @@ if (cluster.isMaster) {
workers.delete(worker.id);
} else if (code > 0) {
// Worker was killed abnormally, likely because of a crash.
require('../lib/crashlogger')(new Error(`Worker ${worker.id} abruptly died with code ${code} and signal ${signal}`), "The main process");
Monitor.crashlog(new Error(`Worker ${worker.id} abruptly died with code ${code} and signal ${signal}`), "The main process");
// Don't delete the worker so it can be inspected if need be.
}
@ -281,7 +286,7 @@ if (cluster.isMaster) {
if (Config.crashguard) {
// graceful crash
process.on('uncaughtException', err => {
require('../lib/crashlogger')(err, `Socket process ${cluster.worker.id} (${process.pid})`);
Monitor.crashlog(err, `Socket process ${cluster.worker.id} (${process.pid})`);
});
}
@ -296,7 +301,7 @@ if (cluster.isMaster) {
try {
key = fs.readFileSync(key);
} catch (e) {
require('../lib/crashlogger')(new Error(`Failed to read the configured SSL private key PEM file:\n${e.stack}`), `Socket process ${cluster.worker.id} (${process.pid})`);
Monitor.crashlog(new Error(`Failed to read the configured SSL private key PEM file:\n${e.stack}`), `Socket process ${cluster.worker.id} (${process.pid})`);
}
} catch (e) {
console.warn('SSL private key config values will not support HTTPS server option values in the future. Please set it to use the absolute path of its PEM file.');
@ -310,7 +315,7 @@ if (cluster.isMaster) {
try {
cert = fs.readFileSync(cert);
} catch (e) {
require('../lib/crashlogger')(new Error(`Failed to read the configured SSL certificate PEM file:\n${e.stack}`), `Socket process ${cluster.worker.id} (${process.pid})`);
Monitor.crashlog(new Error(`Failed to read the configured SSL certificate PEM file:\n${e.stack}`), `Socket process ${cluster.worker.id} (${process.pid})`);
}
} catch (e) {
console.warn('SSL certificate config values will not support HTTPS server option values in the future. Please set it to use the absolute path of its PEM file.');
@ -322,7 +327,7 @@ if (cluster.isMaster) {
// In case there are additional SSL config settings besides the key and cert...
appssl = require('https').createServer(Object.assign({}, Config.ssl.options, {key, cert}));
} catch (e) {
require('../lib/crashlogger')(`The SSL settings are misconfigured:\n${e.stack}`, `Socket process ${cluster.worker.id} (${process.pid})`);
Monitor.crashlog(new Error(`The SSL settings are misconfigured:\n${e.stack}`), `Socket process ${cluster.worker.id} (${process.pid})`);
}
}
}
@ -404,7 +409,7 @@ if (cluster.isMaster) {
// @ts-ignore
options.faye_server_options = {extensions: [deflate]};
} catch (e) {
require('../lib/crashlogger')(new Error("Dependency permessage-deflate is not installed or is otherwise unaccessable. No message compression will take place until server restart."), "Sockets");
Monitor.crashlog(new Error("Dependency permessage-deflate is not installed or is otherwise unaccessable. No message compression will take place until server restart."), "Sockets");
}
}
@ -707,5 +712,7 @@ if (cluster.isMaster) {
console.log(`Test your server at http://${Config.bindaddress === '0.0.0.0' ? 'localhost' : Config.bindaddress}:${Config.port}`);
require('../lib/repl').start(`sockets-${cluster.worker.id}-${process.pid}`, cmd => eval(cmd));
/** @type {typeof import('../lib/repl').Repl} */
const Repl = require(/** @type {any} */('../.lib-dist/repl')).Repl;
Repl.start(`sockets-${cluster.worker.id}-${process.pid}`, cmd => eval(cmd));
}

View File

@ -32,9 +32,10 @@ class ValidatorAsync {
* Process manager
*********************************************************/
const QueryProcessManager = require('../lib/process-manager').QueryProcessManager;
/** @type {typeof import('../lib/process-manager').QueryProcessManager} */
const QueryProcessManager = require(/** @type {any} */('../.lib-dist/process-manager')).QueryProcessManager;
/**@type {QueryProcessManager} */
/** @type {QueryProcessManager} */
// @ts-ignore
const PM = new QueryProcessManager(module, async message => {
let {formatid, removeNicknames, team} = message;
@ -44,7 +45,7 @@ const PM = new QueryProcessManager(module, async message => {
try {
problems = TeamValidator(formatid).validateTeam(parsedTeam, removeNicknames);
} catch (err) {
require('../lib/crashlogger')(err, 'A team validation', {
require(/** @type {any} */('../.lib-dist/crashlogger'))(err, 'A team validation', {
formatid: formatid,
team: team,
});
@ -93,7 +94,9 @@ if (!PM.isParentProcess) {
global.toId = Dex.getId;
global.Chat = require('./chat');
require('../lib/repl').start(`team-validator-${process.pid}`, cmd => eval(cmd));
/** @type {typeof import('../lib/repl').Repl} */
const Repl = require(/** @type {any} */('../.lib-dist/repl')).Repl;
Repl.start(`team-validator-${process.pid}`, cmd => eval(cmd));
} else {
PM.spawn(global.Config ? Config.validatorprocesses : 1);
}

View File

@ -38,7 +38,8 @@ const PERMALOCK_CACHE_TIME = 30 * 24 * 60 * 60 * 1000;
const DEFAULT_TRAINER_SPRITES = [1, 2, 101, 102, 169, 170, 265, 266];
const FS = require('../lib/fs');
/** @type {typeof import('../lib/fs').FS} */
const FS = require(/** @type {any} */('../.lib-dist/fs')).FS;
/*********************************************************
* Utility functions

View File

@ -20,7 +20,8 @@ const crypto = require('crypto');
* Process manager
*********************************************************/
const QueryProcessManager = require('../lib/process-manager').QueryProcessManager;
/** @type {typeof import('../lib/process-manager').QueryProcessManager} */
const QueryProcessManager = require(/** @type {any} */('../.lib-dist/process-manager')).QueryProcessManager;
/**@type {QueryProcessManager} */
// @ts-ignore
@ -39,7 +40,9 @@ if (!PM.isParentProcess) {
// This is a child process!
// @ts-ignore This file doesn't exist on the repository, so Travis checks fail if this isn't ignored
global.Config = require('../config/config');
require('../lib/repl').start('verifier', /** @param {string} cmd */ cmd => eval(cmd));
/** @type {typeof import('../lib/repl').Repl} */
const Repl = require(/** @type {any} */('../.lib-dist/repl')).Repl;
Repl.start('verifier', /** @param {string} cmd */ cmd => eval(cmd));
} else {
PM.spawn(global.Config ? Config.verifierprocesses : 1);
}

View File

@ -26,7 +26,7 @@ before('initialization', function () {
} finally {
config = require('../config/config');
}
require('./../lib/process-manager').disabled = true;
require('./../.lib-dist/process-manager').disabled = true;
Object.assign(config, require('../config/config-example'));
// Actually crash if we crash
@ -39,7 +39,7 @@ before('initialization', function () {
config.fakeladder = false;
// Don't create a REPL
require('../lib/repl').start = noop;
require('../.lib-dist/repl').Repl.start = noop;
// Start the server.
require('../server');

View File

@ -11,7 +11,7 @@
},
"types": ["node"],
"exclude": [
"./.sim-dist/*",
"./.*-dist/*",
"./server/index.js",
"./server/chat-commands.js"
],

View File

@ -54,6 +54,6 @@
},
"rulesDirectory": [],
"linterOptions": {
"exclude": ["node_modules/**/*", "**/*.js", "dev-tools/*", ".sim-dist/*"]
"exclude": ["node_modules/**/*", "**/*.js", "dev-tools/*", ".*-dist/*"]
}
}