@eldrforge/ai-service 0.1.1
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/.github/dependabot.yml +12 -0
- package/.github/workflows/npm-publish.yml +48 -0
- package/.github/workflows/test.yml +33 -0
- package/LICENSE +190 -0
- package/README.md +48 -0
- package/dist/index.js +816 -0
- package/dist/instructions/commit.md +133 -0
- package/dist/instructions/release.md +188 -0
- package/dist/instructions/review.md +169 -0
- package/dist/personas/releaser.md +24 -0
- package/dist/personas/you.md +55 -0
- package/dist/src/ai.d.ts.map +1 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/interactive.d.ts.map +1 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/prompts/commit.d.ts.map +1 -0
- package/dist/src/prompts/index.d.ts.map +1 -0
- package/dist/src/prompts/release.d.ts.map +1 -0
- package/dist/src/prompts/review.d.ts.map +1 -0
- package/dist/src/types.d.ts.map +1 -0
- package/eslint.config.mjs +84 -0
- package/package.json +75 -0
- package/src/ai.ts +421 -0
- package/src/index.ts +14 -0
- package/src/interactive.ts +562 -0
- package/src/logger.ts +69 -0
- package/src/prompts/commit.ts +85 -0
- package/src/prompts/index.ts +28 -0
- package/src/prompts/instructions/commit.md +133 -0
- package/src/prompts/instructions/release.md +188 -0
- package/src/prompts/instructions/review.md +169 -0
- package/src/prompts/personas/releaser.md +24 -0
- package/src/prompts/personas/you.md +55 -0
- package/src/prompts/release.ts +118 -0
- package/src/prompts/review.ts +72 -0
- package/src/types.ts +112 -0
- package/tests/ai-complete-coverage.test.ts +241 -0
- package/tests/ai-create-completion.test.ts +288 -0
- package/tests/ai-edge-cases.test.ts +221 -0
- package/tests/ai-openai-error.test.ts +35 -0
- package/tests/ai-transcribe.test.ts +169 -0
- package/tests/ai.test.ts +139 -0
- package/tests/interactive-editor.test.ts +253 -0
- package/tests/interactive-secure-temp.test.ts +264 -0
- package/tests/interactive-user-choice.test.ts +173 -0
- package/tests/interactive-user-text.test.ts +174 -0
- package/tests/interactive.test.ts +94 -0
- package/tests/logger-noop.test.ts +40 -0
- package/tests/logger.test.ts +122 -0
- package/tests/prompts.test.ts +179 -0
- package/tsconfig.json +35 -0
- package/vite.config.ts +69 -0
- package/vitest.config.ts +25 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createNoOpLogger } from '../src/logger';
|
|
3
|
+
|
|
4
|
+
describe('createNoOpLogger', () => {
|
|
5
|
+
it('should create a logger with all required methods', () => {
|
|
6
|
+
const logger = createNoOpLogger();
|
|
7
|
+
|
|
8
|
+
expect(logger).toBeDefined();
|
|
9
|
+
expect(typeof logger.info).toBe('function');
|
|
10
|
+
expect(typeof logger.error).toBe('function');
|
|
11
|
+
expect(typeof logger.warn).toBe('function');
|
|
12
|
+
expect(typeof logger.debug).toBe('function');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should not throw when calling methods', () => {
|
|
16
|
+
const logger = createNoOpLogger();
|
|
17
|
+
|
|
18
|
+
expect(() => logger.info('test')).not.toThrow();
|
|
19
|
+
expect(() => logger.error('test')).not.toThrow();
|
|
20
|
+
expect(() => logger.warn('test')).not.toThrow();
|
|
21
|
+
expect(() => logger.debug('test')).not.toThrow();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should handle arguments', () => {
|
|
25
|
+
const logger = createNoOpLogger();
|
|
26
|
+
|
|
27
|
+
expect(() => logger.info('test', 'arg1', 'arg2')).not.toThrow();
|
|
28
|
+
expect(() => logger.error('test', { data: 'value' })).not.toThrow();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should return undefined from all methods', () => {
|
|
32
|
+
const logger = createNoOpLogger();
|
|
33
|
+
|
|
34
|
+
expect(logger.info('test')).toBeUndefined();
|
|
35
|
+
expect(logger.error('test')).toBeUndefined();
|
|
36
|
+
expect(logger.warn('test')).toBeUndefined();
|
|
37
|
+
expect(logger.debug('test')).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
2
|
+
import { getLogger, setLogger } from '../src/logger';
|
|
3
|
+
import type { Logger } from '../src/types';
|
|
4
|
+
|
|
5
|
+
describe('Logger', () => {
|
|
6
|
+
let originalLogger: any;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
// Clear module cache to reset logger state
|
|
10
|
+
vi.resetModules();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
if (originalLogger) {
|
|
15
|
+
setLogger(originalLogger);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should return a logger instance', () => {
|
|
20
|
+
const logger = getLogger();
|
|
21
|
+
|
|
22
|
+
expect(logger).toBeDefined();
|
|
23
|
+
expect(logger.info).toBeDefined();
|
|
24
|
+
expect(logger.error).toBeDefined();
|
|
25
|
+
expect(logger.warn).toBeDefined();
|
|
26
|
+
expect(logger.debug).toBeDefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should provide no-op logger by default when winston not available', () => {
|
|
30
|
+
const logger = getLogger();
|
|
31
|
+
|
|
32
|
+
// Should not throw
|
|
33
|
+
expect(() => {
|
|
34
|
+
logger.info('test');
|
|
35
|
+
logger.error('test');
|
|
36
|
+
logger.warn('test');
|
|
37
|
+
logger.debug('test');
|
|
38
|
+
}).not.toThrow();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should use custom logger when set', () => {
|
|
42
|
+
const mockLogger: Logger = {
|
|
43
|
+
info: vi.fn(),
|
|
44
|
+
error: vi.fn(),
|
|
45
|
+
warn: vi.fn(),
|
|
46
|
+
debug: vi.fn(),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
setLogger(mockLogger);
|
|
50
|
+
const logger = getLogger();
|
|
51
|
+
|
|
52
|
+
logger.info('test message', 'extra arg');
|
|
53
|
+
logger.error('error message');
|
|
54
|
+
logger.warn('warn message');
|
|
55
|
+
logger.debug('debug message');
|
|
56
|
+
|
|
57
|
+
expect(mockLogger.info).toHaveBeenCalledWith('test message', 'extra arg');
|
|
58
|
+
expect(mockLogger.error).toHaveBeenCalledWith('error message');
|
|
59
|
+
expect(mockLogger.warn).toHaveBeenCalledWith('warn message');
|
|
60
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('debug message');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should persist custom logger across getLogger calls', () => {
|
|
64
|
+
const mockLogger: Logger = {
|
|
65
|
+
info: vi.fn(),
|
|
66
|
+
error: vi.fn(),
|
|
67
|
+
warn: vi.fn(),
|
|
68
|
+
debug: vi.fn(),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
setLogger(mockLogger);
|
|
72
|
+
|
|
73
|
+
const logger1 = getLogger();
|
|
74
|
+
const logger2 = getLogger();
|
|
75
|
+
|
|
76
|
+
logger1.info('message 1');
|
|
77
|
+
logger2.info('message 2');
|
|
78
|
+
|
|
79
|
+
expect(mockLogger.info).toHaveBeenCalledTimes(2);
|
|
80
|
+
expect(mockLogger.info).toHaveBeenNthCalledWith(1, 'message 1');
|
|
81
|
+
expect(mockLogger.info).toHaveBeenNthCalledWith(2, 'message 2');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should handle logger with metadata', () => {
|
|
85
|
+
const mockLogger: Logger = {
|
|
86
|
+
info: vi.fn(),
|
|
87
|
+
error: vi.fn(),
|
|
88
|
+
warn: vi.fn(),
|
|
89
|
+
debug: vi.fn(),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
setLogger(mockLogger);
|
|
93
|
+
const logger = getLogger();
|
|
94
|
+
|
|
95
|
+
const metadata = { user: 'test', action: 'login' };
|
|
96
|
+
logger.info('User action', metadata);
|
|
97
|
+
|
|
98
|
+
expect(mockLogger.info).toHaveBeenCalledWith('User action', metadata);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should support all log levels', () => {
|
|
102
|
+
const mockLogger: Logger = {
|
|
103
|
+
info: vi.fn(),
|
|
104
|
+
error: vi.fn(),
|
|
105
|
+
warn: vi.fn(),
|
|
106
|
+
debug: vi.fn(),
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
setLogger(mockLogger);
|
|
110
|
+
const logger = getLogger();
|
|
111
|
+
|
|
112
|
+
logger.info('info level');
|
|
113
|
+
logger.error('error level');
|
|
114
|
+
logger.warn('warn level');
|
|
115
|
+
logger.debug('debug level');
|
|
116
|
+
|
|
117
|
+
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
118
|
+
expect(mockLogger.error).toHaveBeenCalledTimes(1);
|
|
119
|
+
expect(mockLogger.warn).toHaveBeenCalledTimes(1);
|
|
120
|
+
expect(mockLogger.debug).toHaveBeenCalledTimes(1);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
createCommitPrompt,
|
|
4
|
+
createReleasePrompt,
|
|
5
|
+
createReviewPrompt,
|
|
6
|
+
} from '../src/prompts';
|
|
7
|
+
|
|
8
|
+
describe('Prompt Builders', () => {
|
|
9
|
+
describe('createCommitPrompt', () => {
|
|
10
|
+
it('should create a prompt with diff content', async () => {
|
|
11
|
+
const prompt = await createCommitPrompt(
|
|
12
|
+
{ overrides: false },
|
|
13
|
+
{
|
|
14
|
+
diffContent: 'diff --git a/file.ts b/file.ts\n+new line',
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(prompt).toBeDefined();
|
|
19
|
+
// RiotPrompt returns a Prompt object, not necessarily with messages property
|
|
20
|
+
expect(typeof prompt).toBe('object');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should include user direction in prompt', async () => {
|
|
24
|
+
const prompt = await createCommitPrompt(
|
|
25
|
+
{ overrides: false },
|
|
26
|
+
{
|
|
27
|
+
diffContent: 'diff --git a/file.ts b/file.ts\n+new line',
|
|
28
|
+
userDirection: 'Focus on performance improvements',
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const promptText = JSON.stringify(prompt);
|
|
33
|
+
expect(promptText.toLowerCase()).toContain('performance');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle file content mode', async () => {
|
|
37
|
+
const prompt = await createCommitPrompt(
|
|
38
|
+
{ overrides: false },
|
|
39
|
+
{
|
|
40
|
+
diffContent: 'function test() { return true; }',
|
|
41
|
+
isFileContent: true,
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(prompt).toBeDefined();
|
|
46
|
+
expect(typeof prompt).toBe('object');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should include optional context', async () => {
|
|
50
|
+
const prompt = await createCommitPrompt(
|
|
51
|
+
{ overrides: false },
|
|
52
|
+
{
|
|
53
|
+
diffContent: 'diff content',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
logContext: 'recent commits',
|
|
57
|
+
context: 'working on auth',
|
|
58
|
+
directories: ['src/'],
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
expect(prompt).toBeDefined();
|
|
63
|
+
expect(typeof prompt).toBe('object');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('createReleasePrompt', () => {
|
|
68
|
+
it('should create a prompt with log and diff content', async () => {
|
|
69
|
+
const result = await createReleasePrompt(
|
|
70
|
+
{ overrides: false },
|
|
71
|
+
{
|
|
72
|
+
logContent: 'commit 1\ncommit 2\ncommit 3',
|
|
73
|
+
diffContent: 'diff content',
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(result).toBeDefined();
|
|
78
|
+
expect(result.prompt).toBeDefined();
|
|
79
|
+
expect(typeof result.prompt).toBe('object');
|
|
80
|
+
expect(result.maxTokens).toBeDefined();
|
|
81
|
+
expect(result.isLargeRelease).toBeDefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should detect normal sized releases', async () => {
|
|
85
|
+
const result = await createReleasePrompt(
|
|
86
|
+
{ overrides: false },
|
|
87
|
+
{
|
|
88
|
+
logContent: 'commit 1\ncommit 2',
|
|
89
|
+
diffContent: 'small diff',
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(result.isLargeRelease).toBe(false);
|
|
94
|
+
expect(result.maxTokens).toBe(10000);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should detect large releases by log line count', async () => {
|
|
98
|
+
// Create log with >60 lines
|
|
99
|
+
const largeLog = Array(70).fill('commit line').join('\n');
|
|
100
|
+
|
|
101
|
+
const result = await createReleasePrompt(
|
|
102
|
+
{ overrides: false },
|
|
103
|
+
{
|
|
104
|
+
logContent: largeLog,
|
|
105
|
+
diffContent: 'diff content',
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(result.isLargeRelease).toBe(true);
|
|
110
|
+
expect(result.maxTokens).toBe(25000);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should detect large releases by diff size', async () => {
|
|
114
|
+
// Create diff with >500 lines
|
|
115
|
+
const largeDiff = Array(600).fill('diff line').join('\n');
|
|
116
|
+
|
|
117
|
+
const result = await createReleasePrompt(
|
|
118
|
+
{ overrides: false },
|
|
119
|
+
{
|
|
120
|
+
logContent: 'small log',
|
|
121
|
+
diffContent: largeDiff,
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
expect(result.isLargeRelease).toBe(true);
|
|
126
|
+
expect(result.maxTokens).toBe(25000);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should include release focus and milestone issues', async () => {
|
|
130
|
+
const result = await createReleasePrompt(
|
|
131
|
+
{ overrides: false },
|
|
132
|
+
{
|
|
133
|
+
logContent: 'commits',
|
|
134
|
+
diffContent: 'diff',
|
|
135
|
+
releaseFocus: 'Bug fixes and performance',
|
|
136
|
+
milestoneIssues: 'Issue #1\nIssue #2',
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
expect(result.prompt).toBeDefined();
|
|
141
|
+
expect(typeof result.prompt).toBe('object');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('createReviewPrompt', () => {
|
|
146
|
+
it('should create a prompt with review notes', async () => {
|
|
147
|
+
const prompt = await createReviewPrompt(
|
|
148
|
+
{ overrides: false },
|
|
149
|
+
{
|
|
150
|
+
notes: 'Review notes about the project',
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
expect(prompt).toBeDefined();
|
|
155
|
+
expect(typeof prompt).toBe('object');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should include optional context', async () => {
|
|
159
|
+
const prompt = await createReviewPrompt(
|
|
160
|
+
{ overrides: false },
|
|
161
|
+
{
|
|
162
|
+
notes: 'Review notes',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
logContext: 'recent commits',
|
|
166
|
+
diffContext: 'current diff',
|
|
167
|
+
releaseNotesContext: 'recent release',
|
|
168
|
+
issuesContext: 'existing issues',
|
|
169
|
+
context: 'additional context',
|
|
170
|
+
directories: ['src/'],
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
expect(prompt).toBeDefined();
|
|
175
|
+
expect(typeof prompt).toBe('object');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": ".",
|
|
11
|
+
"types": [
|
|
12
|
+
"node",
|
|
13
|
+
"vitest"
|
|
14
|
+
],
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"allowJs": true,
|
|
17
|
+
"resolveJsonModule": true,
|
|
18
|
+
"declaration": true,
|
|
19
|
+
"declarationMap": true,
|
|
20
|
+
"baseUrl": ".",
|
|
21
|
+
"paths": {
|
|
22
|
+
"*": [
|
|
23
|
+
"src/*"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"include": [
|
|
28
|
+
"src/**/*",
|
|
29
|
+
"tests/**/*"
|
|
30
|
+
],
|
|
31
|
+
"exclude": [
|
|
32
|
+
"node_modules"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import dts from 'vite-plugin-dts';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [
|
|
9
|
+
dts({
|
|
10
|
+
include: ['src/**/*'],
|
|
11
|
+
exclude: ['src/**/*.test.ts', 'tests/**/*', 'src/**/*.md'],
|
|
12
|
+
outDir: 'dist',
|
|
13
|
+
insertTypesEntry: true,
|
|
14
|
+
}),
|
|
15
|
+
// Custom plugin to copy markdown files
|
|
16
|
+
{
|
|
17
|
+
name: 'copy-markdown',
|
|
18
|
+
writeBundle() {
|
|
19
|
+
// Copy markdown files preserving directory structure
|
|
20
|
+
const copyDir = (src: string, dest: string) => {
|
|
21
|
+
if (!fs.existsSync(dest)) {
|
|
22
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
26
|
+
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
const srcPath = path.join(src, entry.name);
|
|
29
|
+
const destPath = path.join(dest, entry.name);
|
|
30
|
+
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
copyDir(srcPath, destPath);
|
|
33
|
+
} else if (entry.name.endsWith('.md')) {
|
|
34
|
+
fs.copyFileSync(srcPath, destPath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Copy prompts markdown files to dist root
|
|
40
|
+
// (because bundled code in dist/index.js uses __dirname which resolves to dist/)
|
|
41
|
+
copyDir('src/prompts', 'dist');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
build: {
|
|
46
|
+
lib: {
|
|
47
|
+
entry: resolve(__dirname, 'src/index.ts'),
|
|
48
|
+
formats: ['es'],
|
|
49
|
+
fileName: 'index',
|
|
50
|
+
},
|
|
51
|
+
rollupOptions: {
|
|
52
|
+
external: [
|
|
53
|
+
'openai',
|
|
54
|
+
'@riotprompt/riotprompt',
|
|
55
|
+
'@eldrforge/git-tools',
|
|
56
|
+
'winston',
|
|
57
|
+
'fs',
|
|
58
|
+
'fs/promises',
|
|
59
|
+
'path',
|
|
60
|
+
'child_process',
|
|
61
|
+
'os',
|
|
62
|
+
'crypto',
|
|
63
|
+
'url',
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
sourcemap: true,
|
|
67
|
+
minify: false,
|
|
68
|
+
},
|
|
69
|
+
});
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
environment: 'node',
|
|
7
|
+
coverage: {
|
|
8
|
+
provider: 'v8',
|
|
9
|
+
reporter: ['text', 'html', 'lcov'],
|
|
10
|
+
include: ['src/**/*.ts'],
|
|
11
|
+
exclude: [
|
|
12
|
+
'src/**/*.test.ts',
|
|
13
|
+
'tests/**/*',
|
|
14
|
+
'src/types.ts',
|
|
15
|
+
'src/**/index.ts', // Index files are just exports
|
|
16
|
+
],
|
|
17
|
+
thresholds: {
|
|
18
|
+
lines: 90,
|
|
19
|
+
functions: 90,
|
|
20
|
+
branches: 90,
|
|
21
|
+
statements: 90,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|