@rife/logger 0.0.5-beta.9 → 0.0.6-beta.3

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.
@@ -0,0 +1,172 @@
1
+ var __webpack_modules__ = {
2
+ "../../node_modules/.pnpm/real-require@0.2.0/node_modules/real-require/src/index.js": function(module1) {
3
+ const realImport = new Function('modulePath', 'return import(modulePath)');
4
+ function realRequire(modulePath) {
5
+ if ('function' == typeof __non_webpack__require__) return __non_webpack__require__(modulePath);
6
+ return require(modulePath);
7
+ }
8
+ module1.exports = {
9
+ realImport,
10
+ realRequire
11
+ };
12
+ },
13
+ "../../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/lib/indexes.js": function(module1) {
14
+ "use strict";
15
+ const WRITE_INDEX = 4;
16
+ const READ_INDEX = 8;
17
+ module1.exports = {
18
+ WRITE_INDEX,
19
+ READ_INDEX
20
+ };
21
+ },
22
+ "../../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/lib/wait.js": function(module1) {
23
+ "use strict";
24
+ const MAX_TIMEOUT = 1000;
25
+ function wait(state, index, expected, timeout, done) {
26
+ const max = Date.now() + timeout;
27
+ let current = Atomics.load(state, index);
28
+ if (current === expected) return void done(null, 'ok');
29
+ let prior = current;
30
+ const check = (backoff)=>{
31
+ if (Date.now() > max) done(null, 'timed-out');
32
+ else setTimeout(()=>{
33
+ prior = current;
34
+ current = Atomics.load(state, index);
35
+ if (current === prior) check(backoff >= MAX_TIMEOUT ? MAX_TIMEOUT : 2 * backoff);
36
+ else current === expected ? done(null, 'ok') : done(null, 'not-equal');
37
+ }, backoff);
38
+ };
39
+ check(1);
40
+ }
41
+ function waitDiff(state, index, expected, timeout, done) {
42
+ const max = Date.now() + timeout;
43
+ let current = Atomics.load(state, index);
44
+ if (current !== expected) return void done(null, 'ok');
45
+ const check = (backoff)=>{
46
+ if (Date.now() > max) done(null, 'timed-out');
47
+ else setTimeout(()=>{
48
+ current = Atomics.load(state, index);
49
+ if (current !== expected) done(null, 'ok');
50
+ else check(backoff >= MAX_TIMEOUT ? MAX_TIMEOUT : 2 * backoff);
51
+ }, backoff);
52
+ };
53
+ check(1);
54
+ }
55
+ module1.exports = {
56
+ wait,
57
+ waitDiff
58
+ };
59
+ },
60
+ worker_threads: function(module1) {
61
+ "use strict";
62
+ module1.exports = require("worker_threads");
63
+ }
64
+ };
65
+ var __webpack_module_cache__ = {};
66
+ function __webpack_require__(moduleId) {
67
+ var cachedModule = __webpack_module_cache__[moduleId];
68
+ if (void 0 !== cachedModule) return cachedModule.exports;
69
+ var module1 = __webpack_module_cache__[moduleId] = {
70
+ exports: {}
71
+ };
72
+ __webpack_modules__[moduleId](module1, module1.exports, __webpack_require__);
73
+ return module1.exports;
74
+ }
75
+ var __webpack_exports__ = {};
76
+ (()=>{
77
+ "use strict";
78
+ const { realImport, realRequire } = __webpack_require__("../../node_modules/.pnpm/real-require@0.2.0/node_modules/real-require/src/index.js");
79
+ const { workerData, parentPort } = __webpack_require__("worker_threads");
80
+ const { WRITE_INDEX, READ_INDEX } = __webpack_require__("../../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/lib/indexes.js");
81
+ const { waitDiff } = __webpack_require__("../../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/lib/wait.js");
82
+ const { dataBuf, filename, stateBuf } = workerData;
83
+ let destination;
84
+ const state = new Int32Array(stateBuf);
85
+ const data = Buffer.from(dataBuf);
86
+ async function start() {
87
+ let worker;
88
+ try {
89
+ if (filename.endsWith('.ts') || filename.endsWith('.cts')) {
90
+ if (process[Symbol.for('ts-node.register.instance')]) {
91
+ if (process.env.TS_NODE_DEV) realRequire('ts-node-dev');
92
+ } else realRequire('ts-node/register');
93
+ worker = realRequire(decodeURIComponent(filename.replace('win32' === process.platform ? 'file:///' : 'file://', '')));
94
+ } else worker = await realImport(filename);
95
+ } catch (error) {
96
+ if (('ENOTDIR' === error.code || 'ERR_MODULE_NOT_FOUND' === error.code) && filename.startsWith('file://')) worker = realRequire(decodeURIComponent(filename.replace('file://', '')));
97
+ else if (void 0 === error.code || 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING' === error.code) try {
98
+ worker = realRequire(decodeURIComponent(filename.replace('win32' === process.platform ? 'file:///' : 'file://', '')));
99
+ } catch {
100
+ throw error;
101
+ }
102
+ else throw error;
103
+ }
104
+ if ('object' == typeof worker) worker = worker.default;
105
+ if ('object' == typeof worker) worker = worker.default;
106
+ destination = await worker(workerData.workerData);
107
+ destination.on('error', function(err) {
108
+ Atomics.store(state, WRITE_INDEX, -2);
109
+ Atomics.notify(state, WRITE_INDEX);
110
+ Atomics.store(state, READ_INDEX, -2);
111
+ Atomics.notify(state, READ_INDEX);
112
+ parentPort.postMessage({
113
+ code: 'ERROR',
114
+ err
115
+ });
116
+ });
117
+ destination.on('close', function() {
118
+ const end = Atomics.load(state, WRITE_INDEX);
119
+ Atomics.store(state, READ_INDEX, end);
120
+ Atomics.notify(state, READ_INDEX);
121
+ setImmediate(()=>{
122
+ process.exit(0);
123
+ });
124
+ });
125
+ }
126
+ start().then(function() {
127
+ parentPort.postMessage({
128
+ code: 'READY'
129
+ });
130
+ process.nextTick(run);
131
+ });
132
+ function run() {
133
+ const current = Atomics.load(state, READ_INDEX);
134
+ const end = Atomics.load(state, WRITE_INDEX);
135
+ if (end === current) return void (end === data.length ? waitDiff(state, READ_INDEX, end, 1 / 0, run) : waitDiff(state, WRITE_INDEX, end, 1 / 0, run));
136
+ if (-1 === end) return void destination.end();
137
+ const toWrite = data.toString('utf8', current, end);
138
+ const res = destination.write(toWrite);
139
+ if (res) {
140
+ Atomics.store(state, READ_INDEX, end);
141
+ Atomics.notify(state, READ_INDEX);
142
+ setImmediate(run);
143
+ } else destination.once('drain', function() {
144
+ Atomics.store(state, READ_INDEX, end);
145
+ Atomics.notify(state, READ_INDEX);
146
+ run();
147
+ });
148
+ }
149
+ process.on('unhandledRejection', function(err) {
150
+ parentPort.postMessage({
151
+ code: 'ERROR',
152
+ err
153
+ });
154
+ process.exit(1);
155
+ });
156
+ process.on('uncaughtException', function(err) {
157
+ parentPort.postMessage({
158
+ code: 'ERROR',
159
+ err
160
+ });
161
+ process.exit(1);
162
+ });
163
+ process.once('exit', (exitCode)=>{
164
+ if (0 !== exitCode) return void process.exit(exitCode);
165
+ if (destination?.writableNeedDrain && !destination?.writableEnded) parentPort.postMessage({
166
+ code: 'WARNING',
167
+ err: new Error('ThreadStream: process exited before destination stream was drained. this may indicate that the destination stream try to write to a another missing stream')
168
+ });
169
+ process.exit(0);
170
+ });
171
+ })();
172
+ module.exports = __webpack_exports__;
package/package.json CHANGED
@@ -1,25 +1,51 @@
1
1
  {
2
2
  "name": "@rife/logger",
3
- "version": "0.0.5-beta.9",
3
+ "version": "0.0.6-beta.3",
4
4
  "description": "",
5
- "main": "./dist/index.js",
5
+ "type": "module",
6
6
  "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./src/index.ts",
10
+ "require": "./dist/cjs/index.cjs",
11
+ "import": "./dist/esm/index.mjs"
12
+ },
13
+ "./pino": {
14
+ "types": "./src/pino/index.ts",
15
+ "require": "./dist/cjs/pino/index.cjs",
16
+ "import": "./dist/esm/pino/index.mjs"
17
+ }
18
+ },
19
+ "typesVersions": {
20
+ "*": {
21
+ "pino": [
22
+ "./src/pino"
23
+ ]
24
+ }
25
+ },
7
26
  "files": [
8
27
  "dist/**/*",
9
28
  "src/**/*",
10
29
  "tsconfig.json"
11
30
  ],
12
31
  "dependencies": {
13
- "chalk": "4",
14
- "nanoid": "^3.3.6",
15
- "safe-stable-stringify": "^2.4.3",
16
- "winston": "^3.9.0",
17
- "winston-daily-rotate-file": "^4.7.1",
18
- "@rife/config": "0.0.5-beta.9"
32
+ "pino": "^9.7.0",
33
+ "pino-http": "^10.5.0",
34
+ "pino-pretty": "^13.0.0",
35
+ "pino-std-serializers": "^7.0.0",
36
+ "rotating-file-stream": "^3.2.6"
37
+ },
38
+ "devDependencies": {
39
+ "thread-stream": "*",
40
+ "@rife/cli": "0.0.6-beta.9",
41
+ "@rife/plugin-rstack": "0.0.1-beta.1"
19
42
  },
20
43
  "scripts": {
21
- "dev": "infra dev",
22
- "build": "infra build",
23
- "prepublish": "infra build"
44
+ "dev": "rife dev",
45
+ "build": "rife build",
46
+ "lint": "eslint . --fix",
47
+ "release": "rife release",
48
+ "dev2": "rife dev --name app2",
49
+ "build2": "rife build --name app2"
24
50
  }
25
51
  }
package/src/common.ts ADDED
@@ -0,0 +1,4 @@
1
+ import path from 'path';
2
+
3
+ export const isEsm = path.extname(__filename) === '.mjs';
4
+ export const isCjs = !isEsm;
package/src/env.ts ADDED
@@ -0,0 +1,3 @@
1
+ export const isProd = () => process.env.NODE_ENV === 'production';
2
+
3
+ export const isDev = () => !isProd();
package/src/index.ts CHANGED
@@ -1,194 +1 @@
1
- import path from 'path';
2
- import { AsyncLocalStorage } from 'async_hooks';
3
-
4
- import chalk from 'chalk';
5
- import { customAlphabet } from 'nanoid';
6
- import winston from 'winston';
7
- import JSONStringify from 'safe-stable-stringify';
8
- import 'winston-daily-rotate-file';
9
-
10
- export type { winston };
11
-
12
- const als = new AsyncLocalStorage<{ traceId: string } & any>();
13
-
14
- const isDev = () => process.env.NODE_ENV === 'development';
15
- const isProd = () => !isDev();
16
-
17
- // https://github.com/CyberAP/nanoid-dictionary/blob/master/src/nolookalikes.js
18
- export const getNewTraceId = customAlphabet('346789ABCDEFGHJKLMNPQRTUVWXYabcdefghijkmnpqrtwxyz', 5);
19
-
20
- export function runInTrace<T>(fn: (...args: any[]) => T): T {
21
- return als.run({ traceId: getNewTraceId() }, fn);
22
- }
23
-
24
- export function getTraceId() {
25
- return als.getStore()?.traceId;
26
- }
27
-
28
- const levelColor = {
29
- info: chalk.green,
30
- error: chalk.redBright,
31
- warn: chalk.yellow,
32
- };
33
-
34
- const levelSymbolMap = {
35
- info: 'INF',
36
- warn: 'WRN',
37
- error: 'ERR',
38
- };
39
-
40
- // 设置traceId
41
- // https://www.npmjs.com/package/cls-rtracer
42
- const tracerFormat = (options: { writeToFile: boolean; format: { time: boolean } }) =>
43
- winston.format.printf(info => {
44
- let traceId = getTraceId();
45
- const color = (message: string, chalk: chalk.Chalk) => (options.writeToFile ? message : chalk(message));
46
- const levelit = (text: string) => {
47
- switch (info.level) {
48
- case 'info': {
49
- return color(text, levelColor.info);
50
- }
51
- case 'warn': {
52
- return color(text, levelColor.warn);
53
- }
54
- case 'error': {
55
- return color(text, levelColor.error);
56
- }
57
- }
58
- return text;
59
- };
60
- const levelSymbol = levelSymbolMap[info.level as keyof typeof levelSymbolMap];
61
- const level = levelSymbol ? levelit(` [${levelSymbol}]`) : '';
62
- const service = info.metadata.service ? levelit(` [${info.metadata.service}]`) : '';
63
- const timestamp = options.format.time ? color(info.timestamp, chalk.gray) : '';
64
- const trace = traceId ? color(` [${traceId}]`, chalk.gray) : '';
65
- let message = info.message || '';
66
- if (typeof info?.message !== 'string') {
67
- try {
68
- if (isDev()) {
69
- message = '\n' + JSONStringify(info?.message, undefined, 4);
70
- } else {
71
- message = JSONStringify(info?.message, undefined, 0);
72
- }
73
- } catch (e) {
74
- message = 'fail to stringify json';
75
- }
76
- }
77
- message = ' ' + message;
78
- const stack = info.metadata.stack ? color('\n' + info.metadata.stack + '\n', chalk.gray) : '';
79
- return `${timestamp}${trace}${level}${service}${message}${stack}`;
80
- });
81
-
82
- export namespace Logger {
83
- export interface Options {
84
- /**
85
- * 输出日志到console
86
- * 默认:development环境下输出,production环境下不输出
87
- */
88
- writeToConsole?: boolean;
89
-
90
- /**
91
- * 输出日志到文件
92
- * 默认:development环境下输出,production环境下不输出
93
- */
94
- writeToFile?: boolean;
95
-
96
- /**
97
- * 输出日志的文件目录
98
- * 默认:./logs/
99
- */
100
- dirname?: string;
101
-
102
- /**
103
- * 输出日志的文件名
104
- * 默认:server
105
- */
106
- filename?: string;
107
-
108
- /**
109
- * 日志输出格式
110
- */
111
- format?: {
112
- /**
113
- * 显示时间
114
- * 默认:true
115
- */
116
- time?: boolean;
117
-
118
- /**
119
- * 显示traceId
120
- * 默认:true
121
- */
122
- traceId?: boolean;
123
- };
124
- }
125
- }
126
-
127
- export interface Logger {
128
- info: winston.LeveledLogMethod;
129
- warn: winston.LeveledLogMethod;
130
- error: winston.LeveledLogMethod;
131
- setMetaData: (data: any) => void;
132
- }
133
-
134
- export function createLogger(options?: Logger.Options) {
135
- let transports: winston.transport[] = [];
136
- let config = {
137
- writeToConsole: options?.writeToConsole ?? isDev(),
138
- writeToFile: options?.writeToFile ?? isProd(),
139
- dirname: options?.dirname || path.join(process.cwd(), './logs/'),
140
- filename: options?.filename || 'server',
141
- format: {
142
- time: options?.format?.time ?? true,
143
- },
144
- };
145
- if (config.writeToFile) {
146
- transports.push(
147
- new winston.transports.DailyRotateFile({
148
- // https://www.npmjs.com/package/winston-daily-rotate-file
149
- filename: config.filename,
150
- dirname: config.dirname,
151
- datePattern: 'YYYY-MM-DD',
152
- maxFiles: '30d',
153
- extension: '.log',
154
- }),
155
- );
156
- }
157
-
158
- if (config.writeToConsole) {
159
- transports.push(new winston.transports.Console());
160
- }
161
-
162
- const { combine, timestamp, colorize, errors, metadata, splat } = winston.format;
163
- const timeFormat = isProd() ? 'YYYY-MM-DD HH:mm:ss.SSS' : 'HH:mm:ss.SSS';
164
- let format: winston.Logform.Format[] = [
165
- errors({ stack: true }),
166
- metadata(),
167
- timestamp({ format: timeFormat }),
168
- splat(), // https://github.com/winstonjs/logform/blob/master/splat.js
169
- tracerFormat(config),
170
- ];
171
- if (!config.writeToFile) {
172
- format.push(colorize());
173
- }
174
- const instance = winston.createLogger({
175
- format: combine(...format),
176
- transports,
177
- });
178
-
179
- const setMetaData = (data: any) => {
180
- instance.defaultMeta = {
181
- ...instance.defaultMeta,
182
- ...data,
183
- };
184
- };
185
-
186
- const logger: Logger = {
187
- info: instance.info.bind(instance),
188
- warn: instance.warn.bind(instance),
189
- error: instance.error.bind(instance),
190
- setMetaData,
191
- };
192
-
193
- return { logger };
194
- }
1
+ export * from './trace';
@@ -0,0 +1,91 @@
1
+ import path from 'path';
2
+
3
+ import pino from 'pino';
4
+
5
+ import { isEsm } from '../common';
6
+ import { genTraceId, getTraceId, runInTrace } from '../trace';
7
+
8
+ declare var PINO_RSPACK_PLUGIN: any;
9
+
10
+ // https://github.com/pinojs/pino/blob/master/docs/api.md
11
+ function getPinoConfig() {
12
+ const targets: any = [];
13
+
14
+ if (typeof PINO_RSPACK_PLUGIN !== 'undefined') {
15
+ // 打包单文件的时候自动拷贝worker
16
+ (globalThis as any).__bundlerPathsOverrides = {
17
+ 'pino-pretty': path.join(__dirname, './worker/pino-pretty.cjs'),
18
+ 'pino-worker': path.join(__dirname, './worker/pino-worker.cjs'),
19
+ './rotate.cjs': path.join(__dirname, './worker/pino-rotate.cjs'),
20
+ 'pino/file': path.join(__dirname, './worker/pino-file.cjs'),
21
+ 'thread-stream-worker': path.join(__dirname, './worker/thread-stream.cjs'),
22
+ ...(globalThis as any).__bundlerPathsOverrides,
23
+ };
24
+ }
25
+
26
+ if (process.env.NODE_ENV === 'production') {
27
+ } else {
28
+ targets.push({
29
+ level: 'debug',
30
+ target: 'pino-pretty', // must be installed separately,
31
+ options: {},
32
+ });
33
+ }
34
+
35
+ targets.push({
36
+ level: 'debug',
37
+ target: `./rotate${isEsm ? '.mjs' : '.cjs'}`,
38
+ options: { path: './logs/' },
39
+ });
40
+
41
+ const config: pino.LoggerOptions = {
42
+ transport: { targets },
43
+ mixin(data: any) {
44
+ // {"level":30,"time":1726065716643,"pid":21968,"hostname":"Win-LH"}
45
+ // 基础字段不能重复 删除掉
46
+ delete data.level;
47
+ delete data.time;
48
+ delete data.pid;
49
+ delete data.hostname;
50
+ if (!data?.traceId) {
51
+ return { traceId: getTraceId() };
52
+ }
53
+ return {};
54
+ },
55
+ };
56
+
57
+ return config;
58
+ }
59
+
60
+ export function createLogger() {
61
+ const logger = pino({
62
+ ...getPinoConfig(),
63
+ });
64
+
65
+ return { logger };
66
+ }
67
+
68
+ export function express(logger: any) {
69
+ const { mapHttpRequest, mapHttpResponse } = require('pino-std-serializers') as typeof import('pino-std-serializers');
70
+
71
+ return (req: any, res: any, next: any) => {
72
+ const response: any = res;
73
+ response[startTime] = response[startTime] || Date.now();
74
+ const traceId = req.headers['trace-id'] || genTraceId();
75
+
76
+ const onComplete = () => {
77
+ res.removeListener('close', onComplete);
78
+ res.removeListener('finish', onComplete);
79
+ res.removeListener('error', onComplete);
80
+ const duration = Date.now() - response[startTime];
81
+
82
+ logger.info({ duration, request: mapHttpRequest(req).req, response: mapHttpResponse(res).res }, 'request');
83
+ };
84
+
85
+ res.on('close', onComplete);
86
+ res.on('finish', onComplete);
87
+ res.on('error', onComplete);
88
+
89
+ runInTrace({ traceId }, next);
90
+ };
91
+ }
@@ -0,0 +1,32 @@
1
+ import type { Options } from 'rotating-file-stream';
2
+
3
+ export interface PinoRotatingFileStreamOptions extends Options {
4
+ path: string;
5
+ }
6
+
7
+ const generator = (time: number | Date, index?: number) => {
8
+ if (!time) {
9
+ return 'server.log';
10
+ }
11
+ time = new Date(time);
12
+
13
+ const month = String(time.getMonth() + 1).padStart(2, '0');
14
+ const day = String(time.getDate()).padStart(2, '0');
15
+ if (index) {
16
+ const count = String(index).padStart(3, '0');
17
+ return `server.${time.getFullYear()}${month}${day}-${count}.log`;
18
+ }
19
+ return `server.${time.getFullYear()}${month}${day}.log`;
20
+ };
21
+
22
+ export default (options: PinoRotatingFileStreamOptions) => {
23
+ const { createStream } = require('rotating-file-stream') as typeof import('rotating-file-stream');
24
+
25
+ const { path } = options;
26
+ return createStream(generator, {
27
+ size: '50M',
28
+ maxSize: '500M',
29
+ interval: '1d',
30
+ path,
31
+ });
32
+ };
package/src/test.ts CHANGED
@@ -1,25 +1,12 @@
1
- process.env.NODE_ENV = 'development';
2
- // process.env.NODE_ENV = 'production';
3
-
4
- import { createLogger } from '.';
5
-
6
- const { logger } = createLogger({ writeToFile: false, writeToConsole: true, format: { time: false } });
7
-
8
- const deep = { a: { b: { c: { g: { e: { f: 1 } } } } } };
9
- logger.info('1 %s', { a: 1 });
10
- logger.info('2 %j', deep);
11
- logger.info('3 \n%j', deep);
12
- logger.info('4 %s %s', 1, 2);
13
- logger.info('5 %o', deep);
14
- logger.info(deep);
15
- logger.warn('execed');
16
- logger.error(new Error('not found'));
17
-
18
- // import { format } from 'logform';
19
-
20
- // const jsonFormat = format.json({ space: 2 });
21
- // const info = jsonFormat.transform({
22
- // level: 'info',
23
- // message: 'my message',
24
- // });
25
- // console.log(info);
1
+ process.env.NODE_ENV = 'development';
2
+ // process.env.NODE_ENV = 'production';
3
+
4
+ import { createLogger } from './pino';
5
+ import { genTraceId } from './trace';
6
+
7
+ const { logger } = createLogger();
8
+
9
+ logger.info({ a: 999 }, '123 2 ');
10
+
11
+ const start = performance.now();
12
+ logger.info({ duration: performance.now() - start, id: genTraceId() });
package/src/trace.ts CHANGED
@@ -0,0 +1,15 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+
3
+ const als = new AsyncLocalStorage<{ traceId: string } & any>();
4
+
5
+ export function runInTrace<T>(data: any, fn: (...args: any[]) => T): T {
6
+ return als.run(data, fn);
7
+ }
8
+
9
+ export function genTraceId() {
10
+ return crypto.randomUUID().replaceAll('-', '');
11
+ }
12
+
13
+ export function getTraceId() {
14
+ return als.getStore()?.traceId;
15
+ }
package/src/worker.ts ADDED
@@ -0,0 +1 @@
1
+ import 'thread-stream/lib/worker';
package/tsconfig.json CHANGED
@@ -1,12 +1,12 @@
1
- {
2
- "extends": "@rife/config/tsconfig.json", // 继承 packages 目录下通用配置
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "declaration": true,
6
- "baseUrl": ".",
7
- "paths": {
8
- "@/*": ["./src/*"]
9
- }
10
- },
11
- "include": ["./src/**/*"]
12
- }
1
+ {
2
+ "extends": "@rife/config/tsconfig.json", // 继承 packages 目录下通用配置
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "declaration": true,
6
+ "baseUrl": ".",
7
+ "paths": {
8
+ "@/*": ["./src/*"]
9
+ }
10
+ },
11
+ "include": ["./src/**/*"]
12
+ }