1
0
Fork 0

Compare commits

...

3 Commits

Author SHA1 Message Date
Hiroyuki aa3c071c2b
chore: remove unused imports 2021-08-21 02:59:58 -04:00
Hiroyuki 6de47a7e0d
refactor: remove .vscode folder
Somehow the VSC folder got in.
2021-07-08 20:53:29 -04:00
Hiroyuki b9d4a28c4f
refactor(models): use typegoose 2021-07-08 20:50:04 -04:00
17 changed files with 196 additions and 174 deletions

View File

@ -39,6 +39,7 @@
"import/prefer-default-export": "off", "import/prefer-default-export": "off",
"no-useless-constructor": "off", "no-useless-constructor": "off",
"@typescript-eslint/no-useless-constructor": 2, "@typescript-eslint/no-useless-constructor": 2,
"import/extensions": "off" "import/extensions": "off",
"max-classes-per-file": "off"
} }
} }

10
.vscode/settings.json vendored
View File

@ -1,10 +0,0 @@
{
"eslint.enable": true,
"eslint.validate": [
{
"language": "typescript",
"autoFix": true
}
],
"editor.tabSize": 2
}

View File

@ -26,6 +26,7 @@
"mongoose": "^5.7.4", "mongoose": "^5.7.4",
"nodemailer": "^6.3.1", "nodemailer": "^6.3.1",
"signale": "^1.4.0", "signale": "^1.4.0",
"@typegoose/typegoose": "^7.6.2",
"uuid": "^3.3.3", "uuid": "^3.3.3",
"x509": "bsian03/node-x509" "x509": "bsian03/node-x509"
}, },

View File

@ -1,6 +1,6 @@
import express from 'express'; import express from 'express';
import { AccountInterface } from '../models'; import { Account } from '../models';
export interface Req extends express.Request { export interface Req extends express.Request {
account: AccountInterface account: Account
} }

View File

@ -1,7 +1,6 @@
import axios from 'axios'; import axios from 'axios';
import moment from 'moment'; import moment from 'moment';
import { randomBytes } from 'crypto'; import { randomBytes } from 'crypto';
import { AccountInterface } from '../models';
import { Client } from '..'; import { Client } from '..';
export default class AccountUtil { export default class AccountUtil {
@ -19,7 +18,7 @@ export default class AccountUtil {
* @param data.emailAddress The user's email address. * @param data.emailAddress The user's email address.
* @param moderator The Discord user ID for the Staff member that created the account. * @param moderator The Discord user ID for the Staff member that created the account.
*/ */
public async createAccount(data: { userID: string, username: string, emailAddress: string }, moderator: string): Promise<{ account: AccountInterface, tempPass: string }> { public async createAccount(data: { userID: string, username: string, emailAddress: string }, moderator: string) {
const moderatorMember = this.client.guilds.get('446067825673633794').members.get(moderator); const moderatorMember = this.client.guilds.get('446067825673633794').members.get(moderator);
const tempPass = this.client.util.randomPassword(); const tempPass = this.client.util.randomPassword();
let passHash = await this.client.util.createHash(tempPass); passHash = passHash.replace(/[$]/g, '\\$').replace('\n', ''); let passHash = await this.client.util.createHash(tempPass); passHash = passHash.replace(/[$]/g, '\\$').replace('\n', '');
@ -82,7 +81,7 @@ export default class AccountUtil {
return { account: accountInterface, tempPass }; return { account: accountInterface, tempPass };
} }
public async lock(username: string, moderatorID: string, data?: { reason?: string, time?: number}) { public async lock(username: string, moderatorID: string, data?: { reason?: string, time?: number }) {
const account = await this.client.db.Account.findOne({ username }); const account = await this.client.db.Account.findOne({ username });
if (!account) throw new Error('Account does not exist.'); if (!account) throw new Error('Account does not exist.');
if (account.locked) throw new Error('Account is already locked.'); if (account.locked) throw new Error('Account is already locked.');

View File

@ -3,10 +3,11 @@ import Redis from 'ioredis';
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import signale from 'signale'; import signale from 'signale';
import fs from 'fs-extra'; import fs from 'fs-extra';
import { getModelForClass } from '@typegoose/typegoose';
import config from '../config.json'; import config from '../config.json';
import { Account, AccountInterface, Moderation, ModerationInterface, Domain, DomainInterface, Tier, TierInterface } from '../models'; import { Account, Moderation, Domain, Tier } from '../models';
import { emojis } from '../stores'; import { emojis } from '../stores';
import { Command, CSCLI, Util, Collection, Server, Event } from '.'; import { Command, Util, Collection, Server, Event } from '.';
export default class Client extends Eris.Client { export default class Client extends Eris.Client {
@ -18,7 +19,12 @@ export default class Client extends Eris.Client {
public events: Collection<Event>; public events: Collection<Event>;
public db: { Account: mongoose.Model<AccountInterface>; Domain: mongoose.Model<DomainInterface>; Moderation: mongoose.Model<ModerationInterface>; Tier: mongoose.Model<TierInterface>; }; public db = {
Account: getModelForClass(Account),
Domain: getModelForClass(Domain),
Moderation: getModelForClass(Moderation),
Tier: getModelForClass(Tier),
}
public redis: Redis.Redis; public redis: Redis.Redis;
@ -43,7 +49,6 @@ export default class Client extends Eris.Client {
this.commands = new Collection<Command>(); this.commands = new Collection<Command>();
this.events = new Collection<Event>(); this.events = new Collection<Event>();
this.functions = new Collection<Function>(); this.functions = new Collection<Function>();
this.db = { Account, Domain, Moderation, Tier };
this.redis = new Redis(); this.redis = new Redis();
this.stores = { emojis }; this.stores = { emojis };
this.signale = signale; this.signale = signale;

View File

@ -2,7 +2,6 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { Request } from 'express'; import { Request } from 'express';
import { Client } from '.'; import { Client } from '.';
import { AccountInterface } from '../models';
export default class Security { export default class Security {
public client: Client; public client: Client;
@ -36,7 +35,7 @@ export default class Security {
* If the bearer token is valid, will return the Account, else will return null. * If the bearer token is valid, will return the Account, else will return null.
* @param bearer The bearer token provided. * @param bearer The bearer token provided.
*/ */
public async checkBearer(bearer: string): Promise<null | AccountInterface> { public async checkBearer(bearer: string) {
try { try {
const res: any = jwt.verify(bearer, this.keys.key, { issuer: 'Library of Code sp-us | CSD' }); const res: any = jwt.verify(bearer, this.keys.key, { issuer: 'Library of Code sp-us | CSD' });
const account = await this.client.db.Account.findOne({ _id: res.id }); const account = await this.client.db.Account.findOne({ _id: res.id });

View File

@ -10,7 +10,6 @@ import moment from 'moment';
import fs from 'fs'; import fs from 'fs';
import { getUserByUid } from '../functions'; import { getUserByUid } from '../functions';
import { AccountUtil, Client, Command, RichEmbed } from '.'; import { AccountUtil, Client, Command, RichEmbed } from '.';
import { ModerationInterface, AccountInterface, Account } from '../models';
export default class Util { export default class Util {
public client: Client; public client: Client;
@ -36,7 +35,7 @@ export default class Util {
public async exec(command: string, options: childProcess.ExecOptions = {}): Promise<string> { public async exec(command: string, options: childProcess.ExecOptions = {}): Promise<string> {
return new Promise((res, rej) => { return new Promise((res, rej) => {
let output = ''; let output = '';
const writeFunction = (data: string|Buffer|Error) => { const writeFunction = (data: string | Buffer | Error) => {
output += `${data}`; output += `${data}`;
}; };
const cmd = childProcess.exec(command, options); const cmd = childProcess.exec(command, options);
@ -47,7 +46,7 @@ export default class Util {
cmd.stdout.off('data', writeFunction); cmd.stdout.off('data', writeFunction);
cmd.stderr.off('data', writeFunction); cmd.stderr.off('data', writeFunction);
cmd.off('error', writeFunction); cmd.off('error', writeFunction);
setTimeout(() => {}, 1000); setTimeout(() => { }, 1000);
if (code !== 0) rej(new Error(`Command failed: ${command}\n${output}`)); if (code !== 0) rej(new Error(`Command failed: ${command}\n${output}`));
res(output); res(output);
}); });
@ -99,7 +98,7 @@ export default class Util {
* @param query Command input * @param query Command input
* @param message Only used to check for errors * @param message Only used to check for errors
*/ */
public resolveCommand(query: string | string[], message?: Message): Promise<{cmd: Command, args: string[] }> { public resolveCommand(query: string | string[], message?: Message): Promise<{ cmd: Command, args: string[] }> {
try { try {
let resolvedCommand: Command; let resolvedCommand: Command;
if (typeof query === 'string') query = query.split(' '); if (typeof query === 'string') query = query.split(' ');
@ -153,7 +152,7 @@ export default class Util {
public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] { public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] {
let index = 0; let index = 0;
const array: {name: string, value: string, inline?: boolean}[][] = [[]]; const array: { name: string, value: string, inline?: boolean }[][] = [[]];
while (fields.length) { while (fields.length) {
if (array[index].length >= 25) { index += 1; array[index] = []; } if (array[index].length >= 25) { index += 1; array[index] = []; }
array[index].push(fields[0]); fields.shift(); array[index].push(fields[0]); fields.shift();
@ -198,7 +197,7 @@ export default class Util {
return tempPass; return tempPass;
} }
public async createAccount(hash: string, etcPasswd: string, username: string, userID: string, emailAddress: string, moderatorID: string, code: string): Promise<AccountInterface> { public async createAccount(hash: string, etcPasswd: string, username: string, userID: string, emailAddress: string, moderatorID: string, code: string) {
await this.exec(`useradd -m -p ${hash} -c ${etcPasswd} -s /bin/bash ${username}`); await this.exec(`useradd -m -p ${hash} -c ${etcPasswd} -s /bin/bash ${username}`);
await this.exec(`chage -d0 ${username}`); await this.exec(`chage -d0 ${username}`);
const tier = await this.client.db.Tier.findOne({ id: 1 }); const tier = await this.client.db.Tier.findOne({ id: 1 });
@ -222,7 +221,7 @@ export default class Util {
await Promise.all(tasks); await Promise.all(tasks);
} }
public async messageCollector(message: Message, question: string, timeout: number, shouldDelete = false, choices: string[] = null, filter = (msg: Message): boolean|void => {}): Promise<Message> { public async messageCollector(message: Message, question: string, timeout: number, shouldDelete = false, choices: string[] = null, filter = (msg: Message): boolean | void => { }): Promise<Message> {
const msg = await message.channel.createMessage(question); const msg = await message.channel.createMessage(question);
return new Promise((res, rej) => { return new Promise((res, rej) => {
const func = (Msg: Message) => { const func = (Msg: Message) => {
@ -250,12 +249,12 @@ export default class Util {
* *
* `4` - Delete * `4` - Delete
*/ */
public async createModerationLog(user: string, moderator: Member|User, type: number, reason?: string, duration?: number): Promise<ModerationInterface> { public async createModerationLog(user: string, moderator: Member | User, type: number, reason?: string, duration?: number) {
const moderatorID = moderator.id; const moderatorID = moderator.id;
const account = await this.client.db.Account.findOne({ $or: [{ username: user }, { userID: user }] }); const account = await this.client.db.Account.findOne({ $or: [{ username: user }, { userID: user }] });
if (!account) return Promise.reject(new Error(`Account ${user} not found`)); if (!account) return Promise.reject(new Error(`Account ${user} not found`));
const { username, userID } = account; const { username, userID } = account;
const logInput: { username: string, userID: string, logID: string, moderatorID: string, reason?: string, type: number, date: Date, expiration?: { date: Date, processed: boolean }} = { const logInput: { username: string, userID: string, logID: string, moderatorID: string, reason?: string, type: number, date: Date, expiration?: { date: Date, processed: boolean } } = {
username, userID, logID: uuid(), moderatorID, type, date: new Date(), username, userID, logID: uuid(), moderatorID, type, date: new Date(),
}; };

View File

@ -2,7 +2,7 @@ import fs, { writeFile, unlink } from 'fs-extra';
import axios from 'axios'; import axios from 'axios';
import { randomBytes } from 'crypto'; import { randomBytes } from 'crypto';
import { Message } from 'eris'; import { Message } from 'eris';
import { AccountInterface } from '../models'; import { Account } from '../models';
import { Client, Command, RichEmbed } from '../class'; import { Client, Command, RichEmbed } from '../class';
import { parseCertificate } from '../functions'; import { parseCertificate } from '../functions';
@ -144,7 +144,7 @@ export default class CWG_Create extends Command {
* @param x509Certificate The contents the certificate and key files. * @param x509Certificate The contents the certificate and key files.
* @example await CWG.createDomain(account, 'mydomain.cloud.libraryofcode.org', 6781); * @example await CWG.createDomain(account, 'mydomain.cloud.libraryofcode.org', 6781);
*/ */
public async createDomain(account: AccountInterface, domain: string, port: number, x509Certificate: { cert?: string, key?: string }) { public async createDomain(account: Account, domain: string, port: number, x509Certificate: { cert?: string, key?: string }) {
try { try {
if (port <= 1024 || port >= 65535) throw new RangeError(`Port range must be between 1024 and 65535, received ${port}.`); if (port <= 1024 || port >= 65535) throw new RangeError(`Port range must be between 1024 and 65535, received ${port}.`);
if (await this.client.db.Domain.exists({ domain })) throw new Error(`Domain ${domain} already exists in the database.`); if (await this.client.db.Domain.exists({ domain })) throw new Error(`Domain ${domain} already exists in the database.`);

View File

@ -1,8 +1,8 @@
import moment from 'moment'; import moment from 'moment';
import { Message, GuildTextableChannel, Member, Role } from 'eris'; import { Message, GuildTextableChannel, Member, Role } from 'eris';
import { Client, Command, Report, RichEmbed } from '../class'; import { Client, Command, RichEmbed } from '../class';
import { dataConversion } from '../functions'; import { dataConversion } from '../functions';
import { AccountInterface } from '../models'; import { Account } from '../models';
export default class Whois extends Command { export default class Whois extends Command {
constructor(client: Client) { constructor(client: Client) {
@ -21,7 +21,7 @@ export default class Whois extends Command {
public async run(message: Message<GuildTextableChannel>, args: string[]) { public async run(message: Message<GuildTextableChannel>, args: string[]) {
try { try {
let full = false; let full = false;
let account: AccountInterface; let account: Account;
if (args[1] === '--full' && this.fullRoles.some((r) => message.member.roles.includes(r) || message.author.id === '554168666938277889')) full = true; if (args[1] === '--full' && this.fullRoles.some((r) => message.member.roles.includes(r) || message.author.id === '554168666938277889')) full = true;
const user = args[0] || message.author.id; const user = args[0] || message.author.id;
@ -68,7 +68,7 @@ export default class Whois extends Command {
} }
} }
public async full(account: AccountInterface, embed: RichEmbed, member: Member) { public async full(account: Account, embed: RichEmbed, member: Member) {
const [cpuUsage, data, fingerInformation, chage, memory] = await Promise.all([ const [cpuUsage, data, fingerInformation, chage, memory] = await Promise.all([
this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`), this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`),
this.client.redis.get(`storage-${account.username}`), this.client.redis.get(`storage-${account.username}`),
@ -92,7 +92,7 @@ export default class Whois extends Command {
embed.addField('Storage', data ? dataConversion(Number(data)) : 'N/A', true); embed.addField('Storage', data ? dataConversion(Number(data)) : 'N/A', true);
} }
public async default(account: AccountInterface, embed: RichEmbed) { public async default(account: Account, embed: RichEmbed) {
const [cpuUsage, data, memory] = await Promise.all([ const [cpuUsage, data, memory] = await Promise.all([
this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`), this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`),
this.client.redis.get(`storage-${account.username}`), this.client.redis.get(`storage-${account.username}`),

View File

@ -2,7 +2,6 @@
/* eslint-disable no-continue */ /* eslint-disable no-continue */
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
import { Client, RichEmbed } from '../class'; import { Client, RichEmbed } from '../class';
import { Tiers } from '../models';
const channelID = '691824484230889546'; const channelID = '691824484230889546';
@ -18,8 +17,7 @@ export default function memory(client: Client) {
// memory in megabytes // memory in megabytes
const memoryConversion = mem / 1024 / 1024; const memoryConversion = mem / 1024 / 1024;
const userLimits: { soft?: number, hard?: number } = {}; const userLimits: { soft?: number, hard?: number } = {};
// @ts-ignore const tier = await client.db.Tier.findOne({ id: acc.tier }).lean().exec();
const tier: Tiers = await client.db.Tier.findOne({ id: acc.tier }).lean().exec();
userLimits.soft = acc.ramLimitNotification; userLimits.soft = acc.ramLimitNotification;
userLimits.hard = tier.resourceLimits.ram; userLimits.hard = tier.resourceLimits.ram;
if ((memoryConversion <= userLimits.soft) && (acc.ramLimitNotification !== 0)) { if ((memoryConversion <= userLimits.soft) && (acc.ramLimitNotification !== 0)) {

View File

@ -1,53 +1,72 @@
import { Document, Schema, model } from 'mongoose'; import { modelOptions, prop } from '@typegoose/typegoose';
import { Base } from '@typegoose/typegoose/lib/defaultClasses';
export interface AccountInterface extends Document { export type Tier = 1 | 2 | 3;
username: string,
userID: string, class Permissions {
homepath: string, @prop()
emailAddress: string, staff?: boolean;
createdBy: string,
createdAt: Date, @prop()
locked: boolean, technician?: boolean;
tier: number,
supportKey: string, @prop()
referralCode: string, director?: boolean;
totalReferrals: number,
permissions: {
staff: boolean,
technician: boolean,
director: boolean,
},
ramLimitNotification: number,
root: boolean,
hash: boolean,
salt: string,
authTag: Buffer
revokedBearers: string[],
} }
const Account = new Schema<AccountInterface>({ @modelOptions({ schemaOptions: { collection: 'Account' } })
username: String, export default class Account extends Base {
userID: String, @prop({ required: true, unique: true })
homepath: String, username: string;
emailAddress: String,
createdBy: String,
createdAt: Date,
locked: Boolean,
tier: Number,
supportKey: String,
referralCode: String,
totalReferrals: Number,
permissions: {
staff: Boolean,
technician: Boolean,
director: Boolean,
},
ramLimitNotification: Number,
root: Boolean,
hash: Boolean,
salt: String,
authTag: Buffer,
revokedBearers: Array,
});
export default model<AccountInterface>('Account', Account); @prop({ required: true, unique: true })
userID: string;
@prop({ required: true, unique: true })
homepath: string;
@prop({ required: true })
emailAddress: string;
@prop({ required: true })
createdBy: string;
@prop({ required: true })
createdAt: Date;
@prop({ required: true, default: false })
locked: boolean;
@prop({ required: true, default: 1 })
tier: Tier;
@prop({ required: true })
supportKey: string;
@prop({ required: true, unique: true })
referralCode: string;
@prop({ required: true, default: 0 })
totalReferrals: number;
@prop()
permissions?: Permissions;
@prop()
ramLimitNotification: number;
@prop()
root: boolean;
@prop()
hash: boolean;
@prop()
salt: string;
@prop()
authTag: Buffer;
@prop({ type: () => String })
revokedBearers: string[];
}

View File

@ -1,24 +1,28 @@
import { Document, Schema, model } from 'mongoose'; import { modelOptions, prop } from '@typegoose/typegoose';
import { AccountInterface } from './Account'; import { Account } from '.';
export interface DomainInterface extends Document { class X509 {
account: AccountInterface, @prop({ required: true })
domain: string, cert: string;
port: number,
// Below is the full absolute path to the location of the x509 certificate and key files. @prop({ required: true })
x509: { key: string;
cert: string,
key: string
},
enabled: true
} }
const Domain = new Schema<DomainInterface>({ @modelOptions({ schemaOptions: { collection: 'Domain' } })
account: Object, export default class Domain {
domain: String, @prop({ type: () => Account, required: true })
port: Number, account: Account;
x509: { cert: String, key: String },
enabled: Boolean,
});
export default model<DomainInterface>('Domain', Domain); @prop({ required: true, unique: true })
domain: string;
@prop({ required: true })
port: number;
@prop({ required: true, type: () => X509 })
x509: X509;
@prop({ required: true })
enabled: boolean;
}

View File

@ -1,38 +1,45 @@
import { Document, Schema, model } from 'mongoose'; import { modelOptions, prop } from '@typegoose/typegoose';
export interface ModerationInterface extends Document {
username: string, class Expiration {
userID: string, @prop({ required: true })
logID: string, date: Date;
moderatorID: string,
reason: string, @prop({ required: true, default: false })
/** processed: boolean;
* @field 0 - Create
* @field 1 - Warn
* @field 2 - Lock
* @field 3 - Unlock
* @field 4 - Delete
*/
type: 0 | 1 | 2 | 3 | 4
date: Date,
expiration: {
date: Date,
processed: boolean
}
} }
const Moderation = new Schema<ModerationInterface>({ enum Type {
username: String, Create,
userID: String, Warn,
logID: String, Lock,
moderatorID: String, Unlock,
reason: String, Delete
type: Number, }
date: Date,
expiration: {
date: Date,
processed: Boolean,
},
});
export default model<ModerationInterface>('Moderation', Moderation); @modelOptions({ schemaOptions: { collection: 'Moderation' } })
export default class Moderation {
@prop({ required: true })
username: string;
@prop({ required: true })
userID: string;
@prop({ required: true, unique: true })
logID: string;
@prop({ required: true })
moderatorID: string;
@prop()
reason?: string;
@prop({ enum: Type, required: true })
type: Type;
@prop({ required: true })
date: Date;
@prop()
expiration?: Expiration;
}

View File

@ -1,23 +1,23 @@
import { Document, Schema, model } from 'mongoose'; import { modelOptions, prop } from '@typegoose/typegoose';
export interface Tiers { class ResourceLimits {
id: number, @prop({ required: true })
resourceLimits: { ram: number;
// In MB
ram: number, storage: number @prop({ required: true })
} storage: number;
} }
export interface TierInterface extends Tiers, Document { @modelOptions({
id: number; schemaOptions: {
} _id: false,
collection: 'Tier',
const Tier = new Schema<TierInterface>({
id: Number,
resourceLimits: {
ram: Number,
storage: Number,
}, },
}, { id: false }); })
export default class Tier {
@prop({ required: true, unique: true })
id: number;
export default model<TierInterface>('Tier', Tier); @prop({ required: true, type: () => ResourceLimits })
resourceLimits: ResourceLimits;
}

View File

@ -1,4 +1,4 @@
export { default as Account, AccountInterface } from './Account'; export { default as Account } from './Account';
export { default as Domain, DomainInterface } from './Domain'; export { default as Domain } from './Domain';
export { default as Moderation, ModerationInterface } from './Moderation'; export { default as Moderation } from './Moderation';
export { default as Tier, TierInterface, Tiers } from './Tier'; export { default as Tier } from './Tier';

View File

@ -47,7 +47,7 @@
// "typeRoots": [], /* List of folders to include type definitions from. */ // "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */ // "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
@ -58,7 +58,7 @@
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */ /* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
} }
} }