From d19f0c2f8f3f69ce1011671c2c71d9477e105c71 Mon Sep 17 00:00:00 2001 From: arnef Date: Tue, 8 Sep 2026 10:03:09 +0200 Subject: [PATCH] Replace backend submodule with Elysia/Bun sportsmanager backend --- apps/backend | 1 - apps/backend/.gitignore | 42 +++ apps/backend/README.md | 15 ++ apps/backend/package.json | 15 ++ apps/backend/src/index.ts | 513 +++++++++++++++++++++++++++++++++++++ apps/backend/tsconfig.json | 103 ++++++++ 6 files changed, 688 insertions(+), 1 deletion(-) delete mode 160000 apps/backend create mode 100644 apps/backend/.gitignore create mode 100644 apps/backend/README.md create mode 100644 apps/backend/package.json create mode 100644 apps/backend/src/index.ts create mode 100644 apps/backend/tsconfig.json diff --git a/apps/backend b/apps/backend deleted file mode 160000 index 3dd31e6..0000000 --- a/apps/backend +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3dd31e693b04a7cf90ca24a586aebeb979b4b795 diff --git a/apps/backend/.gitignore b/apps/backend/.gitignore new file mode 100644 index 0000000..87e5610 --- /dev/null +++ b/apps/backend/.gitignore @@ -0,0 +1,42 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel + +**/*.trace +**/*.zip +**/*.tar.gz +**/*.tgz +**/*.log +package-lock.json +**/*.bun \ No newline at end of file diff --git a/apps/backend/README.md b/apps/backend/README.md new file mode 100644 index 0000000..688c87e --- /dev/null +++ b/apps/backend/README.md @@ -0,0 +1,15 @@ +# Elysia with Bun runtime + +## Getting Started +To get started with this template, simply paste this command into your terminal: +```bash +bun create elysia ./elysia-example +``` + +## Development +To start the development server run: +```bash +bun run dev +``` + +Open http://localhost:3000/ with your browser to see the result. \ No newline at end of file diff --git a/apps/backend/package.json b/apps/backend/package.json new file mode 100644 index 0000000..c9a287d --- /dev/null +++ b/apps/backend/package.json @@ -0,0 +1,15 @@ +{ + "name": "backend", + "version": "1.0.50", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "bun run --watch src/index.ts" + }, + "dependencies": { + "elysia": "latest" + }, + "devDependencies": { + "bun-types": "latest" + }, + "module": "src/index.js" +} diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts new file mode 100644 index 0000000..083cdc0 --- /dev/null +++ b/apps/backend/src/index.ts @@ -0,0 +1,513 @@ +import { Elysia } from "elysia"; + +const isDev = true; +const config = { + idFormat: isDev ? "utf8" : "base64url", +}; + +class Sportsmanager { + private baseUrl: string; + private params: Record; + + public constructor(baseUrl, params = {}) { + this.baseUrl = baseUrl; + this.params = { + ...params, + format: "json", + option: "com_sportsmanager", + view: "sportsmanager", + }; + } + + private fetch = (params, options) => + fetch( + `${this.baseUrl}/index.php?${new URLSearchParams({ + ...this.params, + ...params, + }).toString()}`, + options, + ); + + private id = (id: string | number) => + Buffer.from(`sportsmanager:${new URL(this.baseUrl).host}:${id}`).toString( + config.idFormat, + ); + private number = (value: string | number | null | undefined) => + (value ?? null) !== null && !isNaN(+value) ? Number(value) : null; + private teamName = (value: string) => value.replace("(NR)", ""); + public getOverview = async () => { + const resp = await this.fetch({ content: "aktuelle_begegnungen" }); + + return resp + .json() + .then(({ data: { running_matches, finished_matches, next_matches } }) => { + return [...running_matches, ...finished_matches, ...next_matches] + .filter(({ zeitpunkt }) => !!zeitpunkt) + .sort((a, b) => { + if (a.zeitpunkt === b.zeitpunkt) { + return Number(a.begegnung_id) < Number(b.begegnung_id) ? -1 : 1; + } + + return a.zeitpunkt < b.zeitpunkt ? -1 : 1; + }) + .map((match) => { + return { + id: this.id(match.begegnung_id), + scheduledAt: new Date(match.zeitpunkt), + homeTeam: { + id: this.id(`${match.heim_team_id}:${match.heim_teamgruppe}`), + name: this.teamName(match.heim_name), + logoUrl: match.heim_bild || null, + goals: this.number(match.heim_punkte), + points: this.number(match.heim_spielpunkte), + }, + awayTeam: { + id: this.id(`${match.gast_team_id}:${match.gast_teamgruppe}`), + name: this.teamName(match.gast_name), + logoUrl: match.gast_bild || null, + goals: this.number(match.gast_punkte), + points: this.number(match.gast_spielpunkte), + }, + event: { + id: this.id(match.veranstaltung_id), + name: match.bezeichnung, + matchday: match.spieltag_titel || match.spieltag, + }, + tableNumber: match.tisch ? `${match.tisch}` : null, + isNonSmoking: Boolean( + match.nichtraucherschutz || match.heim_name.includes("(NR)"), + ), + status: + this.number(match.heim_punkte) !== null && + this.number(match.gast_punkte) !== null + ? Number(match.zwischenergebnis) + ? "LIVE" + : Number(match.unbestaetigtes_ergebnis_id) + ? "AWAITING_APPROVAL" + : "COMPLETED" + : "SCHEDULED", + // orig: match, + }; + }); + }); + }; + + public getMatch = (id: string) => + this.fetch({ + content: "aktuelle_begegnungen", + task: "begegnung_spielplan", + id, + }) + .then((r) => r.json()) + .then(({ data }) => { + const { + begegnung, + heim_team, + heim_spieler, + gast_team, + gast_spieler, + veranstaltung, + modus, + spiele, + spielort, + } = data; + const player = (s) => ({ + id: this.id(s.spieler_id), + name: `${s.vorname} ${s.nachname}`, + firstName: s.vorname, + lastName: s.nachname, + imageUrl: s.bild, + }); + const slotPlayer = (game, key) => + game && game[`${key}_id`] + ? { + id: this.id(game[`${key}_id`]), + firstName: game[`${key}_vorname`], + lastName: game[`${key}_nachname`], + imageUrl: game[`${key}_bild`], + } + : null; + return { + id: this.id(begegnung.begegnung_id), + scheduledAt: new Date(begegnung.zeitpunkt), + homeTeam: { + id: this.id(`${heim_team.team_id}:${heim_team.teamgruppe_id}`), + name: heim_team.teamname, + logoUrl: heim_team.teambild, + goals: this.number(begegnung.heim_punkte), + points: this.number(begegnung.heim_spielpunkte), + }, + awayTeam: { + id: this.id(`${gast_team.team_id}:${gast_team.teamgruppe_id}`), + name: gast_team.teamname, + logoUrl: gast_team.teambild, + goals: this.number(begegnung.gast_punkte), + points: this.number(begegnung.gast_spielpunkte), + }, + event: { + id: this.id(veranstaltung.veranstaltung_id), + name: veranstaltung.bezeichnung, + matchday: begegnung.spieltag_titel || begegnung.spieltag, + }, + venue: spielort + ? { + name: spielort.name, + } + : undefined, + tableNumber: begegnung.tisch ? `${begegnung.tisch}` : null, + isNonSmoking: Boolean( + begegnung.nichtraucherschutz || heim_team.teamname.includes("(NR)"), + ), + status: + this.number(begegnung.heim_punkte) !== null && + this.number(begegnung.gast_punkte) !== null + ? Number(begegnung.zwischenergebnis) + ? "LIVE" + : Number(begegnung.unbestaetigtes_ergebnis_id) + ? "AWAITING_APPROVAL" + : "COMPLETED" + : "SCHEDULED", + + homeRosters: heim_spieler.map(player), + awayRosters: gast_spieler.map(player), + slots: Object.values( + modus.modus.split(",").reduce((acc, cur, idx) => { + const game = spiele[idx] ?? null; + if (!acc[cur]) { + acc[cur] = { + key: cur, + type: + cur[0] === "E" + ? "SINGLE" + : cur[0] === "D" + ? "DOUBLE" + : "SHOOT_OUT", + sets: [], + home: { positionKey: cur.substring(0, 2) }, + away: { positionKey: cur.substring(2, 4) }, + }; + + switch (acc[cur].type) { + case "SINGLE": + acc[cur].home.players = [ + slotPlayer(game, "heim_spieler_1"), + ]; + acc[cur].away.players = [ + slotPlayer(game, "gast_spieler_1"), + ]; + break; + case "DOUBLE": + acc[cur].home.players = [ + slotPlayer(game, "heim_spieler_1"), + slotPlayer(game, "heim_spieler_2"), + ]; + acc[cur].away.players = [ + slotPlayer(game, "gast_spieler_1"), + slotPlayer(game, "gast_spieler_2"), + ]; + break; + case "SHOOT_OUT": + acc[cur].home.players = null; + acc[cur].away.players = null; + break; + } + } + + const set = game + ? { + homeGoals: this.number(game.teamspiel_heim_punkte), + awayGoals: this.number(game.teamspiel_gast_punkte), + } + : null; + acc[cur].sets.push(set); + return acc; + }, {}), + ), + // orig: data, + }; + }); + + public getMatchStats = async (id: string) => { + const match = await this.fetch({ + content: "aktuelle_begegnungen", + task: "begegnung_spielplan", + id, + }) + .then((r) => r.json()) + .then((r) => r.data); + + const teams = { + [match.heim_team.team_id]: { + id: this.id( + `${match.heim_team.team_id}:${match.heim_team.teamgruppe_id}`, + ), + name: match.heim_team.teamname, + logoUrl: match.heim_team.teambild, + }, + + [match.gast_team.team_id]: { + id: this.id( + `${match.gast_team.team_id}:${match.gast_team.teamgruppe_id}`, + ), + name: match.gast_team.teamname, + logoUrl: match.gast_team.teambild, + }, + }; + const [homeTeam, awayTeam] = await Promise.all([ + this.fetch({ + content: "teams", + task: "team_details", + id: match.heim_team.team_id, + }) + .then((r) => r.json()) + .then((r) => r.data), + this.fetch({ + content: "teams", + task: "team_details", + id: match.gast_team.team_id, + }) + .then((r) => r.json()) + .then((r) => r.data), + ]); + + const homeTeamMatches = homeTeam.begegnungen + .filter((a) => { + return ( + !(!a.heim_spielpunkte && !a.gast_spielpunkte) && + a.zeitpunkt < match.begegnung.zeitpunkt + ); + }) + .sort((a, b) => (a.zeitpunkt < b.zeitpunkt ? 1 : -1)); + const home = { + stats: homeTeamMatches.reduce( + (acc, match, index) => { + const weight = Math.max(0.2, 1 - index * 0.8); + const isHome = Number(match.auswaerts) === 0; + + const myGoals = isHome + ? match.heim_spielpunkte + : match.gast_spielpunkte; + const oppGoals = isHome + ? match.gast_spielpunkte + : match.heim_spielpunkte; + + // Punkte für Ausgang (Sieg = 1, Remis = 0.5, Niederlage = 0) + const resultPoints = + myGoals > oppGoals ? 1 : myGoals === oppGoals ? 0.5 : 0; + // Torverhältnis-Faktor (verhindert Division durch 0 bei 0 Gegentoren) + const goalFactor = (myGoals + 1) / (oppGoals + 1); + + acc.weightSum += weight; + acc.scoreSum += (resultPoints * 0.6 + goalFactor * 0.4) * weight; + return acc; + }, + { + weightSum: 0, + scoreSum: 0, + }, + ), + form: homeTeamMatches + .slice(0, 5) + .reverse() + .map((b) => { + if (b.heim_spielpunkte === b.gast_spielpunkte) { + return "DRAW"; + } + return b.auswaerts + ? b.heim_spielpunkte < b.gast_spielpunkte + ? "WIN" + : "LOST" + : b.heim_spielpunkte < b.gast_spielpunkte + ? "LOST" + : "WIN"; + }), + }; + + const awayTeamMatches = awayTeam.begegnungen + .filter((a) => { + return ( + !(!a.heim_spielpunkte && !a.gast_spielpunkte) && + a.zeitpunkt < match.begegnung.zeitpunkt + ); + }) + .sort((a, b) => (a.zeitpunkt < b.zeitpunkt ? 1 : -1)); + + const away = { + stats: awayTeamMatches.reduce( + (acc, match, index) => { + const weight = Math.max(0.2, 1 - index * 0.8); + const isHome = Number(match.auswaerts) === 0; + + const myGoals = isHome + ? match.heim_spielpunkte + : match.gast_spielpunkte; + const oppGoals = isHome + ? match.gast_spielpunkte + : match.heim_spielpunkte; + + // Punkte für Ausgang (Sieg = 1, Remis = 0.5, Niederlage = 0) + const resultPoints = + myGoals > oppGoals ? 1 : myGoals === oppGoals ? 0.5 : 0; + // Torverhältnis-Faktor (verhindert Division durch 0 bei 0 Gegentoren) + const goalFactor = (myGoals + 1) / (oppGoals + 1); + + acc.weightSum += weight; + acc.scoreSum += (resultPoints * 0.6 + goalFactor * 0.4) * weight; + return acc; + }, + { + weightSum: 0, + scoreSum: 0, + }, + ), + form: awayTeamMatches + .slice(0, 5) + .reverse() + .map((b) => { + if (b.heim_spielpunkte === b.gast_spielpunkte) { + return "DRAW"; + } + return b.auswaerts + ? b.heim_spielpunkte < b.gast_spielpunkte + ? "WIN" + : "LOST" + : b.heim_spielpunkte < b.gast_spielpunkte + ? "LOST" + : "WIN"; + }), + }; + + const homePower = + home.stats.weightSum > 0 ? home.stats.scoreSum / home.stats.weightSum : 1; + const awayPower = + away.stats.weightSum > 0 ? away.stats.scoreSum / away.stats.weightSum : 1; + + // 4. Heimvorteil für das anstehende Match draufschlagen (+12% auf die Power) + const finalHomePower = homePower * 1.12; + const finalAwayPower = awayPower; + + // Gesamte Power des Matches + const totalPower = finalHomePower + finalAwayPower; + + // Grundwahrscheinlichkeiten für Sieg + const rawHomeProb = finalHomePower / totalPower; + const rawAwayProb = finalAwayPower / totalPower; + + // 5. Unentschieden extrahieren + // Je näher die "Power" der beiden Teams beieinander liegt, desto höher die Remis-Chance + const powerDiff = Math.abs(finalHomePower - finalAwayPower); + const drawPercent = Math.round(Math.max(15, 26 - powerDiff * 15)); + + // Rest-Prozente auf Heim- und Auswärtssieg aufteilen + const restPercent = 100 - drawPercent; + const homePercent = Math.round(rawHomeProb * restPercent); + const awayPercent = Math.round(rawAwayProb * restPercent); + + return { + prediction: { + homeWinChance: homePercent, + drawChance: drawPercent, + awayWinChance: awayPercent, + }, + form: { + home: home.form, + away: away.form, + }, + standing: Number(match.veranstaltung.tabellenwertung) + ? { + home: { + pos: Number(match.heim_team.platz), + name: match.heim_team.teamname, + logo: match.heim_team.teambild, + games: + match.heim_team.siege + + match.heim_team.unentschieden + + match.heim_team.niederlagen, + points: match.heim_team.begegnungspunkte, + diff: `${match.heim_team.spielpunkte_differenz > 0 ? "+" : ""}${match.heim_team.spielpunkte_differenz}`, + }, + away: { + pos: Number(match.gast_team.platz), + name: match.gast_team.teamname, + logo: match.gast_team.teambild, + games: + match.gast_team.siege + + match.gast_team.unentschieden + + match.gast_team.niederlagen, + points: match.gast_team.begegnungspunkte, + diff: `${match.gast_team.spielpunkte_differenz > 0 ? "+" : ""}${match.gast_team.spielpunkte_differenz}`, + }, + } + : undefined, + firstLeg: match.hinspiel + ? { + homeTeam: { + ...teams[match.hinspiel.heim_team_id], + goals: Number(match.hinspiel.heim_punkte), + points: Number(match.hinspiel.heim_spielpunkte), + }, + awayTeam: { + ...teams[match.hinspiel.gast_team_id], + goals: Number(match.hinspiel.gast_punkte), + points: Number(match.hinspiel.gast_spielpunkte), + }, + } + : undefined, + // orig: { teams, home, away, match, homeTeam, awayTeam }, + }; + }; +} + +const systems = Object.fromEntries( + [ + ["https://dtfb.de", Sportsmanager], + ["https://kickern-hamburg.de", Sportsmanager, { spielort_heim: "1" }], + ["https://nwtfv.com", Sportsmanager], + ["https://stfv.de", Sportsmanager], + ["https://ntfv.de", Sportsmanager, { spielort_heim: "1" }], + ].map(([domain, Instance, params]) => [ + new URL(domain).host, + new Instance(domain, params), + ]), +); + +const app = new Elysia() + .derive(({ error }) => ({ + resolveGlobalId(id) { + try { + const [, system, remoteId] = Buffer.from(id, config.idFormat) + .toString("utf8") + .split(":"); + const provider = systems[system]; + return { provider, remoteId }; + } catch (ex) { + return { error: error(400, "") }; + } + }, + })) + .group("/v1", (v1) => + v1 + .get("/matches", async () => { + return Promise.all( + Object.values(systems).map((o) => o.getOverview()), + ).then((r) => r.flat()); + }) + .get("/matches/:id", async ({ params: { id }, resolveGlobalId }) => { + const { provider, remoteId } = resolveGlobalId(id); + + return provider.getMatch(remoteId); + }) + .get( + "/matches/:id/stats", + async ({ params: { id }, resolveGlobalId }) => { + const { provider, remoteId } = resolveGlobalId(id); + + return provider.getMatchStats(remoteId); + }, + ), + ) + .listen({ port: 3000, host: "0.0.0.0" }); + +console.log( + `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`, +); diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json new file mode 100644 index 0000000..1ca2350 --- /dev/null +++ b/apps/backend/tsconfig.json @@ -0,0 +1,103 @@ +{ + "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 TC39 stage 2 draft 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": "ES2022", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* 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": ["bun-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. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''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. */ + // "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": "./", /* 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. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "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. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "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. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "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. */ + } +}