mirror of
https://github.com/Wessel/pikmin.git
synced 2026-07-27 18:51:15 +02:00
Initial release!
This commit is contained in:
7
lib/colors/index.js
Normal file
7
lib/colors/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const s = require('./styles.js');
|
||||
|
||||
for (const key in s.colors) exports[key] = (val) => { return this.supported ? `\x1b[${s.colors[key]}m${val}\x1b[0m` : val; };
|
||||
for (const key in s.styles) exports[key] = (val) => { return this.supported ? `\x1b[${s.styles[key]}m${val}\x1b[0m` : val; };
|
||||
|
||||
exports.strip = (val) => { return (`${val}`).replace(/\x1B\[\d+m/g, ''); };
|
||||
exports.supported = (process.env.FORCE_COLOR || process.platform === 'win32' || (process.stdout.isTTY && process.env.TERM && process.env.TERM !== 'dumb'));
|
||||
49
lib/colors/styles.js
Normal file
49
lib/colors/styles.js
Normal file
@@ -0,0 +1,49 @@
|
||||
module.exports = {
|
||||
colors: {
|
||||
black: 30,
|
||||
red: 31,
|
||||
green: 32,
|
||||
yellow: 33,
|
||||
blue: 34,
|
||||
magenta: 35,
|
||||
cyan: 36,
|
||||
white: 37,
|
||||
|
||||
bgBlack: 40,
|
||||
bgRed: 41,
|
||||
bgGreen: 42,
|
||||
bgYellow: 43,
|
||||
bgBlue: 44,
|
||||
bgMagenta: 45,
|
||||
bgCyan: 46,
|
||||
bgWhite: 47,
|
||||
|
||||
blackBright: 90,
|
||||
redBright: 91,
|
||||
greenBright: 92,
|
||||
yellowBright: 93,
|
||||
blueBright: 94,
|
||||
magentaBright: 95,
|
||||
cyanBright: 96,
|
||||
whiteBright: 97,
|
||||
|
||||
bgBlackBright: 100,
|
||||
bgRedBright: 101,
|
||||
bgGreenBright: 102,
|
||||
bgYellowBright: 103,
|
||||
bgBlueBright: 104,
|
||||
bgMagentaBright: 105,
|
||||
bgCyanBright: 106,
|
||||
bgWhiteBright: 107
|
||||
},
|
||||
styles: {
|
||||
reset: 0,
|
||||
bold: 1,
|
||||
dim: 2,
|
||||
italic: 3,
|
||||
underline: 4,
|
||||
blink: 5,
|
||||
inverse: 7,
|
||||
strikethrough: 9
|
||||
}
|
||||
};
|
||||
20
lib/pikmin.js
Normal file
20
lib/pikmin.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const pikmin = exports;
|
||||
|
||||
/**
|
||||
* The version of pikmin
|
||||
* @type {string}
|
||||
*/
|
||||
pikmin.version = require('../package.json').version;
|
||||
|
||||
pikmin.bind = require('./pikmin/bindings').bind;
|
||||
pikmin.unbind = require('./pikmin/bindings').unbind;
|
||||
pikmin.instance = require('./pikmin/instance');
|
||||
|
||||
pikmin.colors = require('./colors');
|
||||
pikmin.Collection = require('./util/Collection');
|
||||
|
||||
pikmin.FileTransport = require('./pikmin/transports/File');
|
||||
pikmin.ConsoleTransport = require('./pikmin/transports/Console');
|
||||
pikmin.WebhookTransport = require('./pikmin/transports/Webhook');
|
||||
|
||||
pikmin.loggers = new pikmin.Collection();
|
||||
6
lib/pikmin/PikminError.js
Normal file
6
lib/pikmin/PikminError.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = class PikminError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'PikminError';
|
||||
}
|
||||
};
|
||||
45
lib/pikmin/bindings.js
Normal file
45
lib/pikmin/bindings.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const type = require('../util/type');
|
||||
const Pikmin = require('../pikmin');
|
||||
const PikminError = require('./PikminError');
|
||||
|
||||
exports.bind = (logger, log = console, specific = false) => {
|
||||
if (log && !specific) log.pikmin = {};
|
||||
|
||||
if (type(logger) === 2) {
|
||||
if (!logger.name && !logger.transports && !logger.log) throw new PikminError('The logger you\'ve provided isn\'t valid');
|
||||
|
||||
Pikmin.loggers.get(logger.name).__bound__ = log;
|
||||
if (log && !specific) log.pikmin = Object.assign(log.pikmin, logger.log);
|
||||
else if (specific) log = Object.assign(log, logger.log);
|
||||
} else {
|
||||
if (!Pikmin.loggers.has(logger)) throw new PikminError('The logger you\'ve provided isn\'t valid');
|
||||
|
||||
Pikmin.loggers.get(logger.name).__bound__ = log;
|
||||
if (log && !specific) log.pikmin = Object.assign(log.pikmin, Pikmin.loggers.get(logger));
|
||||
else if (specific) log = Object.assign(log, Pikmin.loggers.get(logger));
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.unbind = (logger, global = true) => {
|
||||
if (type(logger) === 2) {
|
||||
if (!logger.name && !logger.transports && !logger.log) throw new PikminError('The logger you\'ve provided isn\'t valid');
|
||||
|
||||
const log = Pikmin.loggers.get(logger.name);
|
||||
if (global && log.__bound__) {
|
||||
for (const key of Object.keys(log)) delete log.__bound__[key];
|
||||
}
|
||||
|
||||
return Pikmin.loggers.delete(logger.name);
|
||||
} else {
|
||||
if (!Pikmin.loggers.has(logger)) throw new PikminError('The logger you\'ve provided isn\'t valid');
|
||||
|
||||
const log = Pikmin.loggers.get(logger);
|
||||
if (global && log.__bound__) {
|
||||
for (const key of Object.keys(log)) delete log.__bound__[key];
|
||||
}
|
||||
|
||||
return Pikmin.loggers.delete(logger.name);
|
||||
}
|
||||
};
|
||||
130
lib/pikmin/instance.js
Normal file
130
lib/pikmin/instance.js
Normal file
@@ -0,0 +1,130 @@
|
||||
const type = require('../util/type');
|
||||
const Pikmin = require('../pikmin');
|
||||
const { inspect } = require('util');
|
||||
const PikminError = require('./pikminError');
|
||||
|
||||
module.exports = class PikminInstance {
|
||||
/**
|
||||
* Create a pikmin instance
|
||||
*
|
||||
* @param {object} options The options for the instance
|
||||
* @param {string} [options.name='main'] The logger's name
|
||||
* @param {string} [options.format='[%h:%m:%s] %l ->'] The logger's format
|
||||
* @param {boolean} [options.autogen=false] The logger's format
|
||||
* @returns {ThisType} The pikmin instance created
|
||||
*/
|
||||
constructor(options = { autogen: false }) {
|
||||
this.log = {};
|
||||
this.name = this._stringify(options.name || 'main');
|
||||
this.baseFormat = this._stringify(options.format || '[%h:%m:%s] %l ->');
|
||||
this.transports = [];
|
||||
|
||||
if (type(this.name) !== 0) throw new PikminError(`"options.name" must be type of string but received type ${typeof this.name}`);
|
||||
if (Pikmin.loggers.get(this.name)) throw new PikminError(`A logger instance with the name "${this.name}" already exists`);
|
||||
|
||||
if (!options) return this;
|
||||
|
||||
if (type(options) !== 2) throw new TypeError(`"options" must be type of object but received type ${typeof options}`);
|
||||
if (type(options.transports) !== 5) throw new TypeError(`"options.transports" must be type of array but received type ${typeof options.transports}`);
|
||||
|
||||
try {
|
||||
for (const transport of options.transports) {
|
||||
const now = this._formatDate();
|
||||
transport.parent = this.name;
|
||||
|
||||
if (type(transport) !== 2) throw new TypeError(`"data.transports.entry" must be type of object but received ${typeof transport}`);
|
||||
if (transport.name && this[transport.name]) throw new PikminError(`A transporter's name inflicted with an already existing declaration`);
|
||||
|
||||
try {
|
||||
if (transport.type === 'FILE' && options.autogen) transport.append({}, `<PIKMIN_AUTOGEN_LINE<${now.days}/${now.months}/${now.years} ${now.hours}:${now.minutes}:${now.seconds}>>\r\n`);
|
||||
|
||||
this.transports.push(transport);
|
||||
if (transport.name) {
|
||||
this[transport.name] = (msg, options) => this._print(msg, Object.assign(transport.defaults, options), transport.name, transport.format || this.baseFormat);
|
||||
this.log[transport.name] = (msg, options) => this._print(msg, Object.assign(transport.defaults, options), transport.name, transport.format || this.baseFormat);
|
||||
} else transport.permanent = true;
|
||||
} catch(ex) {
|
||||
throw new PikminError(`Failed to write to transporter:\r\n${ex}`);
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
throw new PikminError(ex);
|
||||
}
|
||||
|
||||
Pikmin.loggers.set(this.name, this.log);
|
||||
}
|
||||
|
||||
addTransport(transport, options = { autogen: false }) {
|
||||
const now = this._formatDate();
|
||||
|
||||
if (type(transport) !== 2) throw new TypeError(`"transports" must be type of object but received type ${typeof transport}`);
|
||||
if (transport.name && this[transport.name]) throw new PikminError(`A transporter\'s name inflicted with an already existing declaration`);
|
||||
|
||||
try {
|
||||
if (transport.type === 'FILE' && options.autogen) transport.append(`<PIKMIN_AUTOGEN_LINE<${now.days}/${now.months}/${now.years} ${now.hours}:${now.minutes}:${now.seconds}>>\r\n`);
|
||||
|
||||
this.transports.push(transport);
|
||||
if (transport.name) {
|
||||
this[transport.name] = (msg, opt) => this._print(msg, Object.assign(transport.defaults, opt), transport.name, transport.format || this.baseFormat,);
|
||||
this.log[transport.name] = (msg, opt) => this._print(msg, Object.assign(transport.defaults, opt), transport.name, transport.format || this.baseFormat);
|
||||
} else transport.permanent = true;
|
||||
} catch(ex) {
|
||||
throw new PikminError(`Failed to write to transporter:\r\n${ex}`);
|
||||
}
|
||||
|
||||
Pikmin.loggers.set(this.name, this.log);
|
||||
}
|
||||
|
||||
_print(msg, options = {}, lvl, starter = this.baseFormat) {
|
||||
this.transports
|
||||
.filter((v) => v.name === lvl || v.permanent)
|
||||
.forEach((v) => {
|
||||
let m = msg;
|
||||
let c = false;
|
||||
|
||||
if (type(msg) === 1) m = String(msg);
|
||||
if ((v.defaults.clean && options.clean !== false) || options.clean) {
|
||||
m = this._strip(m);
|
||||
c = true;
|
||||
}
|
||||
if ((v.defaults.inspect && options.inspect !== false) || options.inspect) {
|
||||
if ([ 2, 5 ].some((v) => type(msg) === v)) m = inspect(msg, false, null, !c);
|
||||
}
|
||||
|
||||
v.append(options, `${this._format(starter, lvl, c)} ${m}`);
|
||||
});
|
||||
}
|
||||
|
||||
_format(msg, lvl, clean) {
|
||||
const now = this._formatDate();
|
||||
msg = msg
|
||||
.replace(/%h/g, now.hours)
|
||||
.replace(/%m/g, now.minutes)
|
||||
.replace(/%s/g, now.seconds)
|
||||
.replace(/%l/g, lvl || '');
|
||||
|
||||
if (clean) return this._strip(msg);
|
||||
else return msg;
|
||||
}
|
||||
|
||||
_formatDate(date = new Date()) {
|
||||
return {
|
||||
hours: date.getHours() <= 9 ? `0${date.getHours()}` : date.getHours(),
|
||||
minutes: date.getMinutes() <= 9 ? `0${date.getMinutes()}` : date.getMinutes(),
|
||||
seconds: date.getSeconds() <= 9 ? `0${date.getSeconds()}` : date.getSeconds(),
|
||||
milliseconds: date.getMilliseconds(),
|
||||
|
||||
days: date.getDate() <= 9 ? `0${date.getDate()}` : date.getDate(),
|
||||
years: date.getFullYear(),
|
||||
months: date.getMonth() +1 <= 9 ? `0${date.getMonth() +1}` : date.getMonth() +1
|
||||
};
|
||||
}
|
||||
|
||||
_stringify(string) {
|
||||
if ([ 0, 1, 3, 4, 6, 7, 8, 9 ].some((v) => type(string) === v)) return String(string);
|
||||
if ([ 2, 5 ].some((v) => type(string) === v)) return inspect(string);
|
||||
return String(string);
|
||||
}
|
||||
|
||||
_strip(str) { return (`${str}`).replace(/\x1B\[\d+m/g, ''); }
|
||||
};
|
||||
21
lib/pikmin/transports/Console.js
Normal file
21
lib/pikmin/transports/Console.js
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports = class ConsoleTransport {
|
||||
constructor(options = {}) {
|
||||
if (!options.process) throw new TypeError(`"options.process" must be type of object but received type ${typeof options.process}`);
|
||||
if (!options.process.stdout) throw new TypeError('"options.process" does not have the property "stdout"');
|
||||
|
||||
this.type = 'PROCESS';
|
||||
this.name = typeof options.name === 'string' ? options.name : undefined;
|
||||
this.format = options.format || undefined;
|
||||
this.parent = undefined;
|
||||
this.process = options.process;
|
||||
this.defaults = { inspect: true, ...options.defaults };
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
append(options = this.defaults, data) {
|
||||
this.process.stdout.write(`${data}\r\n`);
|
||||
}
|
||||
|
||||
destroy() { return delete this; }
|
||||
};
|
||||
61
lib/pikmin/transports/File.js
Normal file
61
lib/pikmin/transports/File.js
Normal file
@@ -0,0 +1,61 @@
|
||||
const { createWriteStream, readFileSync, mkdirSync } = require('fs');
|
||||
|
||||
module.exports = class FileTransport {
|
||||
constructor(options = { flags: 'a' }) {
|
||||
let file;
|
||||
|
||||
try {
|
||||
if (/\/\//.test(options.file)) {
|
||||
let prev = './';
|
||||
for (const path of options.file.split('//').slice(0, -1)) {
|
||||
try { mkdirSync(`${prev}/${path}`); }
|
||||
catch (ex) {}
|
||||
prev = `${prev}/${path}`;
|
||||
}
|
||||
}
|
||||
if (/\//.test(options.file)) {
|
||||
let prev = './';
|
||||
for (const path of options.file.split('/').slice(0, -1)) {
|
||||
try { mkdirSync(`${prev}/${path}`); }
|
||||
catch (ex) {}
|
||||
prev = `${prev}/${path}`;
|
||||
}
|
||||
}
|
||||
|
||||
try { file = readFileSync(options.file, { encoding: 'utf8' }); }
|
||||
catch (ex) { file = undefined; }
|
||||
|
||||
this.type = 'FILE';
|
||||
this.stream = createWriteStream(options.file, { flags: options.flags });
|
||||
} catch (ex) {
|
||||
throw new Error(`Unable to access / write to "${options.file}":\r\n${ex}`);
|
||||
}
|
||||
|
||||
this.name = typeof options.name === 'string' ? options.name : undefined;
|
||||
this.parent = undefined;
|
||||
this.format = options.format || undefined;
|
||||
this.defaults = { inspect: true, clean: true, ...options.defaults };
|
||||
|
||||
this.stream.write(file || '');
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
append(options = this.defaults, ...data) {
|
||||
this.stream.write(`${data}\r\n`);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
let trace = { success: false };
|
||||
|
||||
try {
|
||||
this.stream.end();
|
||||
trace.success = true;
|
||||
} catch (ex) {
|
||||
trace.error = ex;
|
||||
trace.success = false;
|
||||
}
|
||||
|
||||
return trace, delete this;
|
||||
}
|
||||
};
|
||||
45
lib/pikmin/transports/Webhook.js
Normal file
45
lib/pikmin/transports/Webhook.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const pkg = require('../../../package.json');
|
||||
const PikminError = require('../PikminError');
|
||||
|
||||
let wump;
|
||||
try { wump = require('wumpfetch'); }
|
||||
catch (ex) { wump = undefined; }
|
||||
|
||||
|
||||
module.exports = class WebhookTransport {
|
||||
constructor(options = {}) {
|
||||
if (typeof options.url !== 'string') throw new TypeError(`"options.url" must be type of string but received type ${typeof options.url}`);
|
||||
|
||||
if (!wump) {
|
||||
try { wump = require('wumpfetch'); }
|
||||
catch (ex) { throw new PikminError('The package "wumpfetch" is required in order to create a webhook transport'); }
|
||||
};
|
||||
|
||||
this.url = options.url;
|
||||
this.type = 'WEBHOOK';
|
||||
this.name = typeof options.name === 'string' ? options.name : undefined;
|
||||
this.body = options.body ? options.body : { content: '%m' };
|
||||
this.clean = options.clean ? options.clean : true;
|
||||
this.queue = 0;
|
||||
this.parent = undefined;
|
||||
this.timeout = typeof options.timeout === 'number' ? options.timeout : 500;
|
||||
this.headers = typeof options.headers === 'object' ? options.headers : {};
|
||||
this.defaults = { inspect: true, clean: true, ...options.defaults };
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
append(options = this.defaults, data) {
|
||||
let req = {};
|
||||
|
||||
for (const v in this.body) req[v] = this.body[v].replace(/%m/g, data);
|
||||
if (!this.headers.hasOwnProperty('User-Agent')) this.headers['User-Agent'] = `Pikmin/${pkg.version} (https://github.com/PassTheWessel/pikmin)`;
|
||||
|
||||
setTimeout(async() => {
|
||||
this.queue--;
|
||||
return await wump(this.url, { method: 'post', data: req, chaining: false });
|
||||
}, this.timeout * ++this.queue);
|
||||
}
|
||||
|
||||
destroy() { return delete this; }
|
||||
};
|
||||
21
lib/util/Collection.js
Normal file
21
lib/util/Collection.js
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports = class PikminCollection extends Map {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
filter(callback) {
|
||||
let result = [];
|
||||
for (const entry of Array.from(this.values())) {
|
||||
if (callback(entry)) result.push(entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
map(callback) {
|
||||
let result = [];
|
||||
for (const value of Array.from(this.values())) result.push(callback(value));
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
9
lib/util/isSymbol.js
Normal file
9
lib/util/isSymbol.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const getTag = (val) => {
|
||||
if (val === null) return val === undefined ? '[object Undefined]' : '[object Null]';
|
||||
return toString.call(val);
|
||||
};
|
||||
|
||||
module.exports = (val) => {
|
||||
const type = typeof val;
|
||||
return type === 'symbol' || (type === 'object' && val !== null && getTag(val) === '[object Symbol]');
|
||||
};
|
||||
24
lib/util/type.js
Normal file
24
lib/util/type.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const symbol = require('./isSymbol');
|
||||
const types = {
|
||||
'string' : 0,
|
||||
'symbol' : 1,
|
||||
'object' : 2,
|
||||
'boolean' : 3,
|
||||
'function' : 4,
|
||||
'array' : 5,
|
||||
'float' : 6,
|
||||
'integer' : 7,
|
||||
'NULL' : 8,
|
||||
'undefined': 9
|
||||
};
|
||||
|
||||
module.exports = (val) => {
|
||||
if (Array.isArray(val)) return types['array'];
|
||||
if (symbol(val)) return types['symbol'];
|
||||
if (Number(val) === val && val % 1 !== 0) return types['float'];
|
||||
if (Number(val) === val && val % 1 === 0) return types['integer'];
|
||||
if (types.hasOwnProperty(typeof val)) return types[typeof val];
|
||||
if (val === null) return types['NULL'];
|
||||
|
||||
return types['undefined'];
|
||||
};
|
||||
Reference in New Issue
Block a user