pyre-cli 1.3.0 → 2.0.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/README.md +278 -17
- package/dist/index.js +1763 -350
- package/package.json +6 -3
- package/parity.md +542 -0
- package/src/formatters/index.ts +17 -0
- package/src/formatters/output.ts +165 -0
- package/src/formatters/render.ts +659 -0
- package/src/formatters/themes.ts +115 -0
- package/src/formatters/types.ts +62 -0
- package/src/history.ts +153 -0
- package/src/index.ts +201 -0
- package/src/live/export.ts +76 -0
- package/src/live/index.ts +16 -0
- package/src/live/input.ts +443 -0
- package/src/live/render.ts +146 -0
- package/src/live/state.ts +103 -0
- package/src/live/types.ts +23 -0
- package/src/monitors/collectors.ts +687 -0
- package/src/monitors/index.ts +14 -0
- package/src/monitors/run.ts +18 -0
- package/src/monitors/smc.ts +107 -0
- package/src/monitors/types.ts +148 -0
- package/src/p2p/client.ts +197 -0
- package/src/p2p/index.ts +3 -0
- package/src/p2p/protocol.ts +71 -0
- package/src/p2p/server.ts +219 -0
- package/src/p2p/types.ts +71 -0
- package/src/pyre_img +0 -0
- package/src/sparkline.ts +34 -0
- package/src/splash.ts +102 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import net from 'node:net';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { collectAll } from '../monitors/index.js';
|
|
4
|
+
import { encodeAuthOk, encodeAuthFail, encodeData, encodePing, encodePong, encodeDisconnect } from './protocol.js';
|
|
5
|
+
import type { P2PServerOptions, P2PConnectionInfo } from './types.js';
|
|
6
|
+
|
|
7
|
+
interface Peer {
|
|
8
|
+
socket: net.Socket;
|
|
9
|
+
authenticated: boolean;
|
|
10
|
+
intervalHandle: NodeJS.Timeout | null;
|
|
11
|
+
pingHandle: NodeJS.Timeout | null;
|
|
12
|
+
readBuffer: Buffer;
|
|
13
|
+
readExpectingHeader: boolean;
|
|
14
|
+
readMessageLength: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const HEADER_SIZE = 4;
|
|
18
|
+
|
|
19
|
+
function readMessageFromPeer(peer: Peer): Buffer | null {
|
|
20
|
+
while (peer.readBuffer.length > 0) {
|
|
21
|
+
if (peer.readExpectingHeader) {
|
|
22
|
+
if (peer.readBuffer.length >= HEADER_SIZE) {
|
|
23
|
+
peer.readMessageLength = peer.readBuffer.readUInt32BE(0);
|
|
24
|
+
peer.readBuffer = peer.readBuffer.subarray(HEADER_SIZE);
|
|
25
|
+
peer.readExpectingHeader = false;
|
|
26
|
+
} else {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!peer.readExpectingHeader) {
|
|
32
|
+
if (peer.readBuffer.length >= peer.readMessageLength) {
|
|
33
|
+
const body = peer.readBuffer.subarray(0, peer.readMessageLength);
|
|
34
|
+
peer.readBuffer = peer.readBuffer.subarray(peer.readMessageLength);
|
|
35
|
+
peer.readExpectingHeader = true;
|
|
36
|
+
return body;
|
|
37
|
+
} else {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class P2PServer {
|
|
46
|
+
private server: net.Server | null = null;
|
|
47
|
+
private peers: Map<net.Socket, Peer> = new Map();
|
|
48
|
+
private options: P2PServerOptions;
|
|
49
|
+
private running = false;
|
|
50
|
+
|
|
51
|
+
constructor(options: P2PServerOptions) {
|
|
52
|
+
this.options = options;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get info(): P2PConnectionInfo {
|
|
56
|
+
return {
|
|
57
|
+
host: this.options.host,
|
|
58
|
+
port: this.options.port,
|
|
59
|
+
peersConnected: this.peers.size,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async start(): Promise<void> {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
this.server = net.createServer((socket) => this.onConnection(socket));
|
|
66
|
+
this.server.on('error', (err) => reject(err));
|
|
67
|
+
this.server.listen(this.options.port, this.options.host, () => {
|
|
68
|
+
this.running = true;
|
|
69
|
+
const addr = this.server?.address();
|
|
70
|
+
const bound = addr ? (typeof addr === 'object' ? `${addr.address}:${addr.port}` : String(addr)) : 'unknown';
|
|
71
|
+
console.log(chalk.green(`P2P server listening on ${bound}`));
|
|
72
|
+
console.log(chalk.dim(` Password: ${this.options.password}`));
|
|
73
|
+
console.log(chalk.dim(` Interval: ${this.options.intervalMs}ms`));
|
|
74
|
+
resolve();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
stop(): void {
|
|
80
|
+
this.running = false;
|
|
81
|
+
for (const peer of this.peers.values()) {
|
|
82
|
+
this.disconnectPeer(peer, 'Server shutting down');
|
|
83
|
+
}
|
|
84
|
+
this.peers.clear();
|
|
85
|
+
if (this.server) {
|
|
86
|
+
this.server.close();
|
|
87
|
+
this.server = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private onConnection(socket: net.Socket): void {
|
|
92
|
+
const peer: Peer = {
|
|
93
|
+
socket,
|
|
94
|
+
authenticated: false,
|
|
95
|
+
intervalHandle: null,
|
|
96
|
+
pingHandle: null,
|
|
97
|
+
readBuffer: Buffer.alloc(0),
|
|
98
|
+
readExpectingHeader: true,
|
|
99
|
+
readMessageLength: 0,
|
|
100
|
+
};
|
|
101
|
+
this.peers.set(socket, peer);
|
|
102
|
+
|
|
103
|
+
const remoteAddr = socket.remoteAddress
|
|
104
|
+
? `${socket.remoteAddress}:${socket.remotePort}`
|
|
105
|
+
: 'unknown';
|
|
106
|
+
console.log(chalk.cyan(`Peer connected: ${remoteAddr}`));
|
|
107
|
+
|
|
108
|
+
socket.on('data', (data) => this.onData(peer, data));
|
|
109
|
+
socket.on('close', () => this.onPeerClose(peer));
|
|
110
|
+
socket.on('error', (err) => this.onPeerError(peer, err));
|
|
111
|
+
|
|
112
|
+
socket.setTimeout(30000);
|
|
113
|
+
socket.on('timeout', () => {
|
|
114
|
+
if (!peer.authenticated) {
|
|
115
|
+
this.disconnectPeer(peer, 'Authentication timeout');
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private onData(peer: Peer, data: Buffer): void {
|
|
121
|
+
peer.readBuffer = Buffer.concat([peer.readBuffer, data]);
|
|
122
|
+
|
|
123
|
+
if (!peer.authenticated) {
|
|
124
|
+
const body = readMessageFromPeer(peer);
|
|
125
|
+
if (body) {
|
|
126
|
+
this.handleAuthMessage(peer, body);
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private handleAuthMessage(peer: Peer, body: Buffer): void {
|
|
133
|
+
try {
|
|
134
|
+
const msg = JSON.parse(body.toString('utf-8'));
|
|
135
|
+
if (msg.kind === 'auth' && typeof msg.payload?.password === 'string') {
|
|
136
|
+
if (msg.payload.password === this.options.password) {
|
|
137
|
+
peer.authenticated = true;
|
|
138
|
+
peer.socket.write(encodeAuthOk());
|
|
139
|
+
console.log(chalk.green(`Peer authenticated: ${peer.socket.remoteAddress}`));
|
|
140
|
+
this.startDataStream(peer);
|
|
141
|
+
this.startPing(peer);
|
|
142
|
+
} else {
|
|
143
|
+
peer.socket.write(encodeAuthFail('Invalid password'));
|
|
144
|
+
this.disconnectPeer(peer, 'Authentication failed');
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
peer.socket.write(encodeAuthFail('Expected auth message first'));
|
|
148
|
+
this.disconnectPeer(peer, 'Expected auth message');
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
peer.socket.write(encodeAuthFail('Invalid message format'));
|
|
152
|
+
this.disconnectPeer(peer, 'Invalid message format');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private startDataStream(peer: Peer): void {
|
|
157
|
+
const send = async () => {
|
|
158
|
+
if (!peer.authenticated || peer.socket.destroyed) return;
|
|
159
|
+
try {
|
|
160
|
+
const data = await collectAll({ detailed: this.options.detailed });
|
|
161
|
+
peer.socket.write(encodeData(data));
|
|
162
|
+
} catch {
|
|
163
|
+
// skip failed tick
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
send();
|
|
168
|
+
peer.intervalHandle = setInterval(send, this.options.intervalMs);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private startPing(peer: Peer): void {
|
|
172
|
+
peer.pingHandle = setInterval(() => {
|
|
173
|
+
if (peer.socket.destroyed) return;
|
|
174
|
+
peer.socket.write(encodePing());
|
|
175
|
+
}, 5000);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private onPeerClose(peer: Peer): void {
|
|
179
|
+
this.cleanupPeer(peer);
|
|
180
|
+
console.log(chalk.yellow(`Peer disconnected: ${peer.socket.remoteAddress}`));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private onPeerError(peer: Peer, err: Error): void {
|
|
184
|
+
const code = (err as any).code;
|
|
185
|
+
if (code === 'ECONNRESET') {
|
|
186
|
+
console.log(chalk.yellow(`Peer connection reset: ${peer.socket.remoteAddress}`));
|
|
187
|
+
} else {
|
|
188
|
+
console.log(chalk.red(`Peer error: ${err.message}`));
|
|
189
|
+
}
|
|
190
|
+
this.cleanupPeer(peer);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private disconnectPeer(peer: Peer, reason: string): void {
|
|
194
|
+
try {
|
|
195
|
+
peer.socket.write(encodeDisconnect(reason));
|
|
196
|
+
} catch {
|
|
197
|
+
// socket may already be closed
|
|
198
|
+
}
|
|
199
|
+
peer.socket.destroy();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private cleanupPeer(peer: Peer): void {
|
|
203
|
+
if (peer.intervalHandle) {
|
|
204
|
+
clearInterval(peer.intervalHandle);
|
|
205
|
+
peer.intervalHandle = null;
|
|
206
|
+
}
|
|
207
|
+
if (peer.pingHandle) {
|
|
208
|
+
clearInterval(peer.pingHandle);
|
|
209
|
+
peer.pingHandle = null;
|
|
210
|
+
}
|
|
211
|
+
this.peers.delete(peer.socket);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export async function startP2PServer(options: P2PServerOptions): Promise<P2PServer> {
|
|
216
|
+
const server = new P2PServer(options);
|
|
217
|
+
await server.start();
|
|
218
|
+
return server;
|
|
219
|
+
}
|
package/src/p2p/types.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P2P live data transfer protocol types.
|
|
3
|
+
*
|
|
4
|
+
* Defines the message shapes exchanged between a pyre server
|
|
5
|
+
* (host) and a pyre client (peer) over a TCP connection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type P2PMessageKind = 'auth' | 'auth-ok' | 'auth-fail' | 'data' | 'ping' | 'pong' | 'disconnect';
|
|
9
|
+
|
|
10
|
+
export interface P2PMessage {
|
|
11
|
+
kind: P2PMessageKind;
|
|
12
|
+
payload: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AuthMessage {
|
|
16
|
+
password: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AuthOkMessage {
|
|
20
|
+
ok: true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AuthFailMessage {
|
|
24
|
+
ok: false;
|
|
25
|
+
reason: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DataMessage {
|
|
29
|
+
stats: import('../monitors/types.js').StatsData;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PingMessage {
|
|
33
|
+
ts: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface PongMessage {
|
|
37
|
+
ts: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DisconnectMessage {
|
|
41
|
+
reason: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type P2PPayload =
|
|
45
|
+
| AuthMessage
|
|
46
|
+
| AuthOkMessage
|
|
47
|
+
| AuthFailMessage
|
|
48
|
+
| DataMessage
|
|
49
|
+
| PingMessage
|
|
50
|
+
| PongMessage
|
|
51
|
+
| DisconnectMessage;
|
|
52
|
+
|
|
53
|
+
export interface P2PServerOptions {
|
|
54
|
+
host: string;
|
|
55
|
+
port: number;
|
|
56
|
+
password: string;
|
|
57
|
+
intervalMs: number;
|
|
58
|
+
detailed: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface P2PClientOptions {
|
|
62
|
+
host: string;
|
|
63
|
+
port: number;
|
|
64
|
+
password: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface P2PConnectionInfo {
|
|
68
|
+
host: string;
|
|
69
|
+
port: number;
|
|
70
|
+
peersConnected: number;
|
|
71
|
+
}
|
package/src/pyre_img
ADDED
|
Binary file
|
package/src/sparkline.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const BLOCKS = '▁▂▃▄▅▆▇█';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Render a series of numbers as a compact unicode sparkline.
|
|
5
|
+
*
|
|
6
|
+
* Values are normalised against `min`/`max` (auto-computed
|
|
7
|
+
* from the data if not supplied). The output uses eight
|
|
8
|
+
* Unicode block characters to represent the range, making
|
|
9
|
+
* it suitable for inline terminal graphs.
|
|
10
|
+
*
|
|
11
|
+
* @param values - Numeric series to render.
|
|
12
|
+
* @param opts.min - Minimum value for normalisation.
|
|
13
|
+
* @param opts.max - Maximum value for normalisation.
|
|
14
|
+
* @returns A string of block characters whose length equals `values.length`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* sparkline([10, 20, 15, 30, 25]); // "▁▂▃▅▄"
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function sparkline(values: number[], opts: { min?: number; max?: number } = {}): string {
|
|
22
|
+
if (!values.length) return '';
|
|
23
|
+
const min = opts.min ?? Math.min(...values);
|
|
24
|
+
const max = opts.max ?? Math.max(...values);
|
|
25
|
+
const range = max - min || 1;
|
|
26
|
+
|
|
27
|
+
return values
|
|
28
|
+
.map(v => {
|
|
29
|
+
const idx = Math.round(((v - min) / range) * (BLOCKS.length - 1));
|
|
30
|
+
const clamped = Math.min(BLOCKS.length - 1, Math.max(0, idx));
|
|
31
|
+
return BLOCKS[clamped];
|
|
32
|
+
})
|
|
33
|
+
.join('');
|
|
34
|
+
}
|
package/src/splash.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ASCII Splash Screen implementation based on the doom-fire effect.
|
|
3
|
+
*/
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const MAXV = 36;
|
|
7
|
+
const CHAR_RAMP = [' ', '.', '`', ',', ':', ';', '+', '*', '?', '%', '#', '@'];
|
|
8
|
+
|
|
9
|
+
// Colors for the fire effect
|
|
10
|
+
const palette = [
|
|
11
|
+
'#0a0505', '#1a0806', '#2a0b06', '#3f1004', '#591404', '#791804', '#9e1c04', '#c92004',
|
|
12
|
+
'#d63004', '#e34004', '#f05004', '#fd6004', '#ff7004', '#ff8004', '#ff9004', '#ffa004',
|
|
13
|
+
'#ffb004', '#ffc004', '#ffd004', '#ffe004', '#fff004', '#fff820', '#ffff40', '#ffff60',
|
|
14
|
+
'#ffff80', '#ffffa0', '#ffffc0', '#ffffe0', '#ffffff', '#ffffff', '#ffffff', '#ffffff',
|
|
15
|
+
'#ffffff', '#ffffff', '#ffffff', '#ffffff'
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function getRgb(hex: string) {
|
|
19
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
20
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
21
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
22
|
+
return { r, g, b };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const rgbPalette = palette.map(getRgb);
|
|
26
|
+
|
|
27
|
+
function getChar(val: number) {
|
|
28
|
+
const idx = Math.min(CHAR_RAMP.length - 1, Math.floor((val / (MAXV + 1)) * CHAR_RAMP.length));
|
|
29
|
+
return CHAR_RAMP[idx];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function showSplash() {
|
|
33
|
+
const cols = 60;
|
|
34
|
+
const rows = 15;
|
|
35
|
+
let fire = new Uint8Array(cols * rows);
|
|
36
|
+
|
|
37
|
+
// Clear screen
|
|
38
|
+
process.stdout.write('\x1b[2J');
|
|
39
|
+
const termRows = process.stdout.rows || 24;
|
|
40
|
+
const termCols = process.stdout.columns || 80;
|
|
41
|
+
const startRow = Math.max(0, Math.floor((termRows - rows) / 2));
|
|
42
|
+
const startCol = Math.max(0, Math.floor((termCols - cols) / 2));
|
|
43
|
+
|
|
44
|
+
return new Promise<void>((resolve) => {
|
|
45
|
+
const interval = setInterval(() => {
|
|
46
|
+
// Bottom row heat
|
|
47
|
+
for (let x = 0; x < cols; x++) {
|
|
48
|
+
fire[(rows - 1) * cols + x] = MAXV - Math.floor(Math.random() * 4);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Spread fire
|
|
52
|
+
for (let y = rows - 1; y >= 1; y--) {
|
|
53
|
+
for (let x = 0; x < cols; x++) {
|
|
54
|
+
const src = y * cols + x;
|
|
55
|
+
const val = fire[src];
|
|
56
|
+
if (val === 0) {
|
|
57
|
+
fire[(y - 1) * cols + x] = 0;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const decay = Math.floor(Math.random() * 3);
|
|
61
|
+
const drift = Math.floor(Math.random() * 3) - 1;
|
|
62
|
+
let nx = x + drift;
|
|
63
|
+
if (nx < 0) nx = 0; else if (nx >= cols) nx = cols - 1;
|
|
64
|
+
fire[(y - 1) * cols + nx] = Math.max(0, val - decay);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Draw
|
|
69
|
+
let output = '';
|
|
70
|
+
for (let i = 0; i < startRow; i++) output += '\n';
|
|
71
|
+
|
|
72
|
+
for (let y = 0; y < rows; y++) {
|
|
73
|
+
output += ' '.repeat(startCol);
|
|
74
|
+
for (let x = 0; x < cols; x++) {
|
|
75
|
+
const v = fire[y * cols + x];
|
|
76
|
+
const color = rgbPalette[Math.min(v, rgbPalette.length - 1)];
|
|
77
|
+
output += chalk.rgb(color.r, color.g, color.b)(getChar(v));
|
|
78
|
+
}
|
|
79
|
+
output += '\n';
|
|
80
|
+
}
|
|
81
|
+
output += ' '.repeat(Math.max(0, startCol + (cols - 45) / 2)) + chalk.bold.white('🔥 pyre — system monitor (warming up sensors...)\n');
|
|
82
|
+
output += ' '.repeat(Math.max(0, startCol + (cols - 25) / 2)) + chalk.dim('[Press Enter to continue]');
|
|
83
|
+
|
|
84
|
+
// Clear viewport area and redraw
|
|
85
|
+
process.stdout.write(`\x1b[H${output}`);
|
|
86
|
+
}, 65);
|
|
87
|
+
|
|
88
|
+
const onEnter = () => {
|
|
89
|
+
process.stdin.removeListener('data', onEnter);
|
|
90
|
+
clearInterval(interval);
|
|
91
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
92
|
+
resolve();
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
process.stdin.setRawMode(true);
|
|
96
|
+
process.stdin.resume();
|
|
97
|
+
process.stdin.on('data', onEnter);
|
|
98
|
+
|
|
99
|
+
// Auto-exit after 5s
|
|
100
|
+
setTimeout(onEnter, 5000);
|
|
101
|
+
});
|
|
102
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": false,
|
|
14
|
+
"sourceMap": false,
|
|
15
|
+
"noEmit": false
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|