add basic wanikani auth
This commit is contained in:
1 parent
a82e49ae34
commit
646c7aed1f
8 files changed
+2490
No files matched your search
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@jaquiz/server",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/index.js",
|
||||
"private": true,
|
||||
"repository": "git@git.polv.cc:jaquiz/server.git",
|
||||
"author": "Pacharapol Withayasakpunt <polv@polv.cc>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"ts": "ts-node",
|
||||
"build": "rm -r lib && tsc --rootDir src --outDir lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@databases/pg": "^5.1.1",
|
||||
"axios": "^0.23.0",
|
||||
"connect-pg-simple": "^7.0.0",
|
||||
"dayjs": "^1.10.7",
|
||||
"fastify": "^3.22.0",
|
||||
"fastify-cookie": "^5.3.1",
|
||||
"fastify-rate-limit": "^5.6.2",
|
||||
"fastify-session": "^5.2.1",
|
||||
"fastify-static": "^4.4.2",
|
||||
"fastify-swagger": "^4.12.4",
|
||||
"jsonschema-definer": "^1.3.2",
|
||||
"short-uuid": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.4.0",
|
||||
"@types/connect-pg-simple": "^4.2.4",
|
||||
"@types/node": "^16.10.9",
|
||||
"@types/pino": "^6.3.11",
|
||||
"better-sqlite3": "^7.4.3",
|
||||
"import-sort-parser-typescript": "^6.0.0",
|
||||
"ts-node": "^10.3.0",
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
"importSort": {
|
||||
".js, .ts": {
|
||||
"parser": "typescript",
|
||||
"style": "module"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import qs from 'querystring'
|
||||
|
||||
import sql from '@databases/sql'
|
||||
import axios from 'axios'
|
||||
import ConnectPG from 'connect-pg-simple'
|
||||
import fastify, { FastifyPluginAsync } from 'fastify'
|
||||
import fCookie from 'fastify-cookie'
|
||||
import rateLimit from 'fastify-rate-limit'
|
||||
import fSession from 'fastify-session'
|
||||
import fastifySwagger from 'fastify-swagger'
|
||||
|
||||
import { db, isDev } from '../shared'
|
||||
|
||||
const apiRouter: FastifyPluginAsync = async (f) => {
|
||||
f.register(fCookie)
|
||||
f.register(fSession, {
|
||||
secret: process.env['SECRET']!,
|
||||
// @ts-ignore
|
||||
store: new (ConnectPG(fSession))(
|
||||
process.env['DATABASE_URL']
|
||||
? {
|
||||
conString: process.env['DATABASE_URL'],
|
||||
}
|
||||
: {
|
||||
conObject: {
|
||||
user: process.env['POSTGRES_USER'],
|
||||
password: process.env['POSTGRES_PASSWORD'],
|
||||
database: process.env['POSTGRES_DB'],
|
||||
host: process.env['POSTGRES_HOST'],
|
||||
},
|
||||
}
|
||||
),
|
||||
})
|
||||
|
||||
f.register(rateLimit, {
|
||||
max: 10,
|
||||
timeWindow: '1 second',
|
||||
allowList: (req) => {
|
||||
if (req.routerPath === '/api/quiz/getSrsLevel') {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
})
|
||||
|
||||
f.register(fastifySwagger, {
|
||||
openapi: {
|
||||
security: [
|
||||
{
|
||||
BearerAuth: [],
|
||||
},
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
BearerAuth: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
exposeRoute: isDev,
|
||||
routePrefix: '/doc',
|
||||
})
|
||||
|
||||
f.addHook('preHandler', async (req) => {
|
||||
const { body, log } = req
|
||||
|
||||
if (body && typeof body === 'object' && body.constructor === Object) {
|
||||
log.info({ body }, 'parsed body')
|
||||
}
|
||||
})
|
||||
|
||||
f.addHook('preHandler', async (req) => {
|
||||
if (['/api/doc', '/api/settings'].some((s) => req.url.startsWith(s))) {
|
||||
return
|
||||
}
|
||||
|
||||
if (req.session['userId']) {
|
||||
return
|
||||
}
|
||||
|
||||
const [, apiKey] =
|
||||
/^Bearer (.+)$/.exec(req.headers.authorization || '') || []
|
||||
|
||||
if (!apiKey) {
|
||||
throw { statusCode: 401, message: 'no apiKey in header' }
|
||||
}
|
||||
|
||||
const userData = await axios
|
||||
.create({
|
||||
baseURL: 'https://api.wanikani.com/v2/',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
validateStatus: function () {
|
||||
return true
|
||||
},
|
||||
})
|
||||
.get('/user')
|
||||
.then(
|
||||
(r) =>
|
||||
r.data as {
|
||||
data: {
|
||||
id: string
|
||||
username: string
|
||||
level: number
|
||||
subscription: {
|
||||
active: boolean
|
||||
type: string
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const userId = userData.data.id
|
||||
|
||||
await db.query(sql`
|
||||
INSERT INTO "user" ("id", "level")
|
||||
VALUES (${userId}, ${userData.data.level})
|
||||
ON CONFLICT ("id") DO UPDATE SET
|
||||
"level" = EXCLUDED."level"
|
||||
`)
|
||||
|
||||
req.session['userId'] = userId
|
||||
})
|
||||
}
|
||||
|
||||
export default apiRouter
|
||||
|
||||
async function main() {
|
||||
const app = fastify({
|
||||
logger: {
|
||||
prettyPrint: isDev,
|
||||
serializers: {
|
||||
req(req) {
|
||||
if (!req.url) {
|
||||
return { method: req.method }
|
||||
}
|
||||
|
||||
const [url = '', q] = req.url.split(/\?(.+)$/)
|
||||
const query = q ? qs.parse(q) : undefined
|
||||
|
||||
return { method: req.method, url, query }
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
app.register(apiRouter, {
|
||||
prefix: '/api',
|
||||
})
|
||||
|
||||
return app.listen(process.env['PORT']!, '0.0.0.0')
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main()
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import path from 'path'
|
||||
import qs from 'querystring'
|
||||
|
||||
import fastify from 'fastify'
|
||||
import fastifyStatic from 'fastify-static'
|
||||
|
||||
import apiRouter from './api'
|
||||
import { gCloudLogger } from './logger'
|
||||
import { isDev } from './shared'
|
||||
|
||||
async function main() {
|
||||
const port = parseInt(process.env['PORT']!) || 35594
|
||||
process.env['PORT'] = port.toString()
|
||||
|
||||
const app = fastify({
|
||||
logger: gCloudLogger({
|
||||
prettyPrint: isDev,
|
||||
serializers: {
|
||||
req(req) {
|
||||
if (!req.url) {
|
||||
return { method: req.method }
|
||||
}
|
||||
|
||||
const [url, q] = req.url.split(/\?(.+)$/)
|
||||
const query = q ? qs.parse(q) : undefined
|
||||
|
||||
return { method: req.method, url, query }
|
||||
},
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
app.register(apiRouter, {
|
||||
prefix: '/api',
|
||||
})
|
||||
|
||||
app.register(fastifyStatic, {
|
||||
root: path.join(__dirname, '../public'),
|
||||
redirect: true,
|
||||
})
|
||||
|
||||
app.setNotFoundHandler((_, reply) => {
|
||||
reply.redirect(200, '/')
|
||||
})
|
||||
|
||||
await app.listen(port, '0.0.0.0')
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main()
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import pino from 'pino'
|
||||
|
||||
const SeverityLookup = {
|
||||
default: 'DEFAULT',
|
||||
silly: 'DEFAULT',
|
||||
verbose: 'DEBUG',
|
||||
debug: 'DEBUG',
|
||||
http: 'notice',
|
||||
info: 'INFO',
|
||||
warn: 'WARNING',
|
||||
error: 'ERROR',
|
||||
}
|
||||
|
||||
const defaultPinoConfig: pino.LoggerOptions = {
|
||||
messageKey: 'message',
|
||||
formatters: {
|
||||
level(label, number) {
|
||||
return {
|
||||
severity: SeverityLookup[label as keyof typeof SeverityLookup],
|
||||
level: number,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const gCloudLogger = (pinoConfigOverrides: pino.LoggerOptions = {}) =>
|
||||
pino({
|
||||
...defaultPinoConfig,
|
||||
...pinoConfigOverrides,
|
||||
})
|
||||
@@ -0,0 +1,23 @@
|
||||
import createConnectionPool, { ConnectionPool } from '@databases/pg'
|
||||
|
||||
export const isDev = process.env['NODE_ENV'] === 'development'
|
||||
|
||||
export let db: ConnectionPool
|
||||
|
||||
// @ts-ignore
|
||||
if (!db) {
|
||||
db = createConnectionPool({
|
||||
...(process.env['DATABASE_URL']
|
||||
? {
|
||||
connectionString: process.env['DATABASE_URL'],
|
||||
}
|
||||
: {
|
||||
user: process.env['POSTGRES_USER']!,
|
||||
password: process.env['POSTGRES_PASSWORD']!,
|
||||
database: process.env['POSTGRES_DB']!,
|
||||
host: process.env['POSTGRES_HOST']!,
|
||||
port: parseInt(process.env['POSTGRES_PORT']!),
|
||||
}),
|
||||
bigIntMode: 'number',
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
import { SQLQuery, sql } from '@databases/pg'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export class QSplit {
|
||||
constructor(
|
||||
private opts: {
|
||||
default: (v: string) => SQLQuery | null
|
||||
fields: {
|
||||
[name: string]: {
|
||||
[op: string]: (v: string) => SQLQuery
|
||||
}
|
||||
}
|
||||
}
|
||||
) {}
|
||||
|
||||
parse(q: string) {
|
||||
const $and: SQLQuery[] = []
|
||||
const $or: SQLQuery[] = []
|
||||
const $not: SQLQuery[] = []
|
||||
|
||||
const ops = new Set(
|
||||
Object.values(this.opts.fields).flatMap((def) => Object.keys(def))
|
||||
)
|
||||
for (let kv of this.doSplit(q, ' ')) {
|
||||
let $current = $and
|
||||
if (kv[0] === '-') {
|
||||
$current = $not
|
||||
kv = kv.substr(1)
|
||||
} else if (kv[0] === '?') {
|
||||
$current = $or
|
||||
kv = kv.substr(1)
|
||||
}
|
||||
|
||||
for (const op of ops) {
|
||||
const segs = this.doSplit(kv, op)
|
||||
|
||||
if (segs.length === 1) {
|
||||
const cond = this.opts.default(segs[0]!)
|
||||
if (cond) {
|
||||
$current.push(cond)
|
||||
}
|
||||
} else if (segs.length === 2) {
|
||||
const fnMap = this.opts.fields[segs[0]!]
|
||||
if (!fnMap) continue
|
||||
|
||||
const fn = fnMap[op]
|
||||
if (!fn) continue
|
||||
|
||||
$current.push(fn(segs[1]!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cond: SQLQuery | null = null
|
||||
|
||||
if ($not.length) {
|
||||
$and.push(sql`NOT ((${sql.join($not, ') AND (')}))`)
|
||||
}
|
||||
|
||||
if ($and.length) {
|
||||
$or.push(sql`(${sql.join($and, ') AND (')})`)
|
||||
}
|
||||
|
||||
if ($or.length) {
|
||||
cond = sql`((${sql.join($or, ') OR (')}))`
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
/**
|
||||
* ```js
|
||||
* > this.doSplit('')
|
||||
* []
|
||||
* > this.doSplit('a:b "c:d e:f"')
|
||||
* ['a:b', 'c:d e:f']
|
||||
* > this.doSplit('a "b c" "d e"')
|
||||
* ['a', 'b c', 'd e']
|
||||
* ```
|
||||
*/
|
||||
private doSplit(ss: string, splitter: string) {
|
||||
const brackets = [
|
||||
['"', '"'],
|
||||
["'", "'"],
|
||||
] as const
|
||||
const keepBraces = false
|
||||
|
||||
const bracketStack = {
|
||||
data: [] as string[],
|
||||
push(c: string) {
|
||||
this.data.push(c)
|
||||
},
|
||||
pop() {
|
||||
return this.data.pop()
|
||||
},
|
||||
peek() {
|
||||
return this.data.length > 0
|
||||
? this.data[this.data.length - 1]
|
||||
: undefined
|
||||
},
|
||||
}
|
||||
const tokenStack = {
|
||||
data: [] as string[],
|
||||
currentChars: [] as string[],
|
||||
addChar(c: string) {
|
||||
this.currentChars.push(c)
|
||||
},
|
||||
flush() {
|
||||
const d = this.currentChars.join('')
|
||||
if (d) {
|
||||
this.data.push(d)
|
||||
}
|
||||
this.currentChars = []
|
||||
},
|
||||
}
|
||||
|
||||
let prev = ''
|
||||
ss.split('').map((c) => {
|
||||
if (prev === '\\') {
|
||||
tokenStack.addChar(c)
|
||||
} else {
|
||||
let canAddChar = true
|
||||
|
||||
for (const [op, cl] of brackets) {
|
||||
if (c === cl) {
|
||||
if (bracketStack.peek() === op) {
|
||||
bracketStack.pop()
|
||||
canAddChar = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (c === op) {
|
||||
bracketStack.push(c)
|
||||
canAddChar = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (c === splitter && !bracketStack.peek()) {
|
||||
tokenStack.flush()
|
||||
} else {
|
||||
if (keepBraces || canAddChar) {
|
||||
tokenStack.addChar(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prev = c
|
||||
})
|
||||
|
||||
tokenStack.flush()
|
||||
|
||||
return tokenStack.data.map((s) => s.trim()).filter((s) => s)
|
||||
}
|
||||
}
|
||||
|
||||
export const qParseNum: (
|
||||
k: SQLQuery
|
||||
) => Record<string, (v: string) => SQLQuery> = (k) => ({
|
||||
':': (v) => {
|
||||
switch (v.toLocaleLowerCase()) {
|
||||
case 'null':
|
||||
return sql`${k} IS NULL`
|
||||
case 'any':
|
||||
return sql`${k} IS NOT NULL`
|
||||
}
|
||||
|
||||
const m = /^([[\(])(\d+),(\d+)([\])])$/.exec(v)
|
||||
if (m) {
|
||||
let gt = sql`>`
|
||||
let lt = sql`<`
|
||||
|
||||
if (m[1] === '[') gt = sql`>=`
|
||||
if (m[4] === ']') gt = sql`<=`
|
||||
|
||||
return sql`${k} ${gt} ${parseInt(m[2])} OR ${k} ${lt} ${parseInt(m[3])}`
|
||||
}
|
||||
|
||||
return sql`${k} = ${parseInt(v)}`
|
||||
},
|
||||
'>': (v) => {
|
||||
return v[0] === '='
|
||||
? sql`${k} >= ${parseInt(v.substr(1))}`
|
||||
: sql`${k} > ${parseInt(v)}`
|
||||
},
|
||||
'<': (v) => {
|
||||
return v[0] === '='
|
||||
? sql`${k} <= ${parseInt(v.substr(1))}`
|
||||
: sql`${k} < ${parseInt(v)}`
|
||||
},
|
||||
})
|
||||
|
||||
const reDur = /^([+-]?\d+(?:\.\d+)?)([A-Z]+)$/i
|
||||
const toDate = (s: string) => {
|
||||
const m = reDur.exec(s)
|
||||
let d = dayjs(s)
|
||||
if (m) {
|
||||
d = dayjs().add(parseFloat(m[1]!), m[2] as any)
|
||||
if (d.isValid()) {
|
||||
return d.toDate()
|
||||
}
|
||||
}
|
||||
|
||||
if (d.isValid()) {
|
||||
return d.toDate()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
const toBetween = (s: string) => {
|
||||
const m = reDur.exec(s)
|
||||
if (m && toDate(s) instanceof Date) {
|
||||
return [
|
||||
dayjs()
|
||||
.add(parseFloat(m[1]!) - 0.5, m[2] as any)
|
||||
.toDate(),
|
||||
dayjs()
|
||||
.add(parseFloat(m[1]!) + 0.5, m[2] as any)
|
||||
.toDate(),
|
||||
]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
export const qParseDate: (
|
||||
k: SQLQuery
|
||||
) => Record<string, (v: string) => SQLQuery> = (k) => ({
|
||||
':': (v) => {
|
||||
let b: Date[] = []
|
||||
|
||||
switch (v.toLocaleLowerCase()) {
|
||||
case 'null':
|
||||
return sql`${k} IS NULL`
|
||||
case 'any':
|
||||
return sql`${k} IS NOT NULL`
|
||||
case 'now':
|
||||
b = toBetween('-0.5d')
|
||||
}
|
||||
|
||||
const reBetween =
|
||||
/^([[\(])([+-]?\d+(?:\.\d+)?[A-Z]+),([+-]?\d+(?:\.\d+)?[A-Z]+)([\])])$/i
|
||||
const m = reBetween.exec(v)
|
||||
if (m) {
|
||||
let gt = sql`>`
|
||||
let lt = sql`<`
|
||||
|
||||
if (m[1] === '[') gt = sql`>=`
|
||||
if (m[4] === ']') gt = sql`<=`
|
||||
|
||||
return sql`${k} ${gt} ${toDate(m[2]!)} OR ${k} ${lt} ${toDate(m[3]!)}`
|
||||
}
|
||||
|
||||
if (reDur.test(v)) {
|
||||
b = toBetween(v)
|
||||
}
|
||||
|
||||
if (b.length === 2) {
|
||||
return sql`${k} > ${b[0]} AND ${k} > ${b[1]}`
|
||||
}
|
||||
|
||||
return sql`FALSE`
|
||||
},
|
||||
'>': (v) => sql`${k} > ${toDate(v)}`,
|
||||
'<': (v) => sql`${k} < ${toDate(v)}`,
|
||||
})
|
||||
|
||||
export const makeQuiz = new QSplit({
|
||||
default: () => null,
|
||||
fields: {
|
||||
srsLevel: qParseNum(sql`quiz."srsLevel"`),
|
||||
nextReview: qParseDate(sql`quiz."nextReview"`),
|
||||
lastRight: qParseDate(sql`quiz."lastRight"`),
|
||||
lastWrong: qParseDate(sql`quiz."lastWrong"`),
|
||||
maxRight: qParseNum(sql`quiz."maxRight"`),
|
||||
maxWrong: qParseNum(sql`quiz."maxWrong"`),
|
||||
rightStreak: qParseNum(sql`quiz."rightStreak"`),
|
||||
wrongStreak: qParseNum(sql`quiz."wrongStreak"`),
|
||||
},
|
||||
})
|
||||
|
||||
export const makeTag = new QSplit({
|
||||
default: () => null,
|
||||
fields: {
|
||||
tag: { ':': (v) => sql`${v} = ANY("entry"."tag")` },
|
||||
type: {
|
||||
':': (v) => sql`"entry"."type" = ${v.replace(/hanzi/gi, 'character')}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const makeLevel = new QSplit({
|
||||
default: () => null,
|
||||
fields: {
|
||||
level: qParseNum(sql`"entry"."level"`),
|
||||
hLevel: qParseNum(sql`"entry"."hLevel"`),
|
||||
},
|
||||
})
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "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": "es2019", /* 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. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs", /* 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": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "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. */
|
||||
// "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. */
|
||||
|
||||
/* 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, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
"alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
"noUnusedLocals": true, /* Enable error reporting when a 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, /* Include 'undefined' in index signature results */
|
||||
"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. */
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user