commit 6436cd087f1ac71d08435d80398b6d5fe25888d2 Author: Dennis Gunia Date: Tue Sep 22 11:48:45 2020 +0200 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7acb7f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/*.key +/out/* +/node_modules +/config.json diff --git a/README b/README new file mode 100644 index 0000000..c067684 --- /dev/null +++ b/README @@ -0,0 +1,114 @@ +# one-time-code.js +Dies ist ein Programm zum erzeugen von anonymen Schlüsseln. Diese werden automatisch den Teilnehmern zugesendet. +Zudem wird ein asymetrisch verschlüsselter Safe erstellt. + +## Zweck +Für ananoyme Wahlen muss sichergestellt werden, dass: +1. Nur berechtigter Personen abstimmen können +2. Jeder Wähler nicht rückverfolgbar ist +3. Jeder Wähler nur einmal abstimmen kann + +Punkte 1. und 2. Lassen sich mit dieser Anwendug realisieren. + +## Funktionsprinzip +Diese Anwendung muss auf zwei Systemen installiert sein. Beide Systeme dürfen nicht aufeinander Zugreifen können und müssen von verschiedenen Personen bedient werden. +Die beiden Systeme sind: +- System A & Person A: Hier werden die Schlüssel erzeugt, benutzern zufällig zugewiesen und versendet. +- System B & Person B: Hier wird das Schlüsselpaar erzeugt und hier kann der Safe entschlüsselt werden. +- Person C: Erhält von Person A die gültigen Codes und verifiziert die eingehenden Wahlzettel. + +Da Person A die SChlüssel-Nutzer Zuordnung zu keinem Zeitpunkt kennt, kann sie auch die selbe Person wie Person C sein. + +### Schritt 1 +Auf System B wird ein Schlüsselpaar erzeugt. Dies besteht aus dem Öffentlichen und Privaten schlüssel. +- Der Öffentliche schlüssel dient zur Verschlüsselung der Daten und kann nicht zum entschlüsseln verwendet werden. +- Der Private schlüssel dient zur entschlüsselung des Safes und darf System B NIEMALS verlassen. + +Dazu wird z.B. folgender Befehl ausgeführt: +`ts-node .\index.ts --privkey private.key --pubkey public.key --genkey + +Der erzeugte Öffentliche schlüssel muss an Person A übergeben werden. Diese nutzt den Schlüssel auf System A zum verschlüsseln des Passwortsafes. + +### Schritt 2 +Auf System A werden jetzt die Codes erzeugt und an die Nutzer versendet. +Die Nutzer-Schlüssel zuweisung erfolgt temporär auf System A, ist jedoch nicht auslesbar und wird unmittelbar im Passwortsafe gespeichert. + +Person A führt folgenden Befehl aus: +`ts-node .\index.ts --config config.json --pubkey public.key --send --safe .\out\credentials.json --mails mail.txt -html template.html + +Dabei wird eine Liste mit den Mails und Namen sowie der schlüssel übergeben. + +Der Ausfbau der mail.txt ist: +``` +; +; +... +``` + +Es werden drei Ausgaben erzeugt: +- List aller Codes +- Eine RegEx vorlage für alle Codes +- Safe-Datei + +Die Speicherorte der Code-Files werden in der config.json angegeben. +Der Safe wird an Person B weitergegeben. + +Das Programm versendet automatisch Mails an alle Personen. Die HTML Vorlage wird mit --html angegeben. +In der Vorlage werden folgende Zeichenketten ersetzt: +`{{code}} +=> Genereirter Code +`{{name}} +=> Name +`{{mail}} +=> Mail + +Der SMTP-Relay-Server wird in der config.json angegeben. +Geenaue Doku: https://nodemailer.com/smtp/ + +### Schritt 3 +Ggf. muss der Safe entschlüsselt werden. Beispielfälle wären: +- Teilnehmer hat keinen Zugriff auf das Mailkonto +- Teilnehmer hat seinen Code vergessen +- Mail wurd durch den Spamfilter entfernt + +Person B muss dann mit dem Privaten Schlüssel den Safe auslesen und dem Teilnehmer seinen Code zukommen lassen. +Dazu muss golgender Befehl ausgeführt werden: +ts-node .\index.ts --privkey private.key --decrypt --safe .\out\credentials.json + +## Config-Datei +{ + "mail":{ + "host": "", + "port": , + "secure": , + "auth": { + "user": "", + "pass": "" + }, + "tls": { + "rejectUnauthorized": false, + "ciphers":"SSLv3" + } + }, + "mailFrom": "", + "outFileCodes": "", + "outFileMatch": "" +} + + +## Syntax + +==> Schlüsselpaar Erzeugen +`ts-node .\index.ts --privkey --pubkey --genkey + +z.B. `ts-node .\index.ts --privkey private.key --pubkey public.key --genkey + +==> Codes Erzeugen und versenden +`ts-node .\index.ts --config --pubkey --send --safe .\out\credentials.json --mails -html + +z.B. `ts-node .\index.ts --config config.json --pubkey public.key --send --safe .\out\credentials.json --mails mail.txt -html template.html + +==> Safe entschlüsseln +`ts-node .\index.ts --privkey --decrypt --safe .\out\credentials.json + +z.B. `ts-node .\index.ts --privkey private.key --decrypt --safe .\out\credentials.json \ No newline at end of file diff --git a/config.template.json b/config.template.json new file mode 100644 index 0000000..fcba61e --- /dev/null +++ b/config.template.json @@ -0,0 +1,18 @@ +{ + "mail":{ + "host": "", + "port": 587, + "secure": false, + "auth": { + "user": "", + "pass": "" + }, + "tls": { + "rejectUnauthorized": false, + "ciphers":"SSLv3" + } + }, + "mailFrom": "BJR", + "outFileCodes": "./out/codes.txt", + "outFileMatch": "./out/match.txt" +} \ No newline at end of file diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..6dbec81 --- /dev/null +++ b/index.ts @@ -0,0 +1,77 @@ +import * as fs from 'fs' +import { generateToken } from './src/generate' +import { SecureVault } from './src/vault' + +let configPath = "", action = -1, pubKey = "", privKey = "", safeFile = "", mails = "", html = ""; +// parse cli args +for (let i = 1; i < process.argv.length ; i++){ + if (process.argv[i] === "--config"){ + if (i + 1 < process.argv.length && !process.argv[i+1].startsWith("--")){ + configPath = process.argv[i+1]; + }else {throw new Error("Invalid params")} + } + if (process.argv[i] === "--pubkey"){ + if (i + 1 < process.argv.length && !process.argv[i+1].startsWith("--")){ + pubKey = process.argv[i+1]; + }else {throw new Error("Invalid params")} + } + if (process.argv[i] === "--privkey"){ + if (i + 1 < process.argv.length && !process.argv[i+1].startsWith("--")){ + privKey = process.argv[i+1]; + }else {throw new Error("Invalid params")} + } + if (process.argv[i] === "--safe"){ + if (i + 1 < process.argv.length && !process.argv[i+1].startsWith("--")){ + safeFile = process.argv[i+1]; + }else {throw new Error("Invalid params")} + } + if (process.argv[i] === "--mails"){ + if (i + 1 < process.argv.length && !process.argv[i+1].startsWith("--")){ + mails = process.argv[i+1]; + }else {throw new Error("Invalid params")} + } + if (process.argv[i] === "--html"){ + if (i + 1 < process.argv.length && !process.argv[i+1].startsWith("--")){ + html = process.argv[i+1]; + }else {throw new Error("Invalid params")} + } + if (process.argv[i] === "--send"){ action = 1 } + if (process.argv[i] === "--decrypt"){ action = 2 } + if (process.argv[i] === "--genkey"){ action = 3 } +} +if ( action == -1){ throw new Error("No Action specified") } +if (!configPath && action == 1){ throw new Error("Config-Path not specified") } +if (!pubKey && action != 2){ throw new Error("Public-Key not specified") } +if (!safeFile && action != 3){ throw new Error("Safe-file not specified") } +if (!privKey && action >= 2){ throw new Error("Private-Key not specified") } +if (!mails && action == 1){ throw new Error("Mail-Input not specified") } +if (!html && action == 1){ throw new Error("Mail-Template not specified") } + + + +if (action == 1){ + let dataSafe: SecureVault = new SecureVault(pubKey,privKey); + // load config + const confRaw = fs.readFileSync(configPath, 'utf8') + let config:any = {} + try { + config = JSON.parse(confRaw) + config.inFileMail = mails; + config.htmlPath = html; + } catch (error) { + console.error("Cannote read config file!") + process.exit(100); + } + generateToken(config,dataSafe).then(el => { + console.log(el) + dataSafe.saveData(safeFile); + }).catch(err => console.error("error", err)) +}else if(action == 2){ + let dataSafe: SecureVault = new SecureVault(pubKey,privKey); + dataSafe.loadData(safeFile); + dataSafe.decryptData(); +}else if(action == 3){ + SecureVault.genKey(pubKey,privKey); +} + + diff --git a/mails.txt b/mails.txt new file mode 100644 index 0000000..af3d60d --- /dev/null +++ b/mails.txt @@ -0,0 +1,8 @@ +test1@dennisgunia.de;Dennis Gunia +test2@dennisgunia.de;Hans Hans +test3@dennisgunia.de;Fred +test4@dennisgunia.de;Keine Ahnung +test5@dennisgunia.de;Bert +test6@dennisgunia.de;Ilse +test7@dennisgunia.de;Hans +test8@dennisgunia.de;Brecht Bricht \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..01a8456 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,77 @@ +{ + "name": "random", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/handlebars": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@types/handlebars/-/handlebars-4.1.0.tgz", + "integrity": "sha512-gq9YweFKNNB1uFK71eRqsd4niVkXrxHugqWFQkeLRJvGjnxsLr16bYtcsG4tOFwmYi0Bax+wCkbf1reUfdl4kA==", + "requires": { + "handlebars": "*" + } + }, + "@types/node": { + "version": "14.11.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.1.tgz", + "integrity": "sha512-oTQgnd0hblfLsJ6BvJzzSL+Inogp3lq9fGgqRkMB/ziKMgEUaFl801OncOzUmalfzt14N0oPHMK47ipl+wbTIw==" + }, + "@types/nodemailer": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.0.tgz", + "integrity": "sha512-KY7bFWB0MahRZvVW4CuW83qcCDny59pJJ0MQ5ifvfcjNwPlIT0vW4uARO4u1gtkYnWdhSvURegecY/tzcukJcA==", + "requires": { + "@types/node": "*" + } + }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "nodemailer": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.11.tgz", + "integrity": "sha512-BVZBDi+aJV4O38rxsUh164Dk1NCqgh6Cm0rQSb9SK/DHGll/DrCMnycVDD7msJgZCnmVa8ASo8EZzR7jsgTukQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "uglify-js": { + "version": "3.10.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.4.tgz", + "integrity": "sha512-kBFT3U4Dcj4/pJ52vfjCSfyLyvG9VYYuGYPmrPvAxRw/i7xHiT4VvCev+uiEMcEEiu6UNB6KgWmGtSUYIWScbw==", + "optional": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..606fb4a --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "random", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@types/handlebars": "^4.1.0", + "@types/node": "^14.11.1", + "@types/nodemailer": "^6.4.0", + "crypto": "^1.0.1", + "handlebars": "^4.7.6", + "nodemailer": "^6.4.11" + } +} diff --git a/src/generate.ts b/src/generate.ts new file mode 100644 index 0000000..d039fc9 --- /dev/null +++ b/src/generate.ts @@ -0,0 +1,114 @@ +import * as fs from 'fs' +import * as nodemailer from 'nodemailer'; +import { shuffleArray } from './util/shuffle'; +import { mkstring } from './util/token'; +import * as Handlebars from "handlebars"; +import Mail from 'nodemailer/lib/mailer'; +import { SecureVault } from './vault'; + +interface mail{ + mail: string; + name: string; +} + +export function generateToken(config: any,dataSafe: SecureVault): Promise{ + let pr = new Promise((resolve,error) => { + let mailArray: mail[] = []; + + // read and process mail list + let readline = require('readline'), + instream = fs.createReadStream(config.inFileMail), + outstream = new (require('stream'))(), + rl = readline.createInterface(instream, outstream); + + rl.on('line', function (line:string) { + console.log(line); + mailArray.push({ + mail: line.substr(0,line.indexOf(";")), + name: line.substr(line.indexOf(";") + 1) + }) + }); + rl.on('close', function (line:string) { + // next step + generateCodes(resolve,error,mailArray,config,dataSafe); + }); + }); + return pr; +} + +// generate codes +async function generateCodes(resolve: (value?: String[]) => void,error: (reason?: any) => void,mailArray: mail[],config: any,dataSafe: SecureVault){ + let codeArray: string[] = []; + let checkString: string = ''; + let listString: string = ''; + + for(let i = 0; i < mailArray.length; i++){ // as many codes as adresses + // check that codes are unique + let code = mkstring(4); + while (codeArray.includes(code)){ + code = mkstring(4); + } + codeArray.push(code); + checkString = `${checkString}|${code}` + listString = `${listString}\n${code}` + } + checkString = checkString.substr(1); + listString = listString.substr(1); + + //write code lists + try { + fs.writeFileSync(config.outFileMatch, checkString); + fs.writeFileSync(config.outFileCodes, listString); + } catch (error) { + error(error); + } + sendMails(resolve,error,mailArray,codeArray,config,dataSafe); +} + + +// randomize mails and tokens +async function sendMails(resolve: (value?: String[]) => void,error: (reason?: any) => void,mailArray: mail[],codeArray: string[],config: any,dataSafe: SecureVault){ + let mailserver = nodemailer.createTransport(config.mail); + // read mail template + let template!: HandlebarsTemplateDelegate; + try { + const htmlSrc=fs.readFileSync(config.htmlPath, "utf8") + template = Handlebars.compile(htmlSrc) + } catch (error) { + console.error("Cannote read template file!") + error(error); + } + + shuffleArray(mailArray); + shuffleArray(codeArray); + for(let i = 0; i < mailArray.length; i++){ + // send mail + dataSafe.pushData({ + name: mailArray[i].name, + mail: mailArray[i].mail, + code: codeArray[i] + }) + await send(mailArray[i].name, mailArray[i].mail, codeArray[i],template,mailserver,config); + } + resolve(codeArray); +} + +async function send(name: string, mail: string, code: string,template: HandlebarsTemplateDelegate,mailserver: Mail,config: any){ + // fill template + let html = template({ + "name": name, + "mail": mail, + "code": code + }) + let mailOptions = { + from: `${config.mailFrom} <${config.mail.auth.user}>`, // sender address + to: mail, // list of receivers + subject: `Dein Zugangscode zur BJR Wahl`, // Subject line + html: html + }; + try { + await mailserver.sendMail(mailOptions); + } catch (error) { + console.log(`Error sendign mail to ${mail} : ${error}`) + } +} \ No newline at end of file diff --git a/src/util/shuffle.ts b/src/util/shuffle.ts new file mode 100644 index 0000000..144802c --- /dev/null +++ b/src/util/shuffle.ts @@ -0,0 +1,8 @@ +export function shuffleArray(array: any[]) { + for (var i = array.length - 1; i > 0; i--) { + var j = Math.floor(Math.random() * (i + 1)); + var temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } +} \ No newline at end of file diff --git a/src/util/token.ts b/src/util/token.ts new file mode 100644 index 0000000..e8bcd23 --- /dev/null +++ b/src/util/token.ts @@ -0,0 +1,9 @@ +export function mkstring (length:number ) { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + var charactersLength = characters.length; + for ( var i = 0; i < length; i++ ) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} diff --git a/src/vault.ts b/src/vault.ts new file mode 100644 index 0000000..ab0127c --- /dev/null +++ b/src/vault.ts @@ -0,0 +1,101 @@ +import * as crypto from 'crypto' +import path from 'path'; +import * as fs from 'fs' +import { generateKeyPair } from 'crypto'; + +export interface SecureVaultItem { + d: string; // data + k: string; // key + iv: string; // init vector +} + +export interface secureVaultList { + items: SecureVaultItem[]; + publicKey?: Buffer; + privateKey?: Buffer; +} + +export class SecureVault { + + safe: secureVaultList; + privPath?: string; + pubPath?: string; + + constructor (publicKey: string, privateKey?: string) { + this.safe = { + items: [], + publicKey: publicKey ?fs.readFileSync(path.resolve(publicKey)): undefined, + privateKey: privateKey ? fs.readFileSync(path.resolve(privateKey)): undefined + }; + this.privPath = publicKey ? path.resolve(publicKey): undefined, + this.pubPath = privateKey ? path.resolve(privateKey): undefined + } + + async pushData(data: any): Promise{ + // encrypt payload + const txtData = JSON.stringify(data); + const key = crypto.randomBytes(32); + const iv = crypto.randomBytes(16); + let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv); + let encrypted = cipher.update(txtData); + encrypted = Buffer.concat([encrypted, cipher.final()]); + // encrypt key + var buffer = new Buffer(key); + if (!this.safe.publicKey){ + throw new Error("Public Key not found"); + } + var asym_encrypted = crypto.publicEncrypt(this.safe.publicKey, buffer); + + this.safe.items.push({ + d: encrypted.toString('hex'), + k: asym_encrypted.toString("base64"), + iv: iv.toString('hex') + }) + } + + async saveData(path: string): Promise{ + fs.writeFileSync(path, JSON.stringify(this.safe.items)); + } + + async loadData(path: string): Promise{ + this.safe.items = JSON.parse(fs.readFileSync(path, 'utf8')) + } + + async decryptData(): Promise{ + + this.safe.items.forEach(el => { + // decrpyt key + let buffer = new Buffer(el.k, "base64"); + if (!this.safe.privateKey){ + throw new Error("Private Key not found"); + } + var key = crypto.privateDecrypt(this.safe.privateKey, buffer); + // decrpyt payload + let iv = Buffer.from(el.iv, 'hex'); + let encryptedText = Buffer.from(el.d, 'hex'); + let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + let decrypted = decipher.update(encryptedText); + decrypted = Buffer.concat([decrypted, decipher.final()]); + const obj = JSON.parse(decrypted.toString()); + console.log(obj); + }) + } + + static genKey(publicKeyDir: string, privateKeyDir: string){ + generateKeyPair('rsa', { + modulusLength: 4096, + publicKeyEncoding: { + type: 'pkcs1', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs1', + format: 'pem', + } + }, (err, publicKey, privateKey) => { + fs.writeFileSync(privateKeyDir, privateKey); + fs.writeFileSync(publicKeyDir, publicKey); + }); + } + +} \ No newline at end of file diff --git a/template.html b/template.html new file mode 100644 index 0000000..1a46036 --- /dev/null +++ b/template.html @@ -0,0 +1,23 @@ +Hallo {{name}},

+ +du wurdest ausgewählt Einfluss auf die Wahl des Bezirks-Jugend-Rats 2020 zu nehmen!
+ +Du hast das Privileg am Jugendtag unter folgender URL abzustimmen:
+https://docs.google.com/forms/d/e/1FAIpQLSdHNZSoYTHnS_Tg-BODcwdkXFDaaP1niuj8a_PlADiilMVEIw/viewform?usp=pp_url&entry.220991222={{code}}
+
+Sollte dieser Link nicht funktionieren, verwende bitte den Code {{code}} auf der Seite https://forms.gle/6WHfptZ7QcbqxBE38.

+ +Die Wahlen werden im Rahmen des Jugendtags-Sitzung durchgeführt. Der Link wird erst zu diesem Zeitpunk gültig.
+Die Sitzung wird am 10. Oktober 2020 Online über Zoom stattfinden. Link und Uhrzeit hierzu folgen in einer seperaten Mail.

+WICHTIG: Bitte stimme erst ab, wenn die Anmeldung offiziell eröffnet ist. Alle vorherigen Einsendungen werden nicht berücksichtigt.
+WICHTIG: Dies ist deine persönlichen Zugangsdaten. Bitte gib diese nicht weiter.

+Bei weiteren Fragen zu den Wahlen wende dich bitte an: nyi@nazarener.de +

+Viel Spaß!
+Dein BJR
+
+


+ +Wenn Sie nicht der richtige Adressat sind, oder diese E-Mail irrtümlich erhalten haben, informieren Sie bitte den Absender und löschen Sie diese Mail.
+Dies ist eine automatisch generierte Mail – bitte antworten sie nicht auf diese e-mail sondern wenden sie sich bei Rückfragen direkt an nyi@nazarener.de +
\ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c9f603c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,69 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "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. */ + "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. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +}