create-epic-graphql-server 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +7 -0
- package/.eslintrc.js +198 -0
- package/.husky/pre-commit +2 -0
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/bin/create-epic-graphql-server.js +81 -0
- package/db-seed.json5 +37 -0
- package/package.json +75 -0
- package/specs/Customer.spec.ts +41 -0
- package/specs/Order.spec.ts +80 -0
- package/specs/logger.spec.ts +92 -0
- package/specs/schema.spec.ts +103 -0
- package/src/config/db.ts +267 -0
- package/src/index.ts +57 -0
- package/src/models/Customer.ts +10 -0
- package/src/models/Order.ts +12 -0
- package/src/routes/check.ts +18 -0
- package/src/routes/index.ts +8 -0
- package/src/schema/schema.ts +227 -0
- package/src/typing/enums.ts +37 -0
- package/src/typing/interfaces.ts +44 -0
- package/src/typing/types.ts +5 -0
- package/src/utilities/logger.ts +191 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
const {
|
|
2
|
+
GraphQLObjectType,
|
|
3
|
+
GraphQLInputObjectType,
|
|
4
|
+
GraphQLID,
|
|
5
|
+
GraphQLString,
|
|
6
|
+
GraphQLSchema,
|
|
7
|
+
GraphQLList,
|
|
8
|
+
GraphQLNonNull,
|
|
9
|
+
GraphQLEnumType,
|
|
10
|
+
} = require('graphql')
|
|
11
|
+
const db = require('../config/db')
|
|
12
|
+
import type { Customer } from '../models/Customer'
|
|
13
|
+
import type { Order } from '../models/Order'
|
|
14
|
+
import { Product, Status } from '../typing/enums'
|
|
15
|
+
|
|
16
|
+
// https://graphql.org/graphql-js/constructing-types/
|
|
17
|
+
|
|
18
|
+
const customerType = new GraphQLObjectType({
|
|
19
|
+
name: 'Customer',
|
|
20
|
+
fields: {
|
|
21
|
+
id: { type: GraphQLID },
|
|
22
|
+
name: { type: GraphQLString },
|
|
23
|
+
email: { type: GraphQLString },
|
|
24
|
+
phone: { type: GraphQLString },
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const orderType = new GraphQLObjectType({
|
|
29
|
+
name: 'Order',
|
|
30
|
+
fields: () => ({
|
|
31
|
+
id: { type: GraphQLID },
|
|
32
|
+
name: { type: GraphQLString },
|
|
33
|
+
notes: { type: GraphQLString },
|
|
34
|
+
status: { type: GraphQLString },
|
|
35
|
+
// NOTE creates relationship with other type
|
|
36
|
+
customer: {
|
|
37
|
+
type: customerType,
|
|
38
|
+
resolve(parent: Order, args: Customer) {
|
|
39
|
+
return db.customers.findById(parent.customerId)
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const orderFilterInput = new GraphQLInputObjectType({
|
|
46
|
+
name: 'OrderFilterInput',
|
|
47
|
+
fields: {
|
|
48
|
+
name: { type: GraphQLString },
|
|
49
|
+
status: { type: GraphQLString },
|
|
50
|
+
customerId: { type: GraphQLID },
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const customerFilterInput = new GraphQLInputObjectType({
|
|
55
|
+
name: 'CustomerFilterInput',
|
|
56
|
+
fields: {
|
|
57
|
+
name: { type: GraphQLString },
|
|
58
|
+
email: { type: GraphQLString },
|
|
59
|
+
phone: { type: GraphQLString },
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const queryType = new GraphQLObjectType({
|
|
64
|
+
name: 'Query',
|
|
65
|
+
fields: {
|
|
66
|
+
orders: {
|
|
67
|
+
type: new GraphQLList(orderType),
|
|
68
|
+
args: {
|
|
69
|
+
filter: { type: orderFilterInput },
|
|
70
|
+
},
|
|
71
|
+
resolve(parent: unknown, { filter }: { filter: Order }) {
|
|
72
|
+
return db.orders.find({ filter })
|
|
73
|
+
},
|
|
74
|
+
// NOTE:
|
|
75
|
+
// GraphQL efficiently resolves only the fields
|
|
76
|
+
// requested by the client, but whether it fetches
|
|
77
|
+
// only those fields from the data source depends
|
|
78
|
+
// entirely on how resolvers are implemented...
|
|
79
|
+
// the example resolver implementation shown below
|
|
80
|
+
// may avoid over fetching from a database or API
|
|
81
|
+
//
|
|
82
|
+
// resolve(parent, { filter }, context, info) {
|
|
83
|
+
// const fields = info
|
|
84
|
+
// .fieldNodes[0]
|
|
85
|
+
// .selectionSet
|
|
86
|
+
// .selections
|
|
87
|
+
// .map(s => s.name.value)
|
|
88
|
+
//
|
|
89
|
+
// return db.orders.find(filter, fields)
|
|
90
|
+
// },
|
|
91
|
+
},
|
|
92
|
+
order: {
|
|
93
|
+
type: orderType,
|
|
94
|
+
args: {
|
|
95
|
+
id: { type: new GraphQLNonNull(GraphQLID) },
|
|
96
|
+
},
|
|
97
|
+
resolve: (parent: unknown, { id }: Order) => {
|
|
98
|
+
return db.orders.findById(id)
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
customers: {
|
|
102
|
+
type: new GraphQLList(customerType),
|
|
103
|
+
args: {
|
|
104
|
+
filter: { type: customerFilterInput },
|
|
105
|
+
},
|
|
106
|
+
resolve(parent: unknown, { filter }: { filter: Customer }) {
|
|
107
|
+
return db.customers.find({ filter })
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
customer: {
|
|
111
|
+
type: customerType,
|
|
112
|
+
args: {
|
|
113
|
+
id: { type: new GraphQLNonNull(GraphQLID) },
|
|
114
|
+
},
|
|
115
|
+
resolve: (parent: unknown, { id }: Customer) => {
|
|
116
|
+
return db.customers.findById(id)
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const mutation = new GraphQLObjectType({
|
|
123
|
+
name: 'Mutation',
|
|
124
|
+
fields: {
|
|
125
|
+
// Add a customer
|
|
126
|
+
addCustomer: {
|
|
127
|
+
type: customerType,
|
|
128
|
+
args: {
|
|
129
|
+
name: { type: new GraphQLNonNull(GraphQLString) },
|
|
130
|
+
email: { type: new GraphQLNonNull(GraphQLString) },
|
|
131
|
+
phone: { type: new GraphQLNonNull(GraphQLString) },
|
|
132
|
+
},
|
|
133
|
+
resolve(parent: unknown, args: Customer) {
|
|
134
|
+
return db.customers.save(args)
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
// Delete a customer
|
|
138
|
+
deleteCustomer: {
|
|
139
|
+
type: customerType,
|
|
140
|
+
args: {
|
|
141
|
+
id: { type: new GraphQLNonNull(GraphQLID) },
|
|
142
|
+
},
|
|
143
|
+
async resolve(parent: unknown, args: Customer) {
|
|
144
|
+
const orders = await db.orders.find({
|
|
145
|
+
filter: { customerId: args.id },
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
orders.forEach(({ id }: Customer) => {
|
|
149
|
+
db.orders.findByIdAndRemove(id)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
return db.customers.findByIdAndRemove(args.id)
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
// Add an order
|
|
156
|
+
addOrder: {
|
|
157
|
+
type: orderType,
|
|
158
|
+
args: {
|
|
159
|
+
name: {
|
|
160
|
+
type: new GraphQLNonNull(new GraphQLEnumType({
|
|
161
|
+
name: 'OrderName',
|
|
162
|
+
values: {
|
|
163
|
+
productOne: { value: Product.productOne },
|
|
164
|
+
productTwo: { value: Product.productTwo },
|
|
165
|
+
productThree: { value: Product.productThree },
|
|
166
|
+
},
|
|
167
|
+
})),
|
|
168
|
+
defaultValue: null,
|
|
169
|
+
},
|
|
170
|
+
notes: { type: GraphQLString },
|
|
171
|
+
status: {
|
|
172
|
+
type: new GraphQLNonNull(new GraphQLEnumType({
|
|
173
|
+
name: 'OrderStatus',
|
|
174
|
+
values: {
|
|
175
|
+
notStarted: { value: Status.notStarted },
|
|
176
|
+
processing: { value: Status.processing },
|
|
177
|
+
sent: { value: Status.sent },
|
|
178
|
+
},
|
|
179
|
+
})),
|
|
180
|
+
defaultValue: Status.notStarted,
|
|
181
|
+
},
|
|
182
|
+
customerId: { type: new GraphQLNonNull(GraphQLID) },
|
|
183
|
+
},
|
|
184
|
+
resolve(parent: unknown, args: Order) {
|
|
185
|
+
return db.orders.save(args)
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
// Update an order
|
|
189
|
+
updateOrder: {
|
|
190
|
+
type: orderType,
|
|
191
|
+
args: {
|
|
192
|
+
id: { type: new GraphQLNonNull(GraphQLID) },
|
|
193
|
+
name: { type: GraphQLString },
|
|
194
|
+
notes: { type: GraphQLString },
|
|
195
|
+
status: {
|
|
196
|
+
type: new GraphQLEnumType({
|
|
197
|
+
name: 'OrderStatusUpdate',
|
|
198
|
+
values: {
|
|
199
|
+
notStarted: { value: Status.notStarted },
|
|
200
|
+
processing: { value: Status.processing },
|
|
201
|
+
sent: { value: Status.sent },
|
|
202
|
+
},
|
|
203
|
+
}),
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
resolve(parent: unknown, { id, ...update }: Order) {
|
|
207
|
+
return db.orders.findByIdAndUpdate(id, { update })
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
// Delete an order
|
|
211
|
+
deleteOrder: {
|
|
212
|
+
type: orderType,
|
|
213
|
+
args: {
|
|
214
|
+
id: { type: new GraphQLNonNull(GraphQLID) },
|
|
215
|
+
},
|
|
216
|
+
resolve(parent: unknown, { id }: Order) {
|
|
217
|
+
return db.orders.findByIdAndRemove(id)
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
export const schema = new GraphQLSchema({
|
|
224
|
+
query: queryType,
|
|
225
|
+
description: 'my schema',
|
|
226
|
+
mutation,
|
|
227
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export enum NodeEnv {
|
|
2
|
+
production = 'production',
|
|
3
|
+
test = 'test',
|
|
4
|
+
development = 'development',
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export enum LogLevel {
|
|
8
|
+
error = 'error',
|
|
9
|
+
warn = 'warn',
|
|
10
|
+
info = 'info',
|
|
11
|
+
http = 'http',
|
|
12
|
+
verbose = 'verbose',
|
|
13
|
+
debug = 'debug',
|
|
14
|
+
silly = 'silly',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export enum LogFile {
|
|
18
|
+
combined = 'combined',
|
|
19
|
+
error = 'error',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export enum Product {
|
|
23
|
+
productOne = 'Product One',
|
|
24
|
+
productTwo = 'Product Two',
|
|
25
|
+
productThree = 'Product Three',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export enum Status {
|
|
29
|
+
notStarted = 'Not Started',
|
|
30
|
+
processing = 'Processing',
|
|
31
|
+
sent = 'Sent',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export enum DataSet {
|
|
35
|
+
customers = 'customers',
|
|
36
|
+
orders = 'orders',
|
|
37
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Customer } from '../models/Customer'
|
|
2
|
+
import type { Order } from '../models/Order'
|
|
3
|
+
import type * as enums from './enums'
|
|
4
|
+
import type * as types from './types'
|
|
5
|
+
|
|
6
|
+
interface DataSetOptions {
|
|
7
|
+
dataSet: enums.DataSet,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface UpdateOptions {
|
|
11
|
+
update: Partial<Customer>|Partial<Order>,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface IdOptions {
|
|
15
|
+
id: types.Id,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface DocOptions {
|
|
19
|
+
doc: Customer | Order,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface FilterOptions {
|
|
23
|
+
filter?: Record<string, string>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface Create extends DataSetOptions, DocOptions {}
|
|
27
|
+
|
|
28
|
+
export interface Read extends DataSetOptions, FilterOptions {}
|
|
29
|
+
|
|
30
|
+
export interface ReadOne extends Read, IdOptions {}
|
|
31
|
+
|
|
32
|
+
export interface Update extends DataSetOptions, IdOptions, UpdateOptions {}
|
|
33
|
+
|
|
34
|
+
export interface Delete extends DataSetOptions, IdOptions {}
|
|
35
|
+
|
|
36
|
+
export interface ErrorHandler {
|
|
37
|
+
header?: types.Msg,
|
|
38
|
+
err: types.Err,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface LogErr extends ErrorHandler {
|
|
42
|
+
meta?: object,
|
|
43
|
+
level?: enums.LogLevel.warn | enums.LogLevel.error,
|
|
44
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const chalk = require('chalk')
|
|
3
|
+
const winston = require('winston')
|
|
4
|
+
require('winston-daily-rotate-file')
|
|
5
|
+
const packageJson = require('../../package.json')
|
|
6
|
+
import type { Logger } from 'winston'
|
|
7
|
+
import type { Format } from 'logform'
|
|
8
|
+
import { NodeEnv, LogLevel, LogFile } from '../typing/enums'
|
|
9
|
+
import type { Msg } from '../typing/types'
|
|
10
|
+
import type { ErrorHandler, LogErr } from '../typing/interfaces'
|
|
11
|
+
|
|
12
|
+
require('dotenv-flow').config()
|
|
13
|
+
|
|
14
|
+
const NODE_ENV = process.env.NODE_ENV as NodeEnv || NodeEnv.development
|
|
15
|
+
|
|
16
|
+
// NOTE:
|
|
17
|
+
// 1) production
|
|
18
|
+
// a) write log file in json to enable log analytics
|
|
19
|
+
// b) INFO logging enabled
|
|
20
|
+
// 2) development
|
|
21
|
+
// a) display logs in console in more readable format, and
|
|
22
|
+
// write to log files to align prod and dev behavior
|
|
23
|
+
// b) VERBOSE logging enabled
|
|
24
|
+
|
|
25
|
+
class CustomLogger {
|
|
26
|
+
private dirPath: string
|
|
27
|
+
private locales: string
|
|
28
|
+
private timeZone: string
|
|
29
|
+
private settings: {
|
|
30
|
+
[key in NodeEnv]: {
|
|
31
|
+
level: keyof typeof winston.config.npm.levels,
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
log: Logger | null
|
|
35
|
+
|
|
36
|
+
constructor (options?: {
|
|
37
|
+
dirPath: string
|
|
38
|
+
locales: string
|
|
39
|
+
timeZone: string
|
|
40
|
+
}) {
|
|
41
|
+
this.dirPath = options?.dirPath || 'tmp'
|
|
42
|
+
this.locales = options?.locales || 'en-US'
|
|
43
|
+
this.timeZone = options?.timeZone || 'UTC' // 'America/New_York'
|
|
44
|
+
|
|
45
|
+
this.settings = {
|
|
46
|
+
[NodeEnv.production]: {
|
|
47
|
+
level: LogLevel.info,
|
|
48
|
+
},
|
|
49
|
+
[NodeEnv.test]: {
|
|
50
|
+
level: LogLevel.info,
|
|
51
|
+
},
|
|
52
|
+
[NodeEnv.development]: {
|
|
53
|
+
level: LogLevel.verbose,
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.log = null
|
|
58
|
+
|
|
59
|
+
this.initWinston()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private initWinston() {
|
|
63
|
+
const {
|
|
64
|
+
combine,
|
|
65
|
+
timestamp,
|
|
66
|
+
json,
|
|
67
|
+
simple,
|
|
68
|
+
colorize,
|
|
69
|
+
} = winston.format
|
|
70
|
+
|
|
71
|
+
const transportsOptions = {
|
|
72
|
+
frequency: '24h',
|
|
73
|
+
datePattern: 'YYYY-MM-DD-HH',
|
|
74
|
+
zippedArchive: true,
|
|
75
|
+
maxSize: '20m',
|
|
76
|
+
maxFiles: '30d',
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const formatFileName = (fileType: LogFile) => {
|
|
80
|
+
const file = `${packageJson.name}_${fileType}_%DATE%.log`
|
|
81
|
+
|
|
82
|
+
return path.join('/', this.dirPath, file)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const transports = [
|
|
86
|
+
new winston.transports.DailyRotateFile({
|
|
87
|
+
level: LogLevel.error,
|
|
88
|
+
filename: formatFileName(LogFile.error),
|
|
89
|
+
frequency: transportsOptions.frequency,
|
|
90
|
+
datePattern: transportsOptions.datePattern,
|
|
91
|
+
zippedArchive: transportsOptions.zippedArchive,
|
|
92
|
+
maxSize: transportsOptions.maxSize,
|
|
93
|
+
maxFiles: transportsOptions.maxFiles,
|
|
94
|
+
}),
|
|
95
|
+
new winston.transports.DailyRotateFile({
|
|
96
|
+
filename: formatFileName(LogFile.combined),
|
|
97
|
+
frequency: transportsOptions.frequency,
|
|
98
|
+
datePattern: transportsOptions.datePattern,
|
|
99
|
+
zippedArchive: transportsOptions.zippedArchive,
|
|
100
|
+
maxSize: transportsOptions.maxSize,
|
|
101
|
+
maxFiles: transportsOptions.maxFiles,
|
|
102
|
+
}),
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
let format: Format
|
|
106
|
+
|
|
107
|
+
if (NODE_ENV === NodeEnv.production) {
|
|
108
|
+
format = combine(timestamp(), json())
|
|
109
|
+
} else {
|
|
110
|
+
format = combine(simple(), colorize())
|
|
111
|
+
transports.push(new winston.transports.Console({ format }))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const level = this.settings[NODE_ENV].level
|
|
115
|
+
|
|
116
|
+
return this.log = winston.createLogger({
|
|
117
|
+
level,
|
|
118
|
+
format,
|
|
119
|
+
transports,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
logTimeStamp = (options?: {
|
|
124
|
+
msg?: string,
|
|
125
|
+
}) => {
|
|
126
|
+
const msg = options?.msg || ''
|
|
127
|
+
|
|
128
|
+
const now = new Date()
|
|
129
|
+
|
|
130
|
+
const time = now.toLocaleString(this.locales, {
|
|
131
|
+
timeZone: this.timeZone,
|
|
132
|
+
timeStyle: 'long',
|
|
133
|
+
dateStyle: 'short',
|
|
134
|
+
hour12: false,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const timeStamp = chalk.grey(`[${time}]`)
|
|
138
|
+
|
|
139
|
+
return this.log?.info(`${msg} ${timeStamp}`)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
logEnv = ({
|
|
143
|
+
msg,
|
|
144
|
+
}: {
|
|
145
|
+
msg: string,
|
|
146
|
+
}) => {
|
|
147
|
+
const env = chalk.grey(`[${NODE_ENV}]`)
|
|
148
|
+
|
|
149
|
+
return this.log?.info(`${msg} ${env}`)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private errorHandler = ({
|
|
153
|
+
header,
|
|
154
|
+
err,
|
|
155
|
+
}: ErrorHandler): Msg => {
|
|
156
|
+
const myHeader: string = header
|
|
157
|
+
? `${header}:`
|
|
158
|
+
: ''
|
|
159
|
+
|
|
160
|
+
if (err instanceof Error) {
|
|
161
|
+
return `${myHeader} ${err.message}`
|
|
162
|
+
} else {
|
|
163
|
+
return `unexpected ${myHeader} ${err}`
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
logErr = ({
|
|
168
|
+
header,
|
|
169
|
+
err,
|
|
170
|
+
meta = {},
|
|
171
|
+
level = LogLevel.error,
|
|
172
|
+
}: LogErr) => {
|
|
173
|
+
const msg = this.errorHandler({
|
|
174
|
+
err,
|
|
175
|
+
header,
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
switch(level) {
|
|
179
|
+
case LogLevel.warn:
|
|
180
|
+
return this.log?.warn(msg, { ...meta })
|
|
181
|
+
case LogLevel.error:
|
|
182
|
+
return this.log?.error(msg, { ...meta })
|
|
183
|
+
default:
|
|
184
|
+
return null
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const logger = new CustomLogger()
|
|
190
|
+
|
|
191
|
+
export = logger
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ts-node": {
|
|
3
|
+
"transpileOnly": true,
|
|
4
|
+
"files": true,
|
|
5
|
+
"compilerOptions": {}
|
|
6
|
+
},
|
|
7
|
+
"compilerOptions": {
|
|
8
|
+
"module": "commonjs",
|
|
9
|
+
"moduleResolution": "node",
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"baseUrl": "src",
|
|
12
|
+
"preserveConstEnums": true,
|
|
13
|
+
"target": "es6",
|
|
14
|
+
"pretty": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"strict": true,
|
|
17
|
+
"outDir": "./build",
|
|
18
|
+
"types": [
|
|
19
|
+
"@types/chai",
|
|
20
|
+
"@types/chalk",
|
|
21
|
+
"@types/dotenv-flow",
|
|
22
|
+
"@types/express",
|
|
23
|
+
"@types/mocha",
|
|
24
|
+
"@types/node",
|
|
25
|
+
"@types/sinon",
|
|
26
|
+
"@types/supertest"
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
"include": [
|
|
30
|
+
"src/**/*"
|
|
31
|
+
],
|
|
32
|
+
"exclude": [
|
|
33
|
+
"./.env*",
|
|
34
|
+
"node_modules",
|
|
35
|
+
"bin",
|
|
36
|
+
"build",
|
|
37
|
+
".vscode",
|
|
38
|
+
"*.json5",
|
|
39
|
+
"**/*.specs.ts",
|
|
40
|
+
"**/*.test.ts"
|
|
41
|
+
]
|
|
42
|
+
}
|