@continum/cli 0.1.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.
Files changed (79) hide show
  1. package/README.md +481 -0
  2. package/SETUP.md +517 -0
  3. package/dist/api/client.d.ts +17 -0
  4. package/dist/api/client.d.ts.map +1 -0
  5. package/dist/api/client.js +70 -0
  6. package/dist/api/client.js.map +1 -0
  7. package/dist/commands/init.d.ts +4 -0
  8. package/dist/commands/init.d.ts.map +1 -0
  9. package/dist/commands/init.js +104 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/login.d.ts +2 -0
  12. package/dist/commands/login.d.ts.map +1 -0
  13. package/dist/commands/login.js +217 -0
  14. package/dist/commands/login.js.map +1 -0
  15. package/dist/commands/patterns.d.ts +3 -0
  16. package/dist/commands/patterns.d.ts.map +1 -0
  17. package/dist/commands/patterns.js +67 -0
  18. package/dist/commands/patterns.js.map +1 -0
  19. package/dist/commands/scan.d.ts +11 -0
  20. package/dist/commands/scan.d.ts.map +1 -0
  21. package/dist/commands/scan.js +219 -0
  22. package/dist/commands/scan.js.map +1 -0
  23. package/dist/commands/status.d.ts +2 -0
  24. package/dist/commands/status.d.ts.map +1 -0
  25. package/dist/commands/status.js +61 -0
  26. package/dist/commands/status.js.map +1 -0
  27. package/dist/commands/uninstall.d.ts +2 -0
  28. package/dist/commands/uninstall.d.ts.map +1 -0
  29. package/dist/commands/uninstall.js +87 -0
  30. package/dist/commands/uninstall.js.map +1 -0
  31. package/dist/config/default-config.d.ts +3 -0
  32. package/dist/config/default-config.d.ts.map +1 -0
  33. package/dist/config/default-config.js +25 -0
  34. package/dist/config/default-config.js.map +1 -0
  35. package/dist/config/loader.d.ts +11 -0
  36. package/dist/config/loader.d.ts.map +1 -0
  37. package/dist/config/loader.js +96 -0
  38. package/dist/config/loader.js.map +1 -0
  39. package/dist/git/git-utils.d.ts +8 -0
  40. package/dist/git/git-utils.d.ts.map +1 -0
  41. package/dist/git/git-utils.js +130 -0
  42. package/dist/git/git-utils.js.map +1 -0
  43. package/dist/index.d.ts +3 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +63 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/scanner/local-scan.d.ts +15 -0
  48. package/dist/scanner/local-scan.d.ts.map +1 -0
  49. package/dist/scanner/local-scan.js +227 -0
  50. package/dist/scanner/local-scan.js.map +1 -0
  51. package/dist/scanner/pattern-updater.d.ts +12 -0
  52. package/dist/scanner/pattern-updater.d.ts.map +1 -0
  53. package/dist/scanner/pattern-updater.js +110 -0
  54. package/dist/scanner/pattern-updater.js.map +1 -0
  55. package/dist/scanner/patterns.d.ts +5 -0
  56. package/dist/scanner/patterns.d.ts.map +1 -0
  57. package/dist/scanner/patterns.js +145 -0
  58. package/dist/scanner/patterns.js.map +1 -0
  59. package/dist/types.d.ts +59 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +3 -0
  62. package/dist/types.js.map +1 -0
  63. package/package.json +40 -0
  64. package/src/api/client.ts +77 -0
  65. package/src/commands/init.ts +113 -0
  66. package/src/commands/login.ts +205 -0
  67. package/src/commands/patterns.ts +68 -0
  68. package/src/commands/scan.ts +257 -0
  69. package/src/commands/status.ts +57 -0
  70. package/src/commands/uninstall.ts +55 -0
  71. package/src/config/default-config.ts +23 -0
  72. package/src/config/loader.ts +67 -0
  73. package/src/git/git-utils.ts +95 -0
  74. package/src/index.ts +72 -0
  75. package/src/scanner/local-scan.ts +222 -0
  76. package/src/scanner/pattern-updater.ts +94 -0
  77. package/src/scanner/patterns.ts +156 -0
  78. package/src/types.ts +64 -0
  79. package/tsconfig.json +19 -0
@@ -0,0 +1,222 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { ScanResult, Violation, UnknownPattern, Pattern, ContinumConfig } from '../types';
4
+ import { BUILTIN_PATTERNS, isHighEntropyCredential, calculateEntropy } from './patterns';
5
+
6
+ export class LocalScanner {
7
+ private patterns: Pattern[] = [];
8
+ private config: ContinumConfig;
9
+
10
+ constructor(config: ContinumConfig, customPatterns: Pattern[] = []) {
11
+ this.config = config;
12
+ this.patterns = [...BUILTIN_PATTERNS, ...customPatterns];
13
+
14
+ // Add custom regex patterns from config
15
+ if (config.patterns?.custom) {
16
+ config.patterns.custom.forEach((pattern, index) => {
17
+ this.patterns.push({
18
+ pattern,
19
+ patternType: 'CUSTOM',
20
+ description: `Custom Pattern ${index + 1}`,
21
+ severity: 'HIGH'
22
+ });
23
+ });
24
+ }
25
+ }
26
+
27
+ async scanFiles(files: string[]): Promise<ScanResult> {
28
+ const violations: Violation[] = [];
29
+ const unknownPatterns: UnknownPattern[] = [];
30
+ let filesScanned = 0;
31
+
32
+ for (const file of files) {
33
+ // Skip ignored files
34
+ if (this.shouldIgnore(file)) {
35
+ continue;
36
+ }
37
+
38
+ try {
39
+ const content = fs.readFileSync(file, 'utf-8');
40
+ const lines = content.split('\n');
41
+
42
+ filesScanned++;
43
+
44
+ // Scan with known patterns
45
+ const fileViolations = this.scanContent(content, file, lines);
46
+ violations.push(...fileViolations);
47
+
48
+ // Detect high-entropy strings that might be credentials
49
+ const possibleCredentials = this.detectHighEntropyStrings(content, file, lines);
50
+ unknownPatterns.push(...possibleCredentials);
51
+ } catch (error) {
52
+ // Skip files that can't be read (binary, etc.)
53
+ continue;
54
+ }
55
+ }
56
+
57
+ return {
58
+ violations,
59
+ unknownPatterns,
60
+ filesScanned,
61
+ clean: violations.length === 0 && unknownPatterns.length === 0
62
+ };
63
+ }
64
+
65
+ private scanContent(content: string, file: string, lines: string[]): Violation[] {
66
+ const violations: Violation[] = [];
67
+
68
+ for (const pattern of this.patterns) {
69
+ const regex = new RegExp(pattern.pattern, 'gi');
70
+ let match;
71
+
72
+ while ((match = regex.exec(content)) !== null) {
73
+ const lineNumber = this.getLineNumber(content, match.index);
74
+
75
+ violations.push({
76
+ file,
77
+ line: lineNumber,
78
+ type: pattern.patternType,
79
+ pattern: pattern.pattern,
80
+ value: match[0],
81
+ severity: pattern.severity,
82
+ message: pattern.description
83
+ });
84
+ }
85
+ }
86
+
87
+ return violations;
88
+ }
89
+
90
+ private detectHighEntropyStrings(content: string, file: string, lines: string[]): UnknownPattern[] {
91
+ const unknownPatterns: UnknownPattern[] = [];
92
+
93
+ // Look for string assignments with high entropy
94
+ const stringPatterns = [
95
+ /(\w+)\s*[:=]\s*["']([a-zA-Z0-9_\-+=\/]{16,})["']/g,
96
+ /const\s+(\w+)\s*=\s*["']([a-zA-Z0-9_\-+=\/]{16,})["']/g,
97
+ /let\s+(\w+)\s*=\s*["']([a-zA-Z0-9_\-+=\/]{16,})["']/g,
98
+ /var\s+(\w+)\s*=\s*["']([a-zA-Z0-9_\-+=\/]{16,})["']/g
99
+ ];
100
+
101
+ for (const pattern of stringPatterns) {
102
+ let match;
103
+ while ((match = pattern.exec(content)) !== null) {
104
+ const variableName = match[1];
105
+ const value = match[2];
106
+ const lineNumber = this.getLineNumber(content, match.index);
107
+
108
+ // Get surrounding context
109
+ const contextStart = Math.max(0, lineNumber - 3);
110
+ const contextEnd = Math.min(lines.length, lineNumber + 2);
111
+ const surroundingCode = lines.slice(contextStart, contextEnd).join('\n');
112
+
113
+ // Check if this looks like a credential
114
+ if (isHighEntropyCredential(value, variableName + ' ' + surroundingCode)) {
115
+ // Check if it matches any known pattern
116
+ const matchesKnown = this.patterns.some(p =>
117
+ new RegExp(p.pattern, 'i').test(value)
118
+ );
119
+
120
+ if (!matchesKnown) {
121
+ const suggestedPattern = this.extractPattern(value);
122
+ const confidence = this.calculateConfidence(value, variableName);
123
+
124
+ unknownPatterns.push({
125
+ value,
126
+ suggestedPattern,
127
+ confidence,
128
+ context: {
129
+ file,
130
+ line: lineNumber,
131
+ variableName,
132
+ surroundingCode
133
+ }
134
+ });
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ return unknownPatterns;
141
+ }
142
+
143
+ private extractPattern(value: string): string {
144
+ // Try to extract a pattern from the value
145
+ const hasPrefix = /^[a-z]{2,}_/.test(value);
146
+
147
+ if (hasPrefix) {
148
+ const prefix = value.match(/^[a-z_]+/)?.[0] || '';
149
+ const rest = value.slice(prefix.length);
150
+
151
+ // Determine character set
152
+ const hasUppercase = /[A-Z]/.test(rest);
153
+ const hasLowercase = /[a-z]/.test(rest);
154
+ const hasDigits = /[0-9]/.test(rest);
155
+ const hasSpecial = /[_\-]/.test(rest);
156
+
157
+ let charSet = '';
158
+ if (hasUppercase) charSet += 'A-Z';
159
+ if (hasLowercase) charSet += 'a-z';
160
+ if (hasDigits) charSet += '0-9';
161
+ if (hasSpecial) charSet += '_\\-';
162
+
163
+ return `${prefix}[${charSet}]{${rest.length}}`;
164
+ }
165
+
166
+ // No clear prefix, just match character set and length
167
+ const hasUppercase = /[A-Z]/.test(value);
168
+ const hasLowercase = /[a-z]/.test(value);
169
+ const hasDigits = /[0-9]/.test(value);
170
+ const hasSpecial = /[_\-+=\/]/.test(value);
171
+
172
+ let charSet = '';
173
+ if (hasUppercase) charSet += 'A-Z';
174
+ if (hasLowercase) charSet += 'a-z';
175
+ if (hasDigits) charSet += '0-9';
176
+ if (hasSpecial) charSet += '_\\-+=\\/';
177
+
178
+ return `[${charSet}]{${value.length}}`;
179
+ }
180
+
181
+ private calculateConfidence(value: string, variableName: string): 'HIGH' | 'MEDIUM' | 'LOW' {
182
+ const entropy = calculateEntropy(value);
183
+ const suspiciousNames = ['key', 'token', 'secret', 'password', 'credential', 'auth', 'api'];
184
+ const hasSuspiciousName = suspiciousNames.some(name =>
185
+ variableName.toLowerCase().includes(name)
186
+ );
187
+
188
+ if (entropy > 5.0 && hasSuspiciousName) return 'HIGH';
189
+ if (entropy > 4.5 && hasSuspiciousName) return 'MEDIUM';
190
+ if (entropy > 5.5) return 'MEDIUM';
191
+ return 'LOW';
192
+ }
193
+
194
+ private getLineNumber(content: string, index: number): number {
195
+ return content.substring(0, index).split('\n').length;
196
+ }
197
+
198
+ private shouldIgnore(file: string): boolean {
199
+ const normalizedFile = file.replace(/\\/g, '/');
200
+
201
+ return this.config.ignore.some(pattern => {
202
+ // Convert glob pattern to regex
203
+ const regexPattern = pattern
204
+ .replace(/\*\*/g, '.*')
205
+ .replace(/\*/g, '[^/]*')
206
+ .replace(/\?/g, '.');
207
+
208
+ const regex = new RegExp(regexPattern);
209
+ return regex.test(normalizedFile);
210
+ });
211
+ }
212
+
213
+ redactValue(value: string): string {
214
+ if (value.length <= 8) {
215
+ return '•'.repeat(value.length);
216
+ }
217
+ const visibleChars = 4;
218
+ const start = value.substring(0, visibleChars);
219
+ const redacted = '•'.repeat(Math.min(10, value.length - visibleChars));
220
+ return start + redacted;
221
+ }
222
+ }
@@ -0,0 +1,94 @@
1
+ import * as fs from 'fs';
2
+ import { ContinumApiClient } from '../api/client';
3
+ import { Pattern } from '../types';
4
+ import { getCacheFile, getCacheDir } from '../config/loader';
5
+ import { BUILTIN_PATTERNS } from './patterns';
6
+
7
+ interface PatternCache {
8
+ builtin: Pattern[];
9
+ customer: Pattern[];
10
+ global: Pattern[];
11
+ lastUpdate: number;
12
+ }
13
+
14
+ export class PatternUpdater {
15
+ private client: ContinumApiClient;
16
+ private cacheFile: string;
17
+
18
+ constructor(client: ContinumApiClient) {
19
+ this.client = client;
20
+ this.cacheFile = getCacheFile();
21
+ }
22
+
23
+ async updatePatterns(force: boolean = false): Promise<void> {
24
+ const cache = this.loadCache();
25
+
26
+ // Update every 24 hours, or on force
27
+ if (!force && Date.now() - cache.lastUpdate < 86400000) {
28
+ return;
29
+ }
30
+
31
+ try {
32
+ // Fetch patterns from API
33
+ const patterns = await this.client.getPatternLibrary();
34
+
35
+ // Separate customer and global patterns
36
+ const customerPatterns = patterns.filter(p => !('isGlobal' in p) || !(p as any).isGlobal);
37
+ const globalPatterns = patterns.filter(p => (p as any).isGlobal);
38
+
39
+ // Update cache
40
+ const newCache: PatternCache = {
41
+ builtin: BUILTIN_PATTERNS,
42
+ customer: customerPatterns,
43
+ global: globalPatterns,
44
+ lastUpdate: Date.now()
45
+ };
46
+
47
+ this.saveCache(newCache);
48
+ } catch (error) {
49
+ // If update fails, continue with cached patterns
50
+ console.warn('Failed to update patterns, using cache');
51
+ }
52
+ }
53
+
54
+ loadPatterns(): Pattern[] {
55
+ const cache = this.loadCache();
56
+ return [
57
+ ...cache.builtin,
58
+ ...cache.customer,
59
+ ...cache.global
60
+ ];
61
+ }
62
+
63
+ private loadCache(): PatternCache {
64
+ if (!fs.existsSync(this.cacheFile)) {
65
+ return {
66
+ builtin: BUILTIN_PATTERNS,
67
+ customer: [],
68
+ global: [],
69
+ lastUpdate: 0
70
+ };
71
+ }
72
+
73
+ try {
74
+ const content = fs.readFileSync(this.cacheFile, 'utf-8');
75
+ return JSON.parse(content);
76
+ } catch (error) {
77
+ return {
78
+ builtin: BUILTIN_PATTERNS,
79
+ customer: [],
80
+ global: [],
81
+ lastUpdate: 0
82
+ };
83
+ }
84
+ }
85
+
86
+ private saveCache(cache: PatternCache): void {
87
+ const cacheDir = getCacheDir();
88
+ if (!fs.existsSync(cacheDir)) {
89
+ fs.mkdirSync(cacheDir, { recursive: true });
90
+ }
91
+
92
+ fs.writeFileSync(this.cacheFile, JSON.stringify(cache, null, 2));
93
+ }
94
+ }
@@ -0,0 +1,156 @@
1
+ import { Pattern } from '../types';
2
+
3
+ // Built-in credential patterns
4
+ export const BUILTIN_PATTERNS: Pattern[] = [
5
+ // AWS
6
+ {
7
+ pattern: 'AKIA[0-9A-Z]{16}',
8
+ patternType: 'AWS_ACCESS_KEY',
9
+ description: 'AWS Access Key ID',
10
+ severity: 'CRITICAL'
11
+ },
12
+ {
13
+ pattern: 'aws_secret_access_key\\s*=\\s*["\']?([A-Za-z0-9/+=]{40})["\']?',
14
+ patternType: 'AWS_SECRET_KEY',
15
+ description: 'AWS Secret Access Key',
16
+ severity: 'CRITICAL'
17
+ },
18
+
19
+ // Stripe
20
+ {
21
+ pattern: 'sk_live_[0-9a-zA-Z]{24,}',
22
+ patternType: 'STRIPE_LIVE_KEY',
23
+ description: 'Stripe Live Secret Key',
24
+ severity: 'CRITICAL'
25
+ },
26
+ {
27
+ pattern: 'rk_live_[0-9a-zA-Z]{24,}',
28
+ patternType: 'STRIPE_RESTRICTED_KEY',
29
+ description: 'Stripe Restricted Key',
30
+ severity: 'HIGH'
31
+ },
32
+
33
+ // GitHub
34
+ {
35
+ pattern: 'ghp_[0-9a-zA-Z]{36}',
36
+ patternType: 'GITHUB_PAT',
37
+ description: 'GitHub Personal Access Token',
38
+ severity: 'CRITICAL'
39
+ },
40
+ {
41
+ pattern: 'gho_[0-9a-zA-Z]{36}',
42
+ patternType: 'GITHUB_OAUTH',
43
+ description: 'GitHub OAuth Token',
44
+ severity: 'CRITICAL'
45
+ },
46
+
47
+ // Anthropic
48
+ {
49
+ pattern: 'sk-ant-api03-[0-9a-zA-Z_-]{95}',
50
+ patternType: 'ANTHROPIC_API_KEY',
51
+ description: 'Anthropic API Key',
52
+ severity: 'CRITICAL'
53
+ },
54
+
55
+ // OpenAI
56
+ {
57
+ pattern: 'sk-[a-zA-Z0-9]{48}',
58
+ patternType: 'OPENAI_API_KEY',
59
+ description: 'OpenAI API Key',
60
+ severity: 'CRITICAL'
61
+ },
62
+
63
+ // Database Connection Strings
64
+ {
65
+ pattern: 'postgresql://[^\\s:]+:[^\\s@]+@[^\\s/]+',
66
+ patternType: 'POSTGRES_CONNECTION',
67
+ description: 'PostgreSQL Connection String with Password',
68
+ severity: 'CRITICAL'
69
+ },
70
+ {
71
+ pattern: 'mysql://[^\\s:]+:[^\\s@]+@[^\\s/]+',
72
+ patternType: 'MYSQL_CONNECTION',
73
+ description: 'MySQL Connection String with Password',
74
+ severity: 'CRITICAL'
75
+ },
76
+ {
77
+ pattern: 'mongodb(\\+srv)?://[^\\s:]+:[^\\s@]+@[^\\s/]+',
78
+ patternType: 'MONGODB_CONNECTION',
79
+ description: 'MongoDB Connection String with Password',
80
+ severity: 'CRITICAL'
81
+ },
82
+
83
+ // Private Keys
84
+ {
85
+ pattern: '-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----',
86
+ patternType: 'PRIVATE_KEY',
87
+ description: 'Private Key (PEM format)',
88
+ severity: 'CRITICAL'
89
+ },
90
+
91
+ // Generic API Keys (high entropy)
92
+ {
93
+ pattern: '(api[_-]?key|apikey|api[_-]?secret)\\s*[:=]\\s*["\']([a-zA-Z0-9_\\-]{32,})["\']',
94
+ patternType: 'GENERIC_API_KEY',
95
+ description: 'Generic API Key',
96
+ severity: 'HIGH'
97
+ },
98
+
99
+ // JWT Tokens
100
+ {
101
+ pattern: 'eyJ[a-zA-Z0-9_-]*\\.eyJ[a-zA-Z0-9_-]*\\.[a-zA-Z0-9_-]*',
102
+ patternType: 'JWT_TOKEN',
103
+ description: 'JWT Token',
104
+ severity: 'HIGH'
105
+ },
106
+
107
+ // UK PII
108
+ {
109
+ pattern: '\\b[A-Z]{2}[0-9]{6}[A-Z]?\\b',
110
+ patternType: 'NHS_NUMBER',
111
+ description: 'NHS Number',
112
+ severity: 'HIGH'
113
+ },
114
+ {
115
+ pattern: '\\b[A-Z]{2}\\s?[0-9]{2}\\s?[0-9]{2}\\s?[0-9]{2}\\s?[A-Z]\\b',
116
+ patternType: 'NI_NUMBER',
117
+ description: 'National Insurance Number',
118
+ severity: 'HIGH'
119
+ }
120
+ ];
121
+
122
+ // Calculate entropy of a string (measure of randomness)
123
+ export function calculateEntropy(str: string): number {
124
+ const len = str.length;
125
+ const frequencies: { [key: string]: number } = {};
126
+
127
+ for (const char of str) {
128
+ frequencies[char] = (frequencies[char] || 0) + 1;
129
+ }
130
+
131
+ let entropy = 0;
132
+ for (const char in frequencies) {
133
+ const p = frequencies[char] / len;
134
+ entropy -= p * Math.log2(p);
135
+ }
136
+
137
+ return entropy;
138
+ }
139
+
140
+ // Check if a string looks like a credential based on entropy and context
141
+ export function isHighEntropyCredential(value: string, context: string): boolean {
142
+ // Must be long enough
143
+ if (value.length < 16) return false;
144
+
145
+ // Must have high entropy (randomness)
146
+ const entropy = calculateEntropy(value);
147
+ if (entropy < 4.5) return false;
148
+
149
+ // Must be in a suspicious context
150
+ const suspiciousPatterns = [
151
+ /\b(key|token|secret|password|credential|auth|api)\b/i,
152
+ /\b(access|private|bearer|jwt)\b/i
153
+ ];
154
+
155
+ return suspiciousPatterns.some(pattern => pattern.test(context));
156
+ }
package/src/types.ts ADDED
@@ -0,0 +1,64 @@
1
+ export interface ContinumConfig {
2
+ scanOnCommit: boolean;
3
+ sandbox: string;
4
+ apiUrl?: string;
5
+ apiKey?: string;
6
+ block: RiskLevel[];
7
+ warn: RiskLevel[];
8
+ ignore: string[];
9
+ patterns?: {
10
+ custom?: string[];
11
+ };
12
+ }
13
+
14
+ export type RiskLevel = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
15
+
16
+ export interface Violation {
17
+ file: string;
18
+ line: number;
19
+ type: string;
20
+ pattern: string;
21
+ value: string;
22
+ severity: RiskLevel;
23
+ message?: string;
24
+ }
25
+
26
+ export interface UnknownPattern {
27
+ value: string;
28
+ suggestedPattern: string;
29
+ confidence: 'HIGH' | 'MEDIUM' | 'LOW';
30
+ context: {
31
+ file: string;
32
+ line: number;
33
+ variableName?: string;
34
+ surroundingCode?: string;
35
+ };
36
+ }
37
+
38
+ export interface ScanResult {
39
+ violations: Violation[];
40
+ unknownPatterns: UnknownPattern[];
41
+ filesScanned: number;
42
+ clean: boolean;
43
+ }
44
+
45
+ export interface Pattern {
46
+ pattern: string;
47
+ patternType: string;
48
+ description: string;
49
+ severity: RiskLevel;
50
+ }
51
+
52
+ export interface ApprovePatternDto {
53
+ pattern: string;
54
+ patternType: string;
55
+ description: string;
56
+ severity: RiskLevel;
57
+ exampleValue: string;
58
+ confidence: string;
59
+ context: {
60
+ file: string;
61
+ line: number;
62
+ variableName?: string;
63
+ };
64
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }