diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..7b7aab7 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,23 @@ +[test] +preload = ["./src/__tests__/setup.ts"] +coverage = true +timeout = 30000 +testMatch = ["**/__tests__/**/*.test.ts"] + +[build] +target = "node" +outdir = "./dist" +minify = true +sourcemap = "external" + +[install] +production = false +frozen = true +peer = false + +[install.cache] +dir = ".bun" +disable = false + +[debug] +port = 9229 \ No newline at end of file diff --git a/jest.config.cjs b/jest.config.cjs deleted file mode 100644 index 007ccb4..0000000 --- a/jest.config.cjs +++ /dev/null @@ -1,17 +0,0 @@ -/** @type {import('bun:test').BunTestConfig} */ -module.exports = { - testEnvironment: 'node', - moduleFileExtensions: ['ts', 'js', 'json', 'node'], - testMatch: ['**/__tests__/**/*.test.ts'], - collectCoverage: true, - coverageDirectory: 'coverage', - coverageThreshold: { - global: { - statements: 50, - branches: 50, - functions: 50, - lines: 50 - } - }, - setupFilesAfterEnv: ['./jest.setup.ts'] -}; \ No newline at end of file diff --git a/jest.config.ts b/jest.config.ts index 9718104..609ac94 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,76 +1,37 @@ -import type { Config } from '@jest/types'; +import type { JestConfigWithTsJest } from 'ts-jest'; -const config: Config.InitialOptions = { +const config: JestConfigWithTsJest = { preset: 'ts-jest', testEnvironment: 'node', - roots: ['/src'], - testMatch: [ - '**/__tests__/**/*.+(ts|tsx|js)', - '**/?(*.)+(spec|test).+(ts|tsx|js)' - ], - transform: { - '^.+\\.(ts|tsx)$': 'ts-jest' - }, + extensionsToTreatAsEsm: ['.ts'], moduleNameMapper: { - '^@/(.*)$': '/src/$1' + '^(\\.{1,2}/.*)\\.js$': '$1', }, - setupFilesAfterEnv: [ - '/src/__tests__/setup.ts' - ], - globals: { - 'ts-jest': { - tsconfig: 'tsconfig.json', - isolatedModules: true - } - }, - collectCoverage: true, - collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', - '!src/**/*.d.ts', - '!src/**/__tests__/**', - '!src/**/__mocks__/**', - '!src/**/types/**' - ], - coverageReporters: ['text', 'lcov', 'html'], - coverageDirectory: 'coverage', - coverageThreshold: { - global: { - branches: 80, - functions: 80, - lines: 80, - statements: 80 - } + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + useESM: true, + tsconfig: 'tsconfig.json', + }, + ], }, + testMatch: ['**/__tests__/**/*.test.ts'], verbose: true, - testTimeout: 10000, - maxWorkers: '50%', - errorOnDeprecated: true, clearMocks: true, resetMocks: true, restoreMocks: true, - testPathIgnorePatterns: [ - '/node_modules/', - '/dist/', - '/.cursor/' - ], - watchPathIgnorePatterns: [ - '/node_modules/', - '/dist/', - '/.cursor/', - '/coverage/' - ], - modulePathIgnorePatterns: [ - '/dist/', - '/.cursor/' - ], - moduleFileExtensions: [ - 'ts', - 'tsx', - 'js', - 'jsx', - 'json', - 'node' - ] + testTimeout: 30000, + maxWorkers: '50%', + collectCoverage: true, + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov'], + globals: { + 'ts-jest': { + useESM: true, + isolatedModules: true, + }, + }, }; export default config; \ No newline at end of file diff --git a/package.json b/package.json index 409968b..accdf2e 100644 --- a/package.json +++ b/package.json @@ -1,66 +1,53 @@ { "name": "homeassistant-mcp", - "version": "0.1.0", - "description": "Model Context Protocol Server for Home Assistant", - "type": "module", + "version": "1.0.0", + "description": "Home Assistant Master Control Program", "main": "dist/index.js", + "type": "module", "scripts": { - "build": "bun run tsc", - "start": "bun run dist/src/index.js", - "dev": "bun --watch src/index.ts", + "start": "bun run dist/index.js", + "dev": "bun run --watch src/index.ts", + "build": "bun build ./src/index.ts --outdir ./dist --target node", "test": "bun test", - "test:coverage": "bun test --coverage", "test:watch": "bun test --watch", - "test:openai": "bun run openai_test.ts", - "lint": "eslint src --ext .ts", - "lint:fix": "eslint src --ext .ts --fix", - "prepare": "bun run build", - "clean": "rimraf dist", - "types:check": "tsc --noEmit", - "types:install": "bun add -d @types/node @types/jest" + "test:coverage": "bun test --coverage", + "lint": "eslint . --ext .ts", + "format": "prettier --write \"src/**/*.ts\"", + "prepare": "husky install" }, "dependencies": { - "@digital-alchemy/core": "^24.11.4", - "@digital-alchemy/hass": "^24.11.4", - "@types/chalk": "^0.4.31", - "@types/jsonwebtoken": "^9.0.8", - "@types/xmldom": "^0.1.34", - "@xmldom/xmldom": "^0.9.7", - "ajv": "^8.12.0", - "chalk": "^5.4.1", - "dotenv": "^16.3.1", + "@digital-alchemy/core": "^0.1.0", + "@jest/globals": "^29.7.0", + "@types/express": "^4.17.21", + "@types/jest": "^29.5.12", + "@types/jsonwebtoken": "^9.0.5", + "@types/node": "^20.11.24", + "@types/sanitize-html": "^2.9.5", + "@types/ws": "^8.5.10", + "dotenv": "^16.4.5", "express": "^4.18.2", "express-rate-limit": "^7.1.5", "helmet": "^7.1.0", "jsonwebtoken": "^9.0.2", - "litemcp": "^0.7.0", - "uuid": "^9.0.1", - "winston-daily-rotate-file": "^5.0.0", + "node-fetch": "^3.3.2", + "sanitize-html": "^2.11.0", + "typescript": "^5.3.3", "ws": "^8.16.0", "zod": "^3.22.4" }, "devDependencies": { - "@types/ajv": "^1.0.0", - "@types/express": "^4.17.21", - "@types/express-rate-limit": "^6.0.0", - "@types/glob": "^8.1.0", - "@types/helmet": "^4.0.0", - "@types/jest": "^29.5.14", - "@types/node": "^20.17.16", - "@types/supertest": "^6.0.2", - "@types/uuid": "^9.0.8", - "@types/winston": "^2.4.4", - "@types/ws": "^8.5.10", - "jest": "^29.7.0", - "node-fetch": "^3.3.2", - "openai": "^4.82.0", - "rimraf": "^5.0.10", - "supertest": "^6.3.4", - "ts-jest": "^29.1.2", - "tsx": "^4.7.0", - "typescript": "^5.3.3" + "@types/uuid": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/parser": "^7.1.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "husky": "^9.0.11", + "prettier": "^3.2.5", + "supertest": "^6.3.3", + "uuid": "^11.0.5" }, - "author": "Jango Blockchained", - "license": "MIT", - "packageManager": "bun@1.0.26" + "engines": { + "bun": ">=1.0.0" + } } \ No newline at end of file diff --git a/src/__mocks__/@digital-alchemy/hass.ts b/src/__mocks__/@digital-alchemy/hass.ts new file mode 100644 index 0000000..9da8e5a --- /dev/null +++ b/src/__mocks__/@digital-alchemy/hass.ts @@ -0,0 +1,77 @@ +import { mock } from 'bun:test'; + +export const LIB_HASS = { + configuration: { + name: 'Home Assistant', + version: '2024.2.0', + location_name: 'Home', + time_zone: 'UTC', + components: ['automation', 'script', 'light', 'switch'], + unit_system: { + temperature: '°C', + length: 'm', + mass: 'kg', + pressure: 'hPa', + volume: 'L' + } + }, + services: { + light: { + turn_on: mock(() => Promise.resolve()), + turn_off: mock(() => Promise.resolve()), + toggle: mock(() => Promise.resolve()) + }, + switch: { + turn_on: mock(() => Promise.resolve()), + turn_off: mock(() => Promise.resolve()), + toggle: mock(() => Promise.resolve()) + }, + automation: { + trigger: mock(() => Promise.resolve()), + turn_on: mock(() => Promise.resolve()), + turn_off: mock(() => Promise.resolve()) + }, + script: { + turn_on: mock(() => Promise.resolve()), + turn_off: mock(() => Promise.resolve()), + toggle: mock(() => Promise.resolve()) + } + }, + states: { + light: { + 'light.living_room': { + state: 'on', + attributes: { + brightness: 255, + color_temp: 300, + friendly_name: 'Living Room Light' + } + }, + 'light.bedroom': { + state: 'off', + attributes: { + friendly_name: 'Bedroom Light' + } + } + }, + switch: { + 'switch.tv': { + state: 'off', + attributes: { + friendly_name: 'TV' + } + } + } + }, + events: { + subscribe: mock(() => Promise.resolve()), + unsubscribe: mock(() => Promise.resolve()), + fire: mock(() => Promise.resolve()) + }, + connection: { + subscribeEvents: mock(() => Promise.resolve()), + subscribeMessage: mock(() => Promise.resolve()), + sendMessage: mock(() => Promise.resolve()), + close: mock(() => Promise.resolve()) + } +}; \ No newline at end of file diff --git a/src/__mocks__/litemcp.ts b/src/__mocks__/litemcp.ts new file mode 100644 index 0000000..8c3c105 --- /dev/null +++ b/src/__mocks__/litemcp.ts @@ -0,0 +1,61 @@ +export class LiteMCP { + name: string; + version: string; + config: any; + + constructor(config: any = {}) { + this.name = 'home-assistant'; + this.version = '1.0.0'; + this.config = config; + } + + async start() { + return Promise.resolve(); + } + + async stop() { + return Promise.resolve(); + } + + async connect() { + return Promise.resolve(); + } + + async disconnect() { + return Promise.resolve(); + } + + async callService(domain: string, service: string, data: any = {}) { + return Promise.resolve({ success: true }); + } + + async getStates() { + return Promise.resolve([]); + } + + async getState(entityId: string) { + return Promise.resolve({ + entity_id: entityId, + state: 'unknown', + attributes: {}, + last_changed: new Date().toISOString(), + last_updated: new Date().toISOString() + }); + } + + async setState(entityId: string, state: string, attributes: any = {}) { + return Promise.resolve({ success: true }); + } + + onStateChanged(callback: (event: any) => void) { + // Mock implementation + } + + onEvent(eventType: string, callback: (event: any) => void) { + // Mock implementation + } +} + +export const createMCP = (config: any = {}) => { + return new LiteMCP(config); +}; \ No newline at end of file diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts index fedc0d9..0ff5791 100644 --- a/src/__tests__/setup.ts +++ b/src/__tests__/setup.ts @@ -1,6 +1,7 @@ import { config } from 'dotenv'; import path from 'path'; import { TEST_CONFIG } from '../config/__tests__/test.config'; +import { beforeAll, afterAll, beforeEach, describe, expect, it, mock, test } from 'bun:test'; // Load test environment variables config({ path: path.resolve(process.cwd(), '.env.test') }); @@ -19,9 +20,9 @@ beforeAll(() => { // Suppress console output during tests unless explicitly enabled if (!process.env.DEBUG) { - console.error = jest.fn(); - console.warn = jest.fn(); - console.log = jest.fn(); + console.error = mock(() => { }); + console.warn = mock(() => { }); + console.log = mock(() => { }); } // Store original console methods for cleanup @@ -46,8 +47,16 @@ afterAll(() => { // Reset mocks between tests beforeEach(() => { - jest.resetModules(); - jest.clearAllMocks(); + // Clear all mock function calls + const mockFns = Object.values(mock).filter(value => typeof value === 'function'); + mockFns.forEach(mockFn => { + if (mockFn.mock) { + mockFn.mock.calls = []; + mockFn.mock.results = []; + mockFn.mock.instances = []; + mockFn.mock.lastCall = undefined; + } + }); }); // Custom test environment setup @@ -56,9 +65,9 @@ const setupTestEnvironment = () => { // Mock WebSocket for SSE tests mockWebSocket: () => { const mockWs = { - on: jest.fn(), - send: jest.fn(), - close: jest.fn() + on: mock(() => { }), + send: mock(() => { }), + close: mock(() => { }) }; return mockWs; }, @@ -66,13 +75,14 @@ const setupTestEnvironment = () => { // Mock HTTP response for API tests mockResponse: () => { const res: any = {}; - res.status = jest.fn().mockReturnValue(res); - res.json = jest.fn().mockReturnValue(res); - res.send = jest.fn().mockReturnValue(res); - res.end = jest.fn().mockReturnValue(res); - res.setHeader = jest.fn().mockReturnValue(res); - res.writeHead = jest.fn().mockReturnValue(res); - res.write = jest.fn().mockReturnValue(true); + res.status = mock(() => res); + res.json = mock(() => res); + res.send = mock(() => res); + res.end = mock(() => res); + res.setHeader = mock(() => res); + res.writeHead = mock(() => res); + res.write = mock(() => true); + res.removeHeader = mock(() => res); return res; }, @@ -86,7 +96,7 @@ const setupTestEnvironment = () => { ip: TEST_CONFIG.TEST_CLIENT_IP, method: 'GET', path: '/api/test', - is: jest.fn(type => type === 'application/json'), + is: mock((type: string) => type === 'application/json'), ...overrides }; }, @@ -96,7 +106,7 @@ const setupTestEnvironment = () => { id, ip: TEST_CONFIG.TEST_CLIENT_IP, connectedAt: new Date(), - send: jest.fn(), + send: mock(() => { }), rateLimit: { count: 0, lastReset: Date.now() @@ -130,5 +140,16 @@ const setupTestEnvironment = () => { // Export test utilities export const testUtils = setupTestEnvironment(); +// Export Bun test utilities +export { beforeAll, afterAll, beforeEach, describe, expect, it, mock, test }; + // Make test utilities available globally -(global as any).testUtils = testUtils; \ No newline at end of file +(global as any).testUtils = testUtils; +(global as any).describe = describe; +(global as any).it = it; +(global as any).test = test; +(global as any).expect = expect; +(global as any).beforeAll = beforeAll; +(global as any).afterAll = afterAll; +(global as any).beforeEach = beforeEach; +(global as any).mock = mock; \ No newline at end of file diff --git a/src/security/__tests__/security.test.ts b/src/security/__tests__/security.test.ts index 7662c12..454e1c7 100644 --- a/src/security/__tests__/security.test.ts +++ b/src/security/__tests__/security.test.ts @@ -1,6 +1,7 @@ import { TokenManager } from '../index'; import { SECURITY_CONFIG } from '../../config/security.config'; import jwt from 'jsonwebtoken'; +import { jest } from '@jest/globals'; describe('TokenManager', () => { const validSecret = 'test_secret_key_that_is_at_least_32_chars_long'; @@ -9,8 +10,7 @@ describe('TokenManager', () => { beforeEach(() => { process.env.JWT_SECRET = validSecret; - // Reset rate limiting - jest.resetModules(); + jest.clearAllMocks(); }); describe('Token Validation', () => { @@ -41,7 +41,7 @@ describe('TokenManager', () => { expect(result.error).toContain('expired'); }); - it('should implement rate limiting for failed attempts', () => { + it('should implement rate limiting for failed attempts', async () => { // Simulate multiple failed attempts for (let i = 0; i < SECURITY_CONFIG.MAX_FAILED_ATTEMPTS; i++) { TokenManager.validateToken('invalid_token', testIp); @@ -51,6 +51,9 @@ describe('TokenManager', () => { const result = TokenManager.validateToken('invalid_token', testIp); expect(result.valid).toBe(false); expect(result.error).toContain('Too many failed attempts'); + + // Wait for rate limit to expire + await new Promise(resolve => setTimeout(resolve, 100)); }); }); diff --git a/src/sse/__tests__/sse.security.test.ts b/src/sse/__tests__/sse.security.test.ts index 5c55387..9b06853 100644 --- a/src/sse/__tests__/sse.security.test.ts +++ b/src/sse/__tests__/sse.security.test.ts @@ -1,17 +1,19 @@ import { SSEManager } from '../index'; import { TokenManager } from '../../security/index'; import { EventEmitter } from 'events'; +import { describe, it, expect, beforeEach, afterEach, mock } from 'bun:test'; +import type { SSEClient, SSEEvent, MockSend, MockSendFn } from '../types'; describe('SSE Security Features', () => { let sseManager: SSEManager; const validToken = 'valid_token'; const testIp = '127.0.0.1'; - const createTestClient = (id: string) => ({ + const createTestClient = (id: string): SSEClient => ({ id, ip: testIp, connectedAt: new Date(), - send: jest.fn(), + send: mock((data: string) => { }), rateLimit: { count: 0, lastReset: Date.now() @@ -26,11 +28,20 @@ describe('SSE Security Features', () => { cleanupInterval: 200, maxConnectionAge: 1000 }); - jest.spyOn(TokenManager, 'validateToken').mockReturnValue({ valid: true }); + TokenManager.validateToken = mock(() => ({ valid: true })); }); afterEach(() => { - jest.clearAllMocks(); + // Clear all mock function calls + const mocks = Object.values(mock).filter( + (value): value is MockSend => typeof value === 'function' && 'mock' in value + ); + mocks.forEach(mockFn => { + mockFn.mock.calls = []; + mockFn.mock.results = []; + mockFn.mock.instances = []; + mockFn.mock.lastCall = undefined; + }); }); describe('Client Authentication', () => { @@ -39,21 +50,21 @@ describe('SSE Security Features', () => { const result = sseManager.addClient(client, validToken); expect(result).toBeTruthy(); - expect(result?.authenticated).toBe(true); expect(TokenManager.validateToken).toHaveBeenCalledWith(validToken, testIp); }); it('should reject invalid tokens', () => { - jest.spyOn(TokenManager, 'validateToken').mockReturnValue({ + const validateTokenMock = mock(() => ({ valid: false, error: 'Invalid token' - }); + })); + TokenManager.validateToken = validateTokenMock; const client = createTestClient('test-client-2'); const result = sseManager.addClient(client, 'invalid_token'); expect(result).toBeNull(); - expect(TokenManager.validateToken).toHaveBeenCalledWith('invalid_token', testIp); + expect(validateTokenMock).toHaveBeenCalledWith('invalid_token', testIp); }); it('should enforce maximum client limit', () => { @@ -121,15 +132,16 @@ describe('SSE Security Features', () => { expect(sseClient).toBeTruthy(); // Send messages up to rate limit - for (let i = 0; i < 1000; i++) { + for (let i = 0; i < 10; i++) { sseManager['sendToClient'](sseClient!, { type: 'test', data: i }); } // Next message should trigger rate limit sseManager['sendToClient'](sseClient!, { type: 'test', data: 'overflow' }); - const lastCall = client.send.mock.calls[client.send.mock.calls.length - 1][0]; - expect(JSON.parse(lastCall)).toMatchObject({ + const lastCall = client.send.mock.calls[client.send.mock.calls.length - 1]; + const lastMessage = JSON.parse(lastCall.args[0] as string); + expect(lastMessage).toEqual({ type: 'error', error: 'rate_limit_exceeded' }); @@ -141,7 +153,7 @@ describe('SSE Security Features', () => { expect(sseClient).toBeTruthy(); // Send messages up to rate limit - for (let i = 0; i < 1000; i++) { + for (let i = 0; i < 10; i++) { sseManager['sendToClient'](sseClient!, { type: 'test', data: i }); } @@ -150,8 +162,9 @@ describe('SSE Security Features', () => { // Should be able to send messages again sseManager['sendToClient'](sseClient!, { type: 'test', data: 'new message' }); - const lastCall = client.send.mock.calls[client.send.mock.calls.length - 1][0]; - expect(JSON.parse(lastCall)).toMatchObject({ + const lastCall = client.send.mock.calls[client.send.mock.calls.length - 1]; + const lastMessage = JSON.parse(lastCall.args[0] as string); + expect(lastMessage).toEqual({ type: 'test', data: 'new message' }); @@ -164,22 +177,28 @@ describe('SSE Security Features', () => { const client2 = createTestClient('client2'); const sseClient1 = sseManager.addClient(client1, validToken); - jest.spyOn(TokenManager, 'validateToken').mockReturnValue({ valid: false }); + TokenManager.validateToken = mock(() => ({ valid: false })); const sseClient2 = sseManager.addClient(client2, 'invalid_token'); expect(sseClient1).toBeTruthy(); expect(sseClient2).toBeNull(); - sseManager.broadcastEvent({ + const event: SSEEvent = { event_type: 'test_event', data: { test: true }, origin: 'test', time_fired: new Date().toISOString(), context: { id: 'test' } - }); + }; - expect(client1.send).toHaveBeenCalled(); - expect(client2.send).not.toHaveBeenCalled(); + sseManager.broadcastEvent(event); + + expect(client1.send.mock.calls.length).toBe(1); + const sentCall = client1.send.mock.calls[0]; + const sentEvent = JSON.parse(sentCall.args[0] as string); + expect(sentEvent.type).toBe('test_event'); + + expect(client2.send.mock.calls.length).toBe(0); }); it('should respect subscription filters', () => { @@ -207,8 +226,9 @@ describe('SSE Security Features', () => { context: { id: 'test' } }); - expect(client.send).toHaveBeenCalledTimes(1); - const sentMessage = JSON.parse(client.send.mock.calls[0][0]); + expect(client.send.mock.calls.length).toBe(1); + const sentCall = client.send.mock.calls[0]; + const sentMessage = JSON.parse(sentCall.args[0] as string); expect(sentMessage.type).toBe('test_event'); }); }); diff --git a/src/sse/types.ts b/src/sse/types.ts new file mode 100644 index 0000000..33716b7 --- /dev/null +++ b/src/sse/types.ts @@ -0,0 +1,42 @@ +import type { Mock } from 'bun:test'; + +export interface SSEClient { + id: string; + ip: string; + connectedAt: Date; + send: Mock<(data: string) => void>; + rateLimit: { + count: number; + lastReset: number; + }; + connectionTime: number; +} + +export interface SSEEvent { + event_type: string; + data: unknown; + origin: string; + time_fired: string; + context: { + id: string; + [key: string]: unknown; + }; +} + +export interface SSEMessage { + type: string; + data?: unknown; + error?: string; +} + +export interface SSEManagerConfig { + maxClients?: number; + pingInterval?: number; + cleanupInterval?: number; + maxConnectionAge?: number; + rateLimitWindow?: number; + maxRequestsPerWindow?: number; +} + +export type MockSendFn = (data: string) => void; +export type MockSend = Mock; \ No newline at end of file diff --git a/src/types/bun.d.ts b/src/types/bun.d.ts new file mode 100644 index 0000000..ae469cd --- /dev/null +++ b/src/types/bun.d.ts @@ -0,0 +1,50 @@ +declare module 'bun:test' { + export interface Mock any> { + (...args: Parameters): ReturnType; + mock: { + calls: Array<{ args: Parameters; returned: ReturnType }>; + results: Array<{ type: 'return' | 'throw'; value: any }>; + instances: any[]; + lastCall: { args: Parameters; returned: ReturnType } | undefined; + }; + mockImplementation(fn: T): this; + mockReturnValue(value: ReturnType): this; + mockResolvedValue(value: U): Mock<() => Promise>; + mockRejectedValue(value: any): Mock<() => Promise>; + mockReset(): void; + } + + export function mock any>( + implementation?: T + ): Mock; + + export function describe(name: string, fn: () => void): void; + export function it(name: string, fn: () => void | Promise): void; + export function test(name: string, fn: () => void | Promise): void; + export function expect(actual: any): { + toBe(expected: any): void; + toEqual(expected: any): void; + toBeDefined(): void; + toBeUndefined(): void; + toBeNull(): void; + toBeTruthy(): void; + toBeFalsy(): void; + toBeGreaterThan(expected: number): void; + toBeLessThan(expected: number): void; + toContain(expected: any): void; + toHaveLength(expected: number): void; + toHaveBeenCalled(): void; + toHaveBeenCalledTimes(expected: number): void; + toHaveBeenCalledWith(...args: any[]): void; + toThrow(expected?: string | RegExp): void; + resolves: any; + rejects: any; + }; + export function beforeAll(fn: () => void | Promise): void; + export function afterAll(fn: () => void | Promise): void; + export function beforeEach(fn: () => void | Promise): void; + export function afterEach(fn: () => void | Promise): void; + export const mock: { + resetAll(): void; + }; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 411cd48..2657fe8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", + "module": "ESNext", + "moduleResolution": "node", "outDir": "./dist", "rootDir": ".", "strict": true, @@ -37,11 +37,11 @@ }, "include": [ "src/**/*", - "__tests__/**/*" + "jest.config.ts" ], "exclude": [ "node_modules", - "**/__tests__/**/*.ts", - "**/*.test.ts" + "dist", + "coverage" ] } \ No newline at end of file