forked from Galactic/galactic-bot
first structure
This commit is contained in:
parent
a7512253ba
commit
da8871b0d6
28
.eslintrc.json
Normal file
28
.eslintrc.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaVersion": 2018,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"warn",
|
||||||
|
"always"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
options.json
|
||||||
|
node_modules
|
9
Logger.js
Normal file
9
Logger.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const Winston = require('winstonjs');
|
||||||
|
|
||||||
|
class Logger extends Winston {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Logger;
|
24
Manager.js
24
Manager.js
@ -1,8 +1,32 @@
|
|||||||
const { EventEmitter } = require('events');
|
const { EventEmitter } = require('events');
|
||||||
|
const options = require('./options.json');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Manager extends EventEmitter {
|
class Manager extends EventEmitter {
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
|
||||||
|
this.registry = new Registry(this);
|
||||||
|
this.storageManager = new StorageManager(this, options.storage)
|
||||||
|
.initialize();
|
||||||
|
|
||||||
|
this.logger = new Logger(this);
|
||||||
|
|
||||||
|
this._built = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async build() {
|
||||||
|
|
||||||
|
await this.registry.loadComponents('components/commands/', Command);
|
||||||
|
|
||||||
|
this._built = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = Manager;
|
module.exports = Manager;
|
9
Registry.js
Normal file
9
Registry.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const { EventEmitter } = require('events');
|
||||||
|
|
||||||
|
class Registry extends EventEmitter {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Registry;
|
7
middleware/Shard.js
Normal file
7
middleware/Shard.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const { EventEmitter } = require('events');
|
||||||
|
|
||||||
|
class Shard extends EventEmitter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Shard;
|
3
middleware/ShardManager.js
Normal file
3
middleware/ShardManager.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class ShardManager {
|
||||||
|
|
||||||
|
}
|
@ -15,5 +15,10 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/Navy-gif/New-GBot/issues"
|
"url": "https://github.com/Navy-gif/New-GBot/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/Navy-gif/New-GBot#readme"
|
"homepage": "https://github.com/Navy-gif/New-GBot#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"discord.js": "discordjs/discord.js",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"mongodb": "^3.5.5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
5
storage/Provider.js
Normal file
5
storage/Provider.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class Provider {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Provider;
|
13
storage/StorageManager.js
Normal file
13
storage/StorageManager.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const { Collection } = require('../util/');
|
||||||
|
|
||||||
|
class StorageManager {
|
||||||
|
|
||||||
|
constructor(manager) {
|
||||||
|
|
||||||
|
this.providers = new Collection();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = StorageManager;
|
0
storage/providers/Mariadb.js
Normal file
0
storage/providers/Mariadb.js
Normal file
175
storage/providers/Mongodb.js
Normal file
175
storage/providers/Mongodb.js
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
const Provider = require('./Provider.js');
|
||||||
|
const { MongoClient } = require('mongodb');
|
||||||
|
|
||||||
|
class MongodbProvider extends Provider {
|
||||||
|
|
||||||
|
constructor(manager, config) {
|
||||||
|
|
||||||
|
super(manager, config);
|
||||||
|
|
||||||
|
if(!config) throw new Error('No config file provided!');
|
||||||
|
if(config && (!config.database || !config.url)) throw new Error('Invalid config file provided!');
|
||||||
|
|
||||||
|
this.config = config;
|
||||||
|
this.client;
|
||||||
|
this.db;
|
||||||
|
this.manager = manager;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
|
||||||
|
this.manager.logger.log('Initializing mongodb.');
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
this.client = await MongoClient.connect(this.config.url+this.config.database, { useUnifiedTopology: true });
|
||||||
|
this.manager.db = await this.client.db(this.config.database);
|
||||||
|
this.db = this.manager.db;
|
||||||
|
// this.manager.logger.print('Database connected.');
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
|
||||||
|
// this.manager.logger.error('Database connection failed!\n' + err);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find and return the first match
|
||||||
|
*
|
||||||
|
* @param {String} db The collection in which the data is to be updated
|
||||||
|
* @param {Object} query The filter that is used to find the data
|
||||||
|
* @returns {Array} An array containing the corresponding objects for the query
|
||||||
|
* @memberof Database
|
||||||
|
*/
|
||||||
|
|
||||||
|
find(db, query) {
|
||||||
|
|
||||||
|
//if(this.manager.debug) this.manager.logger.debug(`Incoming find query for ${db} with parameters ${JSON.stringify(query)}`);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
this.db.collection(db).find(query, async (error, cursor) => {
|
||||||
|
|
||||||
|
if(error) return reject(error);
|
||||||
|
return resolve(await cursor.toArray());
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find and return the first match
|
||||||
|
*
|
||||||
|
* @param {String} db The collection in which the data is to be updated
|
||||||
|
* @param {Object} query The filter that is used to find the data
|
||||||
|
* @returns {Object} An object containing the queried data
|
||||||
|
* @memberof Database
|
||||||
|
*/
|
||||||
|
|
||||||
|
findOne(db, query) {
|
||||||
|
|
||||||
|
//if(this.manager.debug) this.manager.logger.debug(`Incoming findOne query for ${db} with parameters ${JSON.stringify(query)}`);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
this.db.collection(db).findOne(query, async (error, item) => {
|
||||||
|
|
||||||
|
if(error) return reject(error);
|
||||||
|
return resolve(item);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the first filter match.
|
||||||
|
*
|
||||||
|
* @param {String} db The collection in which the data is to be updated
|
||||||
|
* @param {Object} filter The filter that is used to find the data
|
||||||
|
* @param {Object} data The updated data
|
||||||
|
* @param {Boolean} [upsert=false] Create a new entry if no match is found
|
||||||
|
* @returns {WriteResult} Object containing the followint counts: Matched, Upserted, Modified
|
||||||
|
* @memberof Database
|
||||||
|
*/
|
||||||
|
updateOne(db, query, data, upsert = false) {
|
||||||
|
|
||||||
|
if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
this.db.collection(db).updateOne(query, { $set: data }, { upsert: upsert }, async (error, result) => {
|
||||||
|
|
||||||
|
if(error) return reject(error);
|
||||||
|
else {
|
||||||
|
//return resolve(result)
|
||||||
|
let { matchedCount, upsertedCount, modifiedCount } = result;
|
||||||
|
return resolve({ matched: matchedCount, upserted: upsertedCount, modified: modifiedCount });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push data to an array
|
||||||
|
*
|
||||||
|
* @param {string} db The collection to query
|
||||||
|
* @param {object} filter The filter to find the document to update
|
||||||
|
* @param {object} data The data to be pushed
|
||||||
|
* @param {boolean} [upsert=false] Create a new entry if no match is found
|
||||||
|
* @returns
|
||||||
|
* @memberof Database
|
||||||
|
*/
|
||||||
|
push(db, query, data, upsert = false) {
|
||||||
|
|
||||||
|
//if(this.manager.debug) this.manager.logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${JSON.stringify(filter)} and data ${JSON.stringify(data)}`);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
this.db.collection(db).updateOne(query, { $push: data }, { upsert: upsert }, async (error, result) => {
|
||||||
|
|
||||||
|
if(error) return reject(error);
|
||||||
|
else return resolve(result);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a random element from a database
|
||||||
|
*
|
||||||
|
* @param {string} db The collection to query
|
||||||
|
* @param {object} [filter={}] The filtering object to narrow down the sample pool
|
||||||
|
* @param {number} [amount=1] Amount of items to return
|
||||||
|
* @returns {object}
|
||||||
|
* @memberof Database
|
||||||
|
*/
|
||||||
|
random(db, query = {}, amount = 1) {
|
||||||
|
|
||||||
|
//if(this.manager.debug) this.manager.logger.debug(`Incoming random query for ${db} with parameters ${JSON.stringify(filter)} and amount ${amount}`);
|
||||||
|
if(amount > 100) amount = 100;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject)=>{
|
||||||
|
|
||||||
|
this.db.collection(db).aggregate([{ $match: query }, { $sample: {size: amount}}], function(err, item) {
|
||||||
|
|
||||||
|
if(err) return reject(err);
|
||||||
|
resolve(item);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MongodbProvider;
|
6
structure/CacheManager.js
Normal file
6
structure/CacheManager.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class CacheManager {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CacheManager;
|
5
structure/interfaces/Command.js
Normal file
5
structure/interfaces/Command.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class Command {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Command;
|
5
structure/interfaces/Inhibitor.js
Normal file
5
structure/interfaces/Inhibitor.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class Inhibitor {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Inhibitor;
|
6
structure/interfaces/Setting.js
Normal file
6
structure/interfaces/Setting.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class Setting {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Setting;
|
5
structure/interfaces/index.js
Normal file
5
structure/interfaces/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
Command: require('./Command.js'),
|
||||||
|
Inhibitor: require('./Inhibitor.js'),
|
||||||
|
Setting: require('./Setting.js')
|
||||||
|
}
|
230
util/Collection.js
Normal file
230
util/Collection.js
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/* Adopted from Discord.js */
|
||||||
|
|
||||||
|
class Collection extends Map {
|
||||||
|
constructor(iterable) {
|
||||||
|
super(iterable);
|
||||||
|
|
||||||
|
Object.defineProperty(this, '_array', {
|
||||||
|
value: null,
|
||||||
|
writable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, '_keyArray', {
|
||||||
|
value: null,
|
||||||
|
writable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, val) {
|
||||||
|
this._array = null;
|
||||||
|
this._keyArray = null;
|
||||||
|
return super.set(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key) {
|
||||||
|
this._array = null;
|
||||||
|
this._keyArray = null;
|
||||||
|
return super.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
array() {
|
||||||
|
if (!this._array || this._array.length !== this.size) this._array = [...this.values()];
|
||||||
|
return this._array;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyArray() {
|
||||||
|
if (!this._keyArray || this._keyArray.length !== this.size) this._keyArray = [...this.keys()];
|
||||||
|
return this._keyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
first(amount) {
|
||||||
|
if (typeof amount === 'undefined') return this.values().next().value;
|
||||||
|
if (amount < 0) return this.last(amount * -1);
|
||||||
|
amount = Math.min(this.size, amount);
|
||||||
|
const arr = new Array(amount);
|
||||||
|
const iter = this.values();
|
||||||
|
for (let i = 0; i < amount; i++) arr[i] = iter.next().value;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstKey(amount) {
|
||||||
|
if (typeof amount === 'undefined') return this.keys().next().value;
|
||||||
|
if (amount < 0) return this.lastKey(amount * -1);
|
||||||
|
amount = Math.min(this.size, amount);
|
||||||
|
const arr = new Array(amount);
|
||||||
|
const iter = this.keys();
|
||||||
|
for (let i = 0; i < amount; i++) arr[i] = iter.next().value;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
last(amount) {
|
||||||
|
const arr = this.array();
|
||||||
|
if (typeof amount === 'undefined') return arr[arr.length - 1];
|
||||||
|
if (amount < 0) return this.first(amount * -1);
|
||||||
|
if (!amount) return [];
|
||||||
|
return arr.slice(-amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastKey(amount) {
|
||||||
|
const arr = this.keyArray();
|
||||||
|
if (typeof amount === 'undefined') return arr[arr.length - 1];
|
||||||
|
if (amount < 0) return this.firstKey(amount * -1);
|
||||||
|
if (!amount) return [];
|
||||||
|
return arr.slice(-amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
random(amount) {
|
||||||
|
let arr = this.array();
|
||||||
|
if (typeof amount === 'undefined') return arr[Math.floor(Math.random() * arr.length)];
|
||||||
|
if (arr.length === 0 || !amount) return [];
|
||||||
|
const rand = new Array(amount);
|
||||||
|
arr = arr.slice();
|
||||||
|
for (let i = 0; i < amount; i++) rand[i] = arr.splice(Math.floor(Math.random() * arr.length), 1)[0];
|
||||||
|
return rand;
|
||||||
|
}
|
||||||
|
|
||||||
|
randomKey(amount) {
|
||||||
|
let arr = this.keyArray();
|
||||||
|
if (typeof amount === 'undefined') return arr[Math.floor(Math.random() * arr.length)];
|
||||||
|
if (arr.length === 0 || !amount) return [];
|
||||||
|
const rand = new Array(amount);
|
||||||
|
arr = arr.slice();
|
||||||
|
for (let i = 0; i < amount; i++) rand[i] = arr.splice(Math.floor(Math.random() * arr.length), 1)[0];
|
||||||
|
return rand;
|
||||||
|
}
|
||||||
|
|
||||||
|
find(fn, thisArg) {
|
||||||
|
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
|
||||||
|
for (const [key, val] of this) {
|
||||||
|
if (fn(val, key, this)) return val;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
findKey(fn, thisArg) {
|
||||||
|
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
|
||||||
|
for (const [key, val] of this) {
|
||||||
|
if (fn(val, key, this)) return key;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
sweep(fn, thisArg) {
|
||||||
|
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
|
||||||
|
const previousSize = this.size;
|
||||||
|
for (const [key, val] of this) {
|
||||||
|
if (fn(val, key, this)) this.delete(key);
|
||||||
|
}
|
||||||
|
return previousSize - this.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter(fn, thisArg) {
|
||||||
|
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
|
||||||
|
const results = new Collection();
|
||||||
|
for (const [key, val] of this) {
|
||||||
|
if (fn(val, key, this)) results.set(key, val);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
partition(fn, thisArg) {
|
||||||
|
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
|
||||||
|
const results = [new Collection(), new Collection()];
|
||||||
|
for (const [key, val] of this) {
|
||||||
|
if (fn(val, key, this)) {
|
||||||
|
results[0].set(key, val);
|
||||||
|
} else {
|
||||||
|
results[1].set(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
map(fn, thisArg) {
|
||||||
|
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
|
||||||
|
const arr = new Array(this.size);
|
||||||
|
let i = 0;
|
||||||
|
for (const [key, val] of this) arr[i++] = fn(val, key, this);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
some(fn, thisArg) {
|
||||||
|
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
|
||||||
|
for (const [key, val] of this) {
|
||||||
|
if (fn(val, key, this)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
every(fn, thisArg) {
|
||||||
|
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
|
||||||
|
for (const [key, val] of this) {
|
||||||
|
if (!fn(val, key, this)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce(fn, initialValue) {
|
||||||
|
let accumulator;
|
||||||
|
if (typeof initialValue !== 'undefined') {
|
||||||
|
accumulator = initialValue;
|
||||||
|
for (const [key, val] of this) accumulator = fn(accumulator, val, key, this);
|
||||||
|
} else {
|
||||||
|
let first = true;
|
||||||
|
for (const [key, val] of this) {
|
||||||
|
if (first) {
|
||||||
|
accumulator = val;
|
||||||
|
first = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
accumulator = fn(accumulator, val, key, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
tap(fn, thisArg) {
|
||||||
|
this.forEach(fn, thisArg);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new this.constructor(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
concat(...collections) {
|
||||||
|
const newColl = this.clone();
|
||||||
|
for (const coll of collections) {
|
||||||
|
for (const [key, val] of coll) newColl.set(key, val);
|
||||||
|
}
|
||||||
|
return newColl;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAll() {
|
||||||
|
const returns = [];
|
||||||
|
for (const item of this.values()) {
|
||||||
|
if (item.delete) returns.push(item.delete());
|
||||||
|
}
|
||||||
|
return returns;
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(collection) {
|
||||||
|
if (!collection) return false;
|
||||||
|
if (this === collection) return true;
|
||||||
|
if (this.size !== collection.size) return false;
|
||||||
|
return !this.find((value, key) => {
|
||||||
|
const testVal = collection.get(key);
|
||||||
|
return testVal !== value || (testVal === undefined && !collection.has(key));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sort(compareFunction = (x, y) => +(x > y) || +(x === y) - 1) {
|
||||||
|
return new Collection([...this.entries()].sort((a, b) => compareFunction(a[1], b[1], a[0], b[0])));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Collection;
|
3
util/index.js
Normal file
3
util/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
Collection: require('./Collection.js');
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user