@mseep/ai-tech-app-agent 1.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/.env +24 -0
- package/.env.example +24 -0
- package/Jenkinsfile +210 -0
- package/MCP-SERVER-GUIDE.md +405 -0
- package/README.MD +450 -0
- package/dist/config/app.config.d.ts +65 -0
- package/dist/config/app.config.d.ts.map +1 -0
- package/dist/config/app.config.js +94 -0
- package/dist/config/app.config.js.map +1 -0
- package/dist/config/llm.config.d.ts +63 -0
- package/dist/config/llm.config.d.ts.map +1 -0
- package/dist/config/llm.config.js +158 -0
- package/dist/config/llm.config.js.map +1 -0
- package/dist/config/mcp.config.d.ts +175 -0
- package/dist/config/mcp.config.d.ts.map +1 -0
- package/dist/config/mcp.config.js +215 -0
- package/dist/config/mcp.config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +175 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/llamaClient.d.ts +14 -0
- package/dist/llm/llamaClient.d.ts.map +1 -0
- package/dist/llm/llamaClient.js +136 -0
- package/dist/llm/llamaClient.js.map +1 -0
- package/dist/mcp/mcpClient.d.ts +132 -0
- package/dist/mcp/mcpClient.d.ts.map +1 -0
- package/dist/mcp/mcpClient.js +784 -0
- package/dist/mcp/mcpClient.js.map +1 -0
- package/dist/models/testSpec.d.ts +78 -0
- package/dist/models/testSpec.d.ts.map +1 -0
- package/dist/models/testSpec.js +3 -0
- package/dist/models/testSpec.js.map +1 -0
- package/dist/orchestrator/aiTestRunner.d.ts +18 -0
- package/dist/orchestrator/aiTestRunner.d.ts.map +1 -0
- package/dist/orchestrator/aiTestRunner.js +247 -0
- package/dist/orchestrator/aiTestRunner.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +49 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/promptBuilder.d.ts +62 -0
- package/dist/utils/promptBuilder.d.ts.map +1 -0
- package/dist/utils/promptBuilder.js +333 -0
- package/dist/utils/promptBuilder.js.map +1 -0
- package/knowledge/app-knowledge.txt +100 -0
- package/logs/combined.log +486 -0
- package/logs/error.log +50 -0
- package/package.json +62 -0
- package/reports/screenshots/screenshot_1764535110518.png +0 -0
- package/reports/test-report.json +106 -0
- package/scripts/check-mcp-server.sh +100 -0
- package/scripts/extract-pom-knowledge.js +222 -0
- package/scripts/pre-test-setup.js +262 -0
- package/scripts/start-mcp-server.sh +76 -0
- package/src/config/app.config.ts +175 -0
- package/src/config/llm.config.ts +220 -0
- package/src/config/mcp.config.ts +291 -0
- package/src/index.ts +161 -0
- package/src/llm/llamaClient.ts +159 -0
- package/src/mcp/mcpClient.ts +878 -0
- package/src/models/testSpec.ts +85 -0
- package/src/orchestrator/aiTestRunner.ts +286 -0
- package/src/utils/logger.ts +59 -0
- package/src/utils/promptBuilder.ts +384 -0
- package/tests/nlp-specs/login-flow.yaml +31 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import llmConfig, { PromptTemplates } from '../config/llm.config';
|
|
2
|
+
import { UIContext, UIElement } from '../models/testSpec';
|
|
3
|
+
|
|
4
|
+
export class PromptBuilder {
|
|
5
|
+
private maxUIElements: number;
|
|
6
|
+
private maxContextTokens: number;
|
|
7
|
+
private includeAppContext: boolean;
|
|
8
|
+
private includeUIContext: boolean;
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
this.maxUIElements = llmConfig.prompt.maxUIElements;
|
|
12
|
+
this.maxContextTokens = llmConfig.prompt.maxContextTokens;
|
|
13
|
+
this.includeAppContext = llmConfig.prompt.includeAppContext;
|
|
14
|
+
this.includeUIContext = llmConfig.prompt.includeUIContext;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build the main action planning prompt
|
|
19
|
+
*/
|
|
20
|
+
buildActionPlanningPrompt(
|
|
21
|
+
stepText: string,
|
|
22
|
+
uiContext: UIContext,
|
|
23
|
+
appKnowledge?: string,
|
|
24
|
+
platform?: 'android' | 'ios'
|
|
25
|
+
): string {
|
|
26
|
+
const sections: string[] = [];
|
|
27
|
+
|
|
28
|
+
// System context
|
|
29
|
+
sections.push(PromptTemplates.actionPlanning);
|
|
30
|
+
sections.push('');
|
|
31
|
+
|
|
32
|
+
// App-specific knowledge
|
|
33
|
+
if (this.includeAppContext && appKnowledge) {
|
|
34
|
+
sections.push('**App-Specific Knowledge:**');
|
|
35
|
+
sections.push(this.truncateText(appKnowledge, 1000));
|
|
36
|
+
sections.push('');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Platform information
|
|
40
|
+
if (platform) {
|
|
41
|
+
sections.push(`**Platform:** ${platform.toUpperCase()}`);
|
|
42
|
+
sections.push('');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Current UI state
|
|
46
|
+
if (this.includeUIContext) {
|
|
47
|
+
sections.push('**Current UI State:**');
|
|
48
|
+
sections.push(this.formatUIContext(uiContext));
|
|
49
|
+
sections.push('');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Test step to execute
|
|
53
|
+
sections.push('**Test Step to Execute:**');
|
|
54
|
+
sections.push(`"${stepText}"`);
|
|
55
|
+
sections.push('');
|
|
56
|
+
|
|
57
|
+
// Output format instructions
|
|
58
|
+
sections.push(this.getOutputFormatInstructions());
|
|
59
|
+
|
|
60
|
+
return sections.join('\n');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Build prompt for context analysis
|
|
65
|
+
*/
|
|
66
|
+
buildContextAnalysisPrompt(
|
|
67
|
+
uiContext: UIContext,
|
|
68
|
+
focusArea?: string
|
|
69
|
+
): string {
|
|
70
|
+
const sections: string[] = [];
|
|
71
|
+
|
|
72
|
+
sections.push(PromptTemplates.contextAnalysis);
|
|
73
|
+
sections.push('');
|
|
74
|
+
|
|
75
|
+
if (focusArea) {
|
|
76
|
+
sections.push(`**Focus Area:** ${focusArea}`);
|
|
77
|
+
sections.push('');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
sections.push('**Current UI State:**');
|
|
81
|
+
sections.push(this.formatUIContext(uiContext));
|
|
82
|
+
sections.push('');
|
|
83
|
+
|
|
84
|
+
sections.push('Identify and list the most relevant interactive elements with their best selectors.');
|
|
85
|
+
|
|
86
|
+
return sections.join('\n');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build prompt for error recovery
|
|
91
|
+
*/
|
|
92
|
+
buildErrorRecoveryPrompt(
|
|
93
|
+
stepText: string,
|
|
94
|
+
previousError: string,
|
|
95
|
+
uiContext: UIContext,
|
|
96
|
+
attemptNumber: number
|
|
97
|
+
): string {
|
|
98
|
+
const sections: string[] = [];
|
|
99
|
+
|
|
100
|
+
sections.push(PromptTemplates.errorRecovery);
|
|
101
|
+
sections.push('');
|
|
102
|
+
|
|
103
|
+
sections.push(`**Original Test Step:** "${stepText}"`);
|
|
104
|
+
sections.push(`**Attempt Number:** ${attemptNumber}`);
|
|
105
|
+
sections.push(`**Previous Error:** ${previousError}`);
|
|
106
|
+
sections.push('');
|
|
107
|
+
|
|
108
|
+
sections.push('**Current UI State:**');
|
|
109
|
+
sections.push(this.formatUIContext(uiContext));
|
|
110
|
+
sections.push('');
|
|
111
|
+
|
|
112
|
+
sections.push('**Instructions:**');
|
|
113
|
+
sections.push('1. Analyze why the previous attempt failed');
|
|
114
|
+
sections.push('2. Check if the UI state has changed');
|
|
115
|
+
sections.push('3. Suggest alternative actions or selectors');
|
|
116
|
+
sections.push('4. If the step is impossible, return an empty actions array with reasoning');
|
|
117
|
+
sections.push('');
|
|
118
|
+
|
|
119
|
+
sections.push(this.getOutputFormatInstructions());
|
|
120
|
+
|
|
121
|
+
return sections.join('\n');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Build prompt for assertion generation
|
|
126
|
+
*/
|
|
127
|
+
buildAssertionPrompt(
|
|
128
|
+
expectedOutcome: string,
|
|
129
|
+
uiContext: UIContext
|
|
130
|
+
): string {
|
|
131
|
+
const sections: string[] = [];
|
|
132
|
+
|
|
133
|
+
sections.push(PromptTemplates.assertionGeneration);
|
|
134
|
+
sections.push('');
|
|
135
|
+
|
|
136
|
+
sections.push(`**Expected Outcome:** "${expectedOutcome}"`);
|
|
137
|
+
sections.push('');
|
|
138
|
+
|
|
139
|
+
sections.push('**Current UI State:**');
|
|
140
|
+
sections.push(this.formatUIContext(uiContext));
|
|
141
|
+
sections.push('');
|
|
142
|
+
|
|
143
|
+
sections.push('Generate assertion actions to verify the expected outcome.');
|
|
144
|
+
sections.push('');
|
|
145
|
+
|
|
146
|
+
sections.push(this.getOutputFormatInstructions());
|
|
147
|
+
|
|
148
|
+
return sections.join('\n');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Format UI context for inclusion in prompts
|
|
153
|
+
*/
|
|
154
|
+
private formatUIContext(uiContext: UIContext): string {
|
|
155
|
+
const lines: string[] = [];
|
|
156
|
+
|
|
157
|
+
// Current screen/activity
|
|
158
|
+
if (uiContext.currentActivity) {
|
|
159
|
+
lines.push(`Current Screen: ${uiContext.currentActivity}`);
|
|
160
|
+
lines.push('');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Visible elements
|
|
164
|
+
const elements = uiContext.visibleElements || [];
|
|
165
|
+
|
|
166
|
+
if (elements.length === 0) {
|
|
167
|
+
lines.push('No visible elements detected.');
|
|
168
|
+
return lines.join('\n');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
lines.push(`Visible Elements (showing ${Math.min(elements.length, this.maxUIElements)} of ${elements.length}):`);
|
|
172
|
+
lines.push('');
|
|
173
|
+
|
|
174
|
+
// Filter and sort elements by relevance
|
|
175
|
+
const relevantElements = this.filterRelevantElements(elements);
|
|
176
|
+
const topElements = relevantElements.slice(0, this.maxUIElements);
|
|
177
|
+
|
|
178
|
+
topElements.forEach((element, index) => {
|
|
179
|
+
lines.push(this.formatElement(element, index + 1));
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return lines.join('\n');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Format a single UI element
|
|
187
|
+
*/
|
|
188
|
+
private formatElement(element: UIElement, index: number): string {
|
|
189
|
+
const parts: string[] = [`${index}.`];
|
|
190
|
+
|
|
191
|
+
// Element type
|
|
192
|
+
parts.push(`[${element.type}]`);
|
|
193
|
+
|
|
194
|
+
// Text content
|
|
195
|
+
if (element.text && element.text.trim().length > 0) {
|
|
196
|
+
parts.push(`text="${this.truncateText(element.text, 50)}"`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Resource ID (Android) or Name (iOS)
|
|
200
|
+
if (element.resourceId) {
|
|
201
|
+
const shortId = element.resourceId.split('/').pop() || element.resourceId;
|
|
202
|
+
parts.push(`id="${shortId}"`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Content description / accessibility label
|
|
206
|
+
if (element.contentDesc) {
|
|
207
|
+
parts.push(`desc="${this.truncateText(element.contentDesc, 30)}"`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Attributes
|
|
211
|
+
const attrs: string[] = [];
|
|
212
|
+
if (element.clickable) attrs.push('clickable');
|
|
213
|
+
if (element.enabled) attrs.push('enabled');
|
|
214
|
+
if (!element.enabled) attrs.push('disabled');
|
|
215
|
+
|
|
216
|
+
if (attrs.length > 0) {
|
|
217
|
+
parts.push(`[${attrs.join(', ')}]`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Bounds (if useful)
|
|
221
|
+
if (element.bounds) {
|
|
222
|
+
parts.push(`bounds=${element.bounds}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return parts.join(' ');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Filter elements to show most relevant ones
|
|
230
|
+
*/
|
|
231
|
+
private filterRelevantElements(elements: UIElement[]): UIElement[] {
|
|
232
|
+
// Prioritize elements that are:
|
|
233
|
+
// 1. Interactive (clickable, editable)
|
|
234
|
+
// 2. Have meaningful text or labels
|
|
235
|
+
// 3. Are common UI patterns (buttons, inputs, etc.)
|
|
236
|
+
|
|
237
|
+
const scored = elements.map(element => ({
|
|
238
|
+
element,
|
|
239
|
+
score: this.calculateElementRelevance(element),
|
|
240
|
+
}));
|
|
241
|
+
|
|
242
|
+
// Sort by score (descending)
|
|
243
|
+
scored.sort((a, b) => b.score - a.score);
|
|
244
|
+
|
|
245
|
+
return scored.map(s => s.element);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Calculate relevance score for an element
|
|
250
|
+
*/
|
|
251
|
+
private calculateElementRelevance(element: UIElement): number {
|
|
252
|
+
let score = 0;
|
|
253
|
+
|
|
254
|
+
// Interactive elements get high priority
|
|
255
|
+
if (element.clickable) score += 10;
|
|
256
|
+
|
|
257
|
+
// Elements with text are usually important
|
|
258
|
+
if (element.text && element.text.trim().length > 0) {
|
|
259
|
+
score += 8;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Elements with IDs are identifiable
|
|
263
|
+
if (element.resourceId) score += 5;
|
|
264
|
+
|
|
265
|
+
// Elements with accessibility labels
|
|
266
|
+
if (element.contentDesc) score += 5;
|
|
267
|
+
|
|
268
|
+
// Common interactive types
|
|
269
|
+
const interactiveTypes = [
|
|
270
|
+
'Button', 'EditText', 'TextView', 'ImageButton',
|
|
271
|
+
'CheckBox', 'RadioButton', 'Switch', 'Spinner',
|
|
272
|
+
'UIButton', 'UITextField', 'UITextView', 'UILabel',
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
if (interactiveTypes.some(type => element.type.includes(type))) {
|
|
276
|
+
score += 7;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Enabled elements
|
|
280
|
+
if (element.enabled) score += 3;
|
|
281
|
+
|
|
282
|
+
// Penalize common container types (unless they have text)
|
|
283
|
+
const containerTypes = ['ViewGroup', 'LinearLayout', 'FrameLayout', 'View', 'UIView'];
|
|
284
|
+
if (containerTypes.some(type => element.type.includes(type)) && !element.text) {
|
|
285
|
+
score -= 5;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return Math.max(0, score);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get output format instructions
|
|
293
|
+
*/
|
|
294
|
+
private getOutputFormatInstructions(): string {
|
|
295
|
+
return `**Output Format (JSON only, no markdown):**
|
|
296
|
+
{
|
|
297
|
+
"actions": [
|
|
298
|
+
{
|
|
299
|
+
"type": "tap|type|scroll|swipe|wait|assert|screenshot",
|
|
300
|
+
"selector": {
|
|
301
|
+
"strategy": "text|id|accessibility-id|xpath|class",
|
|
302
|
+
"value": "selector_value",
|
|
303
|
+
"index": 0
|
|
304
|
+
},
|
|
305
|
+
"value": "text_to_type (only for type action)",
|
|
306
|
+
"direction": "up|down|left|right (only for scroll/swipe)",
|
|
307
|
+
"duration": 1000,
|
|
308
|
+
"assertionType": "exists|visible|text|enabled (only for assert)",
|
|
309
|
+
"expectedValue": "expected_value (only for assert)"
|
|
310
|
+
}
|
|
311
|
+
],
|
|
312
|
+
"reasoning": "Brief explanation of the action sequence and why these selectors were chosen"
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
**Critical Rules:**
|
|
316
|
+
1. Return ONLY valid JSON, no markdown code blocks
|
|
317
|
+
2. Always prefer text-based selectors over XPath
|
|
318
|
+
3. Use accessibility-id or resource-id when available
|
|
319
|
+
4. Include reasoning for your selector choices
|
|
320
|
+
5. If no action is possible, return empty actions array with explanation
|
|
321
|
+
6. Keep action sequences minimal (1-3 actions preferred)
|
|
322
|
+
7. Add wait actions if UI transitions are expected
|
|
323
|
+
|
|
324
|
+
Generate the JSON action plan now:`;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Truncate text to max length
|
|
329
|
+
*/
|
|
330
|
+
private truncateText(text: string, maxLength: number): string {
|
|
331
|
+
if (text.length <= maxLength) {
|
|
332
|
+
return text;
|
|
333
|
+
}
|
|
334
|
+
return text.substring(0, maxLength - 3) + '...';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Estimate token count (rough approximation)
|
|
339
|
+
*/
|
|
340
|
+
private estimateTokens(text: string): number {
|
|
341
|
+
// Rough estimate: 1 token ≈ 4 characters
|
|
342
|
+
return Math.ceil(text.length / 4);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Build a few-shot learning prompt with examples
|
|
347
|
+
*/
|
|
348
|
+
buildFewShotPrompt(
|
|
349
|
+
stepText: string,
|
|
350
|
+
uiContext: UIContext,
|
|
351
|
+
examples: Array<{ step: string; actions: any; context?: string }>
|
|
352
|
+
): string {
|
|
353
|
+
const sections: string[] = [];
|
|
354
|
+
|
|
355
|
+
sections.push(PromptTemplates.actionPlanning);
|
|
356
|
+
sections.push('');
|
|
357
|
+
|
|
358
|
+
sections.push('**Example Scenarios:**');
|
|
359
|
+
sections.push('');
|
|
360
|
+
|
|
361
|
+
examples.forEach((example, index) => {
|
|
362
|
+
sections.push(`Example ${index + 1}:`);
|
|
363
|
+
sections.push(`Step: "${example.step}"`);
|
|
364
|
+
if (example.context) {
|
|
365
|
+
sections.push(`Context: ${example.context}`);
|
|
366
|
+
}
|
|
367
|
+
sections.push(`Actions: ${JSON.stringify(example.actions, null, 2)}`);
|
|
368
|
+
sections.push('');
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
sections.push('**Now, solve this:**');
|
|
372
|
+
sections.push('');
|
|
373
|
+
sections.push('**Current UI State:**');
|
|
374
|
+
sections.push(this.formatUIContext(uiContext));
|
|
375
|
+
sections.push('');
|
|
376
|
+
sections.push(`**Test Step:** "${stepText}"`);
|
|
377
|
+
sections.push('');
|
|
378
|
+
sections.push(this.getOutputFormatInstructions());
|
|
379
|
+
|
|
380
|
+
return sections.join('\n');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export default PromptBuilder;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Technician App Login Flow
|
|
2
|
+
description: Test the complete login flow with OTP verification
|
|
3
|
+
platform: Android
|
|
4
|
+
|
|
5
|
+
setup:
|
|
6
|
+
- step: Launch the DmgPro app
|
|
7
|
+
timeout: 10000
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- step: Click on 'Allow' if notifications dialog displayed
|
|
11
|
+
expectedOutcome: Get Started Screen should appear
|
|
12
|
+
timeout: 5000
|
|
13
|
+
|
|
14
|
+
- step: Click on 'Get Started' button
|
|
15
|
+
expectedOutcome: Login screen should appear
|
|
16
|
+
timeout: 5000
|
|
17
|
+
|
|
18
|
+
- step: Input the mobile number "112233445" into the Phone Number input field
|
|
19
|
+
expectedOutcome: OTP screen should appear
|
|
20
|
+
timeout: 5000
|
|
21
|
+
|
|
22
|
+
- step: Input OTP "1111"
|
|
23
|
+
expectedOutcome: Home screen should load
|
|
24
|
+
timeout: 5000
|
|
25
|
+
|
|
26
|
+
- step: Enable location access
|
|
27
|
+
expectedOutcome: Location permission granted
|
|
28
|
+
timeout: 5000
|
|
29
|
+
|
|
30
|
+
teardown:
|
|
31
|
+
- step: Close the app
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": [
|
|
6
|
+
"ES2020"
|
|
7
|
+
],
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"strict": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"moduleResolution": "node",
|
|
16
|
+
"declaration": true,
|
|
17
|
+
"declarationMap": true,
|
|
18
|
+
"sourceMap": true,
|
|
19
|
+
"types": [
|
|
20
|
+
"node"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"include": [
|
|
24
|
+
"src/**/*"
|
|
25
|
+
],
|
|
26
|
+
"exclude": [
|
|
27
|
+
"node_modules",
|
|
28
|
+
"dist",
|
|
29
|
+
"tests"
|
|
30
|
+
]
|
|
31
|
+
}
|