Initial commit

This commit is contained in:
Maksym 2024-07-22 01:36:06 +02:00
commit fd2f5df26b
17 changed files with 9698 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
.vscode
logs
VoicemeeterRemote.h

8894
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

70
package.json Normal file
View File

@ -0,0 +1,70 @@
{
"name": "voicemeeter-remote-backend",
"version": "1.0.0",
"description": "",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"koffi": "^2.8.11",
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
"ts-node": "^10.9.2",
"typescript": "^5.5.2",
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.42.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"tsconfig-paths": "^4.2.0"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src/server",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}

78
src/Logger.ts Normal file
View File

@ -0,0 +1,78 @@
import path from "path";
import { DEBUG } from "./variables";
import fs from "fs";
export class Logger {
public currentLogFile: string;
public tag?: string;
private get pathToLogFile(): string {
return path.join(__dirname, "../log", this.currentLogFile);
}
constructor(options?: { tag?: string }) {
this.currentLogFile = this.createNewLogFile();
if (options !== undefined) {
this.tag = options.tag;
}
}
private createNewLogFile() {
const date = new Date().toISOString().split("T");
date[1] = date[1].split(".")[0].replaceAll(":", "-");
const fileName = `${date}.log`;
fs.writeFileSync(this.pathToLogFile, "");
return fileName;
}
private toString(object: any) {
if (typeof object === "object")
return Object.prototype.toString.call(object);
else if (Array.isArray(object)) return object.join();
else if (typeof object === "number") return object.toString();
else if (typeof object === "boolean") return object.toString();
else if (typeof object === "string") return object;
return "Cannot transfrom object to string";
}
private writeEntryToFile(
options: {
tag?: string;
type: "log" | "error" | "warn" | "info";
divider?: string;
},
...args: any
) {
const strings: string[] = [];
for (const argument of args) {
const string = this.toString(argument);
strings.push(string);
}
const dateString = new Date().toLocaleString(undefined, {
formatMatcher: "best fit",
dateStyle: "short",
timeStyle: "medium",
});
const divider = options.divider === undefined ? "\n\t" : options.divider;
let string = `[${dateString}] `;
if (this.tag !== undefined) string += `[LTAG: ${this.tag}`;
if (options.tag !== undefined) string += ` TAG: ${options.tag}] `;
else string += "] ";
string += `[${options.type.toUpperCase()}] `;
string += strings.join(divider);
fs.appendFileSync(this.pathToLogFile, string);
}
public log(...args: any) {
if (!DEBUG) console.log(...args);
this.writeEntryToFile({ type: "log" }, ...args);
}
public error(...args: any) {
if (!DEBUG) console.error(...args);
this.writeEntryToFile({ type: "error" }, ...args);
}
public warn(...args: any) {
if (!DEBUG) console.warn(...args);
this.writeEntryToFile({ type: "warn" }, ...args);
}
public info(...args: any) {
if (!DEBUG) console.info(...args);
this.writeEntryToFile({ type: "info" }, ...args);
}
}

0
src/VMR/Bus.ts Normal file
View File

109
src/VMR/Control.ts Normal file
View File

@ -0,0 +1,109 @@
import { Remote } from "./Remote";
import { validation } from "./decorators/validation";
export enum LoginReturnType {
"OK" = 0,
"OK but Voicemeeter Application not launched" = 1,
"Cannot get client (unexpected)" = -1,
"Unexpected login (logout was expected before)" = -2,
}
export enum VoicemeeterType {
Basic = 1,
Banana = 2,
Potato = 3,
}
export class Control extends Remote {
private static buses = {
Potato: { total: 8, virtual: 3, physical: 5 },
Banana: { total: 5, virtual: 2, physical: 3 },
Basic: { total: 2, virtual: 1, physical: 1 },
};
private static strips = {
Potato: { total: 8, virtual: 3, physical: 5 },
Banana: { total: 8, virtual: 3, physical: 5 },
Basic: { total: 8, virtual: 3, physical: 5 },
};
public loggedIn: boolean = false;
public parametersUpdated: boolean = false;
private interval: NodeJS.Timeout | undefined;
@validation()
public get voicemeeterType(): VoicemeeterType {
const buffer = Buffer.alloc(512);
this.run("VBVMR_GetVoicemeeterType", buffer);
return buffer.readInt32LE();
}
@validation()
public get voicemeeterVersion(): number[] {
const buffer = Buffer.alloc(512);
this.run("VBVMR_GetVoicemeeterVersion", buffer);
const version = buffer.readInt32LE();
return [
(version & 0xff000000) >> 24,
(version & 0x00ff0000) >> 16,
(version & 0x0000ff00) >> 8,
version & 0x000000ff,
];
}
@validation()
public get availableBuses() {
return Control.buses[
VoicemeeterType[this.voicemeeterType] as keyof typeof VoicemeeterType
];
}
@validation()
public get availableStrips() {
return Control.strips[
VoicemeeterType[this.voicemeeterType] as keyof typeof VoicemeeterType
];
}
public minVersion(minVersion: number) {
if (minVersion > this.voicemeeterType) throw new Error("Test");
}
public login() {
if (this.loggedIn) return;
const createTimer = () => {
this.interval = setInterval(() => {
const result = this.run("VBVMR_IsParametersDirty");
if (result !== 0 && result !== 1)
throw new Error(
`An error occurred while checking for updated parameters. DLL result code: ${result}`
);
const isUpdated = result === 1 ? true : false;
if (this.parametersUpdated !== isUpdated && isUpdated)
console.log(`Updated state`);
this.parametersUpdated = isUpdated;
}, 20);
};
if (this.interval === undefined) createTimer();
else {
clearInterval(this.interval);
createTimer();
}
const result = this.run("VBVMR_Login") as LoginReturnType;
if (result !== 0) throw new Error(`DLL result code: ${result}`);
this.loggedIn = true;
return result;
}
public logout() {
if (!this.loggedIn) return;
if (this.interval !== undefined) {
clearInterval(this.interval);
this.interval = undefined;
}
const result = this.run("VBVMR_Logout") as number;
if (result !== 0) throw new Error(`DLL result code: ${result}`);
this.loggedIn = false;
return result;
}
}

53
src/VMR/Remote.ts Normal file
View File

@ -0,0 +1,53 @@
import { IKoffiLib, KoffiFunction, load } from "koffi";
import { Parser } from "../utils/getFuncsNamesFromVRHFile";
import path from "path";
export type RemoteFuncs = (typeof Remote.allowedFuncs)[number];
export class Remote {
public static allowedFuncs = [
"VBVMR_Login",
"VBVMR_Logout",
"VBVMR_GetVoicemeeterType",
"VBVMR_SetParameterFloat",
"VBVMR_SetParameterStringA",
"VBVMR_SetParameters",
"VBVMR_IsParametersDirty",
"VBVMR_GetParameterFloat",
"VBVMR_GetParameterStringA",
"VBVMR_Output_GetDeviceNumber",
"VBVMR_Input_GetDeviceNumber",
"VBVMR_GetVoicemeeterVersion"
] as const;
public dllFuncs = {} as Partial<
Record<RemoteFuncs, KoffiFunction>
>;
public dll: IKoffiLib;
constructor(dllPath: string) {
this.dll = load(dllPath);
}
public async initialize() {
const parser = new Parser(path.join(__dirname,"../", "VoicemeeterRemote.h"));
const results = await parser.parse();
for (const func of Remote.allowedFuncs) {
const funcHeader = results.find((r) => r.name === func);
if (funcHeader === undefined)
throw new Error(`Allowed function not found in header file. Caused by function: ${func}`);
this.dllFuncs[func] = this.dll.func(funcHeader.rawString);
}
}
public run(
command: RemoteFuncs,
...args: any
) {
if (!Remote.allowedFuncs.includes(command))
throw new Error("Used function isn't listed in allowed");
const func = this.dllFuncs[command];
if(func === undefined) throw new Error("Executed function wasn't initialized");
return func(...args)
}
}

124
src/VMR/Strip.ts Normal file
View File

@ -0,0 +1,124 @@
import { VMObject } from "./VMObject";
import { minType } from "./decorators/minVersion";
import { validation } from "./decorators/validation";
import { Control } from "./Control";
export type OutputBuses = Record<"A" | "B", Record<number, boolean>>;
export class Strip extends VMObject {
@validation()
public get Mono(): boolean {
return this.getParamValueBoolean(
this.buildParameterString("Mono")
)[1];
}
public set Mono(v: boolean) {
this.setParamValueBoolean(this.buildParameterString("Mono"), v);
}
@validation()
public get Mute(): boolean {
return this.getParamValueBoolean(
this.buildParameterString("Mute")
)[1];
}
public set Mute(v: boolean) {
this.setParamValueBoolean(this.buildParameterString("Mute"), v);
}
@validation()
public get Solo(): boolean {
return this.getParamValueBoolean(
this.buildParameterString("Solo")
)[1];
}
public set Solor(v: boolean) {
this.setParamValueBoolean(this.buildParameterString("Solo"), v);
}
@validation()
public get MuteCenter(): boolean {
return this.getParamValueBoolean(this.buildParameterString("MC"))[1];
}
public set MuteCenter(v: boolean) {
this.setParamValueBoolean(this.buildParameterString("MC"), v);
}
@validation()
public get Gain(): number {
return this.getParamValueNumber(this.buildParameterString("Gain"))[1];
}
public set Gain(v: number) {
this.setParamValueNumber(this.buildParameterString("Gain"), v);
}
@minType(3)
public get GainLayers(): number[] {
const buses: number[] = [];
for (let i = 0; i < this.control.availableBuses.total; i++) {
buses.push(
this.getParamValueNumber(
this.buildParameterString("GainLayer", i)
)[1]
);
}
return buses;
}
public set GainLayers(changedBuses: (undefined | number)[]) {
let script = "";
for (let i = 0; i < changedBuses.length; i++) {
const gainForBus = changedBuses[i];
script += `${this.buildParameterString(
"GainLayer", i,
)} = ${gainForBus};`;
}
this.setParameters(script);
}
@validation()
public get outputBuses() {
const buses: OutputBuses = { A: {}, B: {} };
for (let i = 0; i < this.control.availableBuses.physical; i++) {
buses["A"][i + 1] = this.getParamValueBoolean(
this.buildParameterString(`A${i + 1}`)
)[1];
}
for (let i = 0; i < this.control.availableBuses.virtual; i++) {
buses["B"][i + 1] = this.getParamValueBoolean(
this.buildParameterString(`B${i + 1}`)
)[1];
}
return buses;
}
public set outputBuses(changedBuses: Partial<OutputBuses>) {
let script = "";
if (changedBuses.A !== undefined)
for (const busId in changedBuses.A) {
if (Object.prototype.hasOwnProperty.call(changedBuses.A, busId)) {
const element = changedBuses.A[busId];
script += `${this.buildParameterString(`A${busId}`)} = ${
element ? "1" : "0"
};`;
}
}
if (changedBuses.B !== undefined)
for (const busId in changedBuses.B) {
if (Object.prototype.hasOwnProperty.call(changedBuses.B, busId)) {
const outputToBus = changedBuses.B[busId];
script += `${this.buildParameterString(`B${busId}`)} = ${
outputToBus ? "1" : "0"
};`;
}
}
this.setParameters(script);
}
constructor(control: Control, public id: number) {
// control.validation();
super(control, "Strip", id);
}
initialize(): void {
console.log("Nothing to initialize");
}
}

91
src/VMR/VMObject.ts Normal file
View File

@ -0,0 +1,91 @@
import { Control } from "./Control";
import { RemoteFuncs } from "./Remote";
export type ObjectType = "Strip" | "Bus";
export abstract class VMObject {
constructor(
public control: Control,
public objectType: ObjectType,
public objectId?: number
) {
this.initialize();
}
abstract initialize(): void;
protected buildParameterString(...path: (string | number)[]) {
let string = `${this.objectType}${
this.objectId !== undefined ? `[${this.objectId}]` : ""
}`;
for (let pathId = 0; pathId < path.length; pathId++) {
const parameter = path[pathId];
let idForParameter: number | undefined = undefined;
if (typeof path[pathId + 1] === "number") {
idForParameter = path[pathId + 1] as number;
pathId++;
}
string += `.${parameter}${
idForParameter !== undefined ? `[${idForParameter}]` : ""
}`;
}
return string;
}
protected getParamValueString(parameter: string): [number, string] {
const buffer = Buffer.alloc(512);
const result = this.control.run(
"VBVMR_GetParameterStringA",
parameter,
buffer
);
return [result, buffer.toString()];
}
protected getParamValueBoolean(parameter: string): [number, boolean] {
const buffer = Buffer.alloc(512);
const result = this.control.run(
"VBVMR_GetParameterStringA",
parameter,
buffer
);
return [result, buffer.subarray(0, 5).toString().startsWith("1")];
}
protected getParamValueNumber(parameter: string): [number, number] {
const buffer = Buffer.alloc(512);
const result = this.control.run(
"VBVMR_GetParameterFloat",
parameter,
buffer
);
return [result, buffer.readFloatLE()];
}
protected setParamValueString(parameter: string, value: string): number {
const result = this.control.run(
"VBVMR_SetParameterStringA",
parameter,
value
);
return result;
}
protected setParamValueBoolean(parameter: string, value: boolean): number {
const result = this.control.run(
"VBVMR_SetParameterFloat",
parameter,
value ? 1 : 0
);
return result;
}
protected setParamValueNumber(parameter: string, value: number): number {
const result = this.control.run(
"VBVMR_GetParameterFloat",
parameter,
value
);
return result;
}
protected setParameters(script: string): number {
const result = this.control.run("VBVMR_SetParameters", script);
return result;
}
}

View File

@ -0,0 +1,23 @@
import { validate } from "./validation";
import { Control, VoicemeeterType } from "../Control";
export function minType(minType: VoicemeeterType) {
return (object: any, name: string, descriptor: PropertyDescriptor) => {
const keys = ["set", "get", "value"] as (keyof typeof descriptor)[];
for (const key of keys) {
if (descriptor[key] === undefined) continue;
const originalFunction = descriptor[key];
descriptor[key] = function (this: Control, ...args: any[]) {
validate.bind(this)();
if (this.voicemeeterType < minType)
throw new Error(
`Used property/method with name: ${name} is not supported by this type of Voicemeeter. Your type: ${
VoicemeeterType[this.voicemeeterType]
} Minimal required type is ${VoicemeeterType[minType]}`
);
return originalFunction.apply(this, args);
};
}
return descriptor;
};
}

View File

@ -0,0 +1,26 @@
import { Control } from "../Control";
import { Strip } from "../Strip";
type validateThisType = Control | Strip;
export function validate(this: validateThisType) {
let isValid = true;
if (this instanceof Control && !this.loggedIn) isValid = false;
else if (this instanceof Strip && !this.control.loggedIn) isValid = false;
if (!isValid) throw new Error("Communication pipe isn't open");
}
export function validation() {
return (object: any, name: string, descriptor: PropertyDescriptor) => {
const keys = ["set", "get", "value"] as (keyof typeof descriptor)[];
for (const key of keys) {
if (descriptor[key] === undefined) continue;
const originalFunction = descriptor[key];
descriptor[key] = function (this: validateThisType, ...args: any[]) {
validate.bind(this)();
return originalFunction.apply(this, args);
};
}
return descriptor;
};
}

30
src/main.ts Normal file
View File

@ -0,0 +1,30 @@
import { Control, VoicemeeterType } from "./VMR/Control";
import { Logger } from "./Logger";
let control: Control;
let interval: NodeJS.Timeout;
const logger = new Logger({});
function exit() {
clearInterval(interval);
control.logout();
logger.log("Logout");
process.exit();
}
const exitEvents = [
{ name: "SIGINT" },
{ name: "SIGABRT" },
{ name: "SIGKILL" },
{ name: "SIGUSR1" },
{ name: "SIGUSR2" },
{ name: "beforeExit" },
{ name: "exit" },
{ name: "uncaughtException" },
{ name: "unhandledRejection" },
] as { name: NodeJS.Signals }[];
for (const event of exitEvents) {
process.on(event.name, exit.bind(null));
}

View File

@ -0,0 +1,12 @@
import fs from "fs/promises";
import { Parser } from "./getFuncsNamesFromVRHFile";
async function main() {
const parser = new Parser("./VoicemeeterRemote.h");
console.log(await parser.parse());
const fileContent = await fs.readFile("./src/Remote.ts", {encoding: "utf-8"});
// fileContent.
}
main();

View File

@ -0,0 +1,11 @@
export function getEnvVariable(
name: string,
type?: "string" | "boolean" | "number"
) {
const variable = process.env[name];
if (process.env[name] === undefined) return undefined;
if (type === undefined) return variable;
if (type === "boolean") return new Boolean(variable).valueOf();
else if (type === "string") return variable;
else if (type === "number") return new Number(variable).valueOf();
}

View File

@ -0,0 +1,56 @@
import { promises as fs } from "fs";
interface Parameter {
name: string;
type: string;
}
interface ParsedFunction {
name: string;
params: Parameter[];
returnType: string;
rawString: string;
}
export class Parser {
constructor(private filePath: string) {}
public async parse() {
const data = await fs.readFile(this.filePath, { encoding: "utf-8" });
const lines = data.split("\n").map((l) => l.trim());
let i = 0;
let isComment = false;
const functionArray = [] as ParsedFunction[];
while (++i < lines.length) {
const line = lines[i];
if (!isComment && line.includes("/*")) {
isComment = true;
continue;
}
if (isComment && line.includes("*/")) {
isComment = false;
continue;
}
if (line.includes("typedef") || line.includes("//") || line.includes("#"))
continue;
if (line.includes(" __stdcall ") && line.endsWith(");")) {
const split = line
.substring(0, line.length - ");".length)
.split(" __stdcall ");
const returnType = split[0];
const [functionName, parametersString] = split[1].split("(");
let params = parametersString
.split(", ")
.map((p) => p.split(/\s(?!\*)/));
functionArray.push({
rawString: line,
name: functionName,
params: params.map((p) => ({ name: p[1], type: p[0] })),
returnType,
});
}
}
return functionArray;
}
}

8
src/variables.ts Normal file
View File

@ -0,0 +1,8 @@
import { getEnvVariable } from "./utils/getEnvVariable";
export const DEBUG = getEnvVariable("DEBUG", "boolean");
export const VOICEMEETER_REMOTE_DLL = getEnvVariable(
"VOICEMEETER_REMOTE_DLL",
"string"
);
export const API_PORT = getEnvVariable("API_PORT", "number");

109
tsconfig.json Normal file
View File

@ -0,0 +1,109 @@
{
"include": ["src/**/*", "src/utils/**/*"],
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
"experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}