feat: typescript migration (#40)

* chore: initialize TypeScript configuration and build setup

- Add tsconfig.json for root and mcp-server with strict type checking
- Install typescript and @types/node as devDependencies
- Add npm build script for TypeScript compilation
- Update main entrypoint to compiled dist/shannon.js
- Update Dockerfile to build TypeScript before running
- Configure output directory and module resolution for Node.js

* refactor: migrate codebase from JavaScript to TypeScript

- Convert all 37 JavaScript files to TypeScript (.js -> .ts)
- Add type definitions in src/types/ for agents, config, errors, session
- Update mcp-server with proper TypeScript types
- Move entry point from shannon.mjs to src/shannon.ts
- Update tsconfig.json with rootDir: "./src" for cleaner dist output
- Update Dockerfile to build TypeScript before runtime
- Update package.json paths to use compiled dist/shannon.js

No runtime behavior changes - pure type safety migration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: update CLI references from ./shannon.mjs to shannon

- Update help text in src/cli/ui.ts
- Update usage examples in src/cli/command-handler.ts
- Update setup message in src/shannon.ts
- Update CLAUDE.md documentation with TypeScript file structure
- Replace all ./shannon.mjs references with shannon command

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove unnecessary eslint-disable comments

ESLint is not configured in this project, making these comments redundant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ezl-keygraph
2026-01-08 00:18:25 +05:30
committed by GitHub
parent b4d2c35b91
commit dd18f4629b
55 changed files with 3213 additions and 2057 deletions

View File

@@ -10,6 +10,10 @@
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.1.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/node": "^25.0.3",
"typescript": "^5.9.3"
}
},
"node_modules/@anthropic-ai/claude-agent-sdk": {
@@ -241,6 +245,37 @@
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@types/node": {
"version": "25.0.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",

View File

@@ -2,12 +2,17 @@
"name": "@shannon/mcp-server",
"version": "1.0.0",
"type": "module",
"main": "./src/index.js",
"main": "./dist/index.js",
"scripts": {
"build": "tsc",
"clean": "rm -rf dist"
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.1.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/node": "^25.0.3",
"typescript": "^5.9.3"
}
}

View File

@@ -17,13 +17,14 @@ import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk';
import { saveDeliverableTool } from './tools/save-deliverable.js';
import { generateTotpTool } from './tools/generate-totp.js';
declare global {
var __SHANNON_TARGET_DIR: string | undefined;
}
/**
* Create Shannon Helper MCP Server with target directory context
*
* @param {string} targetDir - The target repository directory where deliverables should be saved
* @returns {Object} MCP server instance
*/
export function createShannonHelperServer(targetDir) {
export function createShannonHelperServer(targetDir: string): ReturnType<typeof createSdkMcpServer> {
// Store target directory for tool access
global.__SHANNON_TARGET_DIR = targetDir;

View File

@@ -15,7 +15,7 @@
import { tool } from '@anthropic-ai/claude-agent-sdk';
import { createHmac } from 'crypto';
import { z } from 'zod';
import { createToolResult } from '../types/tool-responses.js';
import { createToolResult, type ToolResult, type GenerateTotpResponse } from '../types/tool-responses.js';
import { base32Decode, validateTotpSecret } from '../validation/totp-validator.js';
import { createCryptoError, createGenericError } from '../utils/error-formatter.js';
@@ -30,16 +30,13 @@ export const GenerateTotpInputSchema = z.object({
.describe('Base32-encoded TOTP secret'),
});
export type GenerateTotpInput = z.infer<typeof GenerateTotpInputSchema>;
/**
* Generate HOTP code (RFC 4226)
* Ported from generate-totp-standalone.mjs (lines 74-99)
*
* @param {string} secret - Base32-encoded secret
* @param {number} counter - Counter value
* @param {number} [digits=6] - Number of digits in OTP
* @returns {string} OTP code
*/
function generateHOTP(secret, counter, digits = 6) {
function generateHOTP(secret: string, counter: number, digits: number = 6): string {
const key = base32Decode(secret);
// Convert counter to 8-byte buffer (big-endian)
@@ -52,12 +49,12 @@ function generateHOTP(secret, counter, digits = 6) {
const hash = hmac.digest();
// Dynamic truncation
const offset = hash[hash.length - 1] & 0x0f;
const offset = hash[hash.length - 1]! & 0x0f;
const code =
((hash[offset] & 0x7f) << 24) |
((hash[offset + 1] & 0xff) << 16) |
((hash[offset + 2] & 0xff) << 8) |
(hash[offset + 3] & 0xff);
((hash[offset]! & 0x7f) << 24) |
((hash[offset + 1]! & 0xff) << 16) |
((hash[offset + 2]! & 0xff) << 8) |
(hash[offset + 3]! & 0xff);
// Generate digits
const otp = (code % Math.pow(10, digits)).toString().padStart(digits, '0');
@@ -67,13 +64,8 @@ function generateHOTP(secret, counter, digits = 6) {
/**
* Generate TOTP code (RFC 6238)
* Ported from generate-totp-standalone.mjs (lines 101-106)
*
* @param {string} secret - Base32-encoded secret
* @param {number} [timeStep=30] - Time step in seconds
* @param {number} [digits=6] - Number of digits in OTP
* @returns {string} OTP code
*/
function generateTOTP(secret, timeStep = 30, digits = 6) {
function generateTOTP(secret: string, timeStep: number = 30, digits: number = 6): string {
const currentTime = Math.floor(Date.now() / 1000);
const counter = Math.floor(currentTime / timeStep);
return generateHOTP(secret, counter, digits);
@@ -81,23 +73,16 @@ function generateTOTP(secret, timeStep = 30, digits = 6) {
/**
* Get seconds until TOTP code expires
*
* @param {number} [timeStep=30] - Time step in seconds
* @returns {number} Seconds until expiration
*/
function getSecondsUntilExpiration(timeStep = 30) {
function getSecondsUntilExpiration(timeStep: number = 30): number {
const currentTime = Math.floor(Date.now() / 1000);
return timeStep - (currentTime % timeStep);
}
/**
* generate_totp tool implementation
*
* @param {Object} args
* @param {string} args.secret - Base32-encoded TOTP secret
* @returns {Promise<Object>} Tool result
*/
export async function generateTotp(args) {
export async function generateTotp(args: GenerateTotpInput): Promise<ToolResult> {
try {
const { secret } = args;
@@ -110,7 +95,7 @@ export async function generateTotp(args) {
const timestamp = new Date().toISOString();
// Success response
const successResponse = {
const successResponse: GenerateTotpResponse = {
status: 'success',
message: 'TOTP code generated successfully',
totpCode,

View File

@@ -14,7 +14,7 @@
import { tool } from '@anthropic-ai/claude-agent-sdk';
import { z } from 'zod';
import { DeliverableType, DELIVERABLE_FILENAMES, isQueueType } from '../types/deliverables.js';
import { createToolResult } from '../types/tool-responses.js';
import { createToolResult, type ToolResult, type SaveDeliverableResponse } from '../types/tool-responses.js';
import { validateQueueJson } from '../validation/queue-validator.js';
import { saveDeliverableFile } from '../utils/file-operations.js';
import { createValidationError, createGenericError } from '../utils/error-formatter.js';
@@ -27,15 +27,12 @@ export const SaveDeliverableInputSchema = z.object({
content: z.string().min(1).describe('File content (markdown for analysis/evidence, JSON for queues)'),
});
export type SaveDeliverableInput = z.infer<typeof SaveDeliverableInputSchema>;
/**
* save_deliverable tool implementation
*
* @param {Object} args
* @param {string} args.deliverable_type - Type of deliverable to save
* @param {string} args.content - File content
* @returns {Promise<Object>} Tool result
*/
export async function saveDeliverable(args) {
export async function saveDeliverable(args: SaveDeliverableInput): Promise<ToolResult> {
try {
const { deliverable_type, content } = args;
@@ -44,7 +41,7 @@ export async function saveDeliverable(args) {
const queueValidation = validateQueueJson(content);
if (!queueValidation.valid) {
const errorResponse = createValidationError(
queueValidation.message,
queueValidation.message ?? 'Invalid queue JSON',
true,
{
deliverableType: deliverable_type,
@@ -60,7 +57,7 @@ export async function saveDeliverable(args) {
const filepath = saveDeliverableFile(filename, content);
// Success response
const successResponse = {
const successResponse: SaveDeliverableResponse = {
status: 'success',
message: `Deliverable saved successfully: ${filename}`,
filepath,

View File

@@ -11,63 +11,42 @@
* Must match the exact mappings from tools/save_deliverable.js.
*/
/**
* @typedef {Object} DeliverableType
* @property {string} CODE_ANALYSIS
* @property {string} RECON
* @property {string} INJECTION_ANALYSIS
* @property {string} INJECTION_QUEUE
* @property {string} XSS_ANALYSIS
* @property {string} XSS_QUEUE
* @property {string} AUTH_ANALYSIS
* @property {string} AUTH_QUEUE
* @property {string} AUTHZ_ANALYSIS
* @property {string} AUTHZ_QUEUE
* @property {string} SSRF_ANALYSIS
* @property {string} SSRF_QUEUE
* @property {string} INJECTION_EVIDENCE
* @property {string} XSS_EVIDENCE
* @property {string} AUTH_EVIDENCE
* @property {string} AUTHZ_EVIDENCE
* @property {string} SSRF_EVIDENCE
*/
export const DeliverableType = {
export enum DeliverableType {
// Pre-recon agent
CODE_ANALYSIS: 'CODE_ANALYSIS',
CODE_ANALYSIS = 'CODE_ANALYSIS',
// Recon agent
RECON: 'RECON',
RECON = 'RECON',
// Vulnerability analysis agents
INJECTION_ANALYSIS: 'INJECTION_ANALYSIS',
INJECTION_QUEUE: 'INJECTION_QUEUE',
INJECTION_ANALYSIS = 'INJECTION_ANALYSIS',
INJECTION_QUEUE = 'INJECTION_QUEUE',
XSS_ANALYSIS: 'XSS_ANALYSIS',
XSS_QUEUE: 'XSS_QUEUE',
XSS_ANALYSIS = 'XSS_ANALYSIS',
XSS_QUEUE = 'XSS_QUEUE',
AUTH_ANALYSIS: 'AUTH_ANALYSIS',
AUTH_QUEUE: 'AUTH_QUEUE',
AUTH_ANALYSIS = 'AUTH_ANALYSIS',
AUTH_QUEUE = 'AUTH_QUEUE',
AUTHZ_ANALYSIS: 'AUTHZ_ANALYSIS',
AUTHZ_QUEUE: 'AUTHZ_QUEUE',
AUTHZ_ANALYSIS = 'AUTHZ_ANALYSIS',
AUTHZ_QUEUE = 'AUTHZ_QUEUE',
SSRF_ANALYSIS: 'SSRF_ANALYSIS',
SSRF_QUEUE: 'SSRF_QUEUE',
SSRF_ANALYSIS = 'SSRF_ANALYSIS',
SSRF_QUEUE = 'SSRF_QUEUE',
// Exploitation agents
INJECTION_EVIDENCE: 'INJECTION_EVIDENCE',
XSS_EVIDENCE: 'XSS_EVIDENCE',
AUTH_EVIDENCE: 'AUTH_EVIDENCE',
AUTHZ_EVIDENCE: 'AUTHZ_EVIDENCE',
SSRF_EVIDENCE: 'SSRF_EVIDENCE',
};
INJECTION_EVIDENCE = 'INJECTION_EVIDENCE',
XSS_EVIDENCE = 'XSS_EVIDENCE',
AUTH_EVIDENCE = 'AUTH_EVIDENCE',
AUTHZ_EVIDENCE = 'AUTHZ_EVIDENCE',
SSRF_EVIDENCE = 'SSRF_EVIDENCE',
}
/**
* Hard-coded filename mappings from agent prompts
* Must match tools/save_deliverable.js exactly
*/
export const DELIVERABLE_FILENAMES = {
export const DELIVERABLE_FILENAMES: Record<DeliverableType, string> = {
[DeliverableType.CODE_ANALYSIS]: 'code_analysis_deliverable.md',
[DeliverableType.RECON]: 'recon_deliverable.md',
[DeliverableType.INJECTION_ANALYSIS]: 'injection_analysis_deliverable.md',
@@ -90,7 +69,7 @@ export const DELIVERABLE_FILENAMES = {
/**
* Queue types that require JSON validation
*/
export const QUEUE_TYPES = [
export const QUEUE_TYPES: DeliverableType[] = [
DeliverableType.INJECTION_QUEUE,
DeliverableType.XSS_QUEUE,
DeliverableType.AUTH_QUEUE,
@@ -100,14 +79,18 @@ export const QUEUE_TYPES = [
/**
* Type guard to check if a deliverable type is a queue
* @param {string} type - Deliverable type to check
* @returns {boolean} True if the type is a queue type
*/
export function isQueueType(type) {
return QUEUE_TYPES.includes(type);
export function isQueueType(type: string): boolean {
return QUEUE_TYPES.includes(type as DeliverableType);
}
/**
* @typedef {Object} VulnerabilityQueue
* @property {Array<Object>} vulnerabilities - Array of vulnerability objects
* Vulnerability queue structure
*/
export interface VulnerabilityQueue {
vulnerabilities: VulnerabilityItem[];
}
export interface VulnerabilityItem {
[key: string]: unknown;
}

View File

@@ -1,64 +0,0 @@
// Copyright (C) 2025 Keygraph, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License version 3
// as published by the Free Software Foundation.
/**
* Tool Response Type Definitions
*
* Defines structured response formats for MCP tools to ensure
* consistent error handling and success reporting.
*/
/**
* @typedef {Object} ErrorResponse
* @property {'error'} status
* @property {string} message
* @property {string} errorType - ValidationError, FileSystemError, CryptoError, etc.
* @property {boolean} retryable
* @property {Record<string, unknown>} [context]
*/
/**
* @typedef {Object} SuccessResponse
* @property {'success'} status
* @property {string} message
*/
/**
* @typedef {Object} SaveDeliverableResponse
* @property {'success'} status
* @property {string} message
* @property {string} filepath
* @property {string} deliverableType
* @property {boolean} validated - true if queue JSON was validated
*/
/**
* @typedef {Object} GenerateTotpResponse
* @property {'success'} status
* @property {string} message
* @property {string} totpCode
* @property {string} timestamp
* @property {number} expiresIn - seconds until expiration
*/
/**
* Helper to create tool result from response
* MCP tools should return this format
*
* @param {ErrorResponse | SaveDeliverableResponse | GenerateTotpResponse} response
* @returns {{ content: Array<{ type: string; text: string }>; isError: boolean }}
*/
export function createToolResult(response) {
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2),
},
],
isError: response.status === 'error',
};
}

View File

@@ -0,0 +1,73 @@
// Copyright (C) 2025 Keygraph, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License version 3
// as published by the Free Software Foundation.
/**
* Tool Response Type Definitions
*
* Defines structured response formats for MCP tools to ensure
* consistent error handling and success reporting.
*/
export interface ErrorResponse {
status: 'error';
message: string;
errorType: string; // ValidationError, FileSystemError, CryptoError, etc.
retryable: boolean;
context?: Record<string, unknown>;
}
export interface SuccessResponse {
status: 'success';
message: string;
}
export interface SaveDeliverableResponse {
status: 'success';
message: string;
filepath: string;
deliverableType: string;
validated: boolean; // true if queue JSON was validated
}
export interface GenerateTotpResponse {
status: 'success';
message: string;
totpCode: string;
timestamp: string;
expiresIn: number; // seconds until expiration
}
export type ToolResponse =
| ErrorResponse
| SuccessResponse
| SaveDeliverableResponse
| GenerateTotpResponse;
export interface ToolResultContent {
type: string;
text: string;
}
export interface ToolResult {
content: ToolResultContent[];
isError: boolean;
}
/**
* Helper to create tool result from response
* MCP tools should return this format
*/
export function createToolResult(response: ToolResponse): ToolResult {
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2),
},
],
isError: response.status === 'error',
};
}

View File

@@ -10,60 +10,50 @@
* Helper functions for creating structured error responses.
*/
/**
* @typedef {Object} ErrorResponse
* @property {'error'} status
* @property {string} message
* @property {string} errorType
* @property {boolean} retryable
* @property {Record<string, unknown>} [context]
*/
import type { ErrorResponse } from '../types/tool-responses.js';
/**
* Create a validation error response
*
* @param {string} message
* @param {boolean} [retryable=true]
* @param {Record<string, unknown>} [context]
* @returns {ErrorResponse}
*/
export function createValidationError(message, retryable = true, context) {
export function createValidationError(
message: string,
retryable: boolean = true,
context?: Record<string, unknown>
): ErrorResponse {
return {
status: 'error',
message,
errorType: 'ValidationError',
retryable,
context,
...(context !== undefined && { context }),
};
}
/**
* Create a crypto error response
*
* @param {string} message
* @param {boolean} [retryable=false]
* @param {Record<string, unknown>} [context]
* @returns {ErrorResponse}
*/
export function createCryptoError(message, retryable = false, context) {
export function createCryptoError(
message: string,
retryable: boolean = false,
context?: Record<string, unknown>
): ErrorResponse {
return {
status: 'error',
message,
errorType: 'CryptoError',
retryable,
context,
...(context !== undefined && { context }),
};
}
/**
* Create a generic error response
*
* @param {unknown} error
* @param {boolean} [retryable=false]
* @param {Record<string, unknown>} [context]
* @returns {ErrorResponse}
*/
export function createGenericError(error, retryable = false, context) {
export function createGenericError(
error: unknown,
retryable: boolean = false,
context?: Record<string, unknown>
): ErrorResponse {
const message = error instanceof Error ? error.message : String(error);
const errorType = error instanceof Error ? error.constructor.name : 'UnknownError';
@@ -72,6 +62,6 @@ export function createGenericError(error, retryable = false, context) {
message,
errorType,
retryable,
context,
...(context !== undefined && { context }),
};
}

View File

@@ -14,14 +14,14 @@
import { writeFileSync, mkdirSync } from 'fs';
import { join } from 'path';
declare global {
var __SHANNON_TARGET_DIR: string | undefined;
}
/**
* Save deliverable file to deliverables/ directory
*
* @param {string} filename - Name of the file to save
* @param {string} content - Content to write to the file
* @returns {string} Full path to the saved file
*/
export function saveDeliverableFile(filename, content) {
export function saveDeliverableFile(filename: string, content: string): string {
// Use target directory from global context (set by createShannonHelperServer)
const targetDir = global.__SHANNON_TARGET_DIR || process.cwd();
const deliverablesDir = join(targetDir, 'deliverables');
@@ -30,7 +30,7 @@ export function saveDeliverableFile(filename, content) {
// Ensure deliverables directory exists
try {
mkdirSync(deliverablesDir, { recursive: true });
} catch (error) {
} catch {
// Directory might already exist, ignore
}

View File

@@ -11,33 +11,41 @@
* Ported from tools/save_deliverable.js (lines 56-75).
*/
/**
* @typedef {Object} ValidationResult
* @property {boolean} valid
* @property {string} [message]
* @property {Object} [data]
*/
import type { VulnerabilityQueue } from '../types/deliverables.js';
export interface ValidationResult {
valid: boolean;
message?: string;
data?: VulnerabilityQueue;
}
/**
* Validate JSON structure for queue files
* Queue files must have a 'vulnerabilities' array
*
* @param {string} content - JSON string to validate
* @returns {ValidationResult} ValidationResult with valid flag, optional error message, and parsed data
*/
export function validateQueueJson(content) {
export function validateQueueJson(content: string): ValidationResult {
try {
const parsed = JSON.parse(content);
const parsed = JSON.parse(content) as unknown;
// Type guard for the parsed result
if (typeof parsed !== 'object' || parsed === null) {
return {
valid: false,
message: `Invalid queue structure: Expected an object. Got: ${typeof parsed}`,
};
}
const obj = parsed as Record<string, unknown>;
// Queue files must have a 'vulnerabilities' array
if (!parsed.vulnerabilities) {
if (!('vulnerabilities' in obj)) {
return {
valid: false,
message: `Invalid queue structure: Missing 'vulnerabilities' property. Expected: {"vulnerabilities": [...]}`,
};
}
if (!Array.isArray(parsed.vulnerabilities)) {
if (!Array.isArray(obj.vulnerabilities)) {
return {
valid: false,
message: `Invalid queue structure: 'vulnerabilities' must be an array. Expected: {"vulnerabilities": [...]}`,
@@ -46,7 +54,7 @@ export function validateQueueJson(content) {
return {
valid: true,
data: parsed,
data: parsed as VulnerabilityQueue,
};
} catch (error) {
return {

View File

@@ -14,11 +14,8 @@
/**
* Base32 decode function
* Ported from generate-totp-standalone.mjs
*
* @param {string} encoded - Base32 encoded string
* @returns {Buffer} Buffer containing decoded bytes
*/
export function base32Decode(encoded) {
export function base32Decode(encoded: string): Buffer {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
const cleanInput = encoded.toUpperCase().replace(/[^A-Z2-7]/g, '');
@@ -26,7 +23,7 @@ export function base32Decode(encoded) {
return Buffer.alloc(0);
}
const output = [];
const output: number[] = [];
let bits = 0;
let value = 0;
@@ -52,10 +49,9 @@ export function base32Decode(encoded) {
* Validate TOTP secret
* Must be base32-encoded string
*
* @param {string} secret - Secret to validate
* @returns {boolean} true if valid, throws Error if invalid
* @returns true if valid, throws Error if invalid
*/
export function validateTotpSecret(secret) {
export function validateTotpSecret(secret: string): boolean {
if (!secret || secret.length === 0) {
throw new Error('TOTP secret cannot be empty');
}

50
mcp-server/tsconfig.json Normal file
View File

@@ -0,0 +1,50 @@
{
// Visit https://aka.ms/tsconfig to read more about this file
"compilerOptions": {
// File Layout
"rootDir": "./src",
"outDir": "./dist",
// Environment Settings
// See also https://aka.ms/tsconfig/module
"module": "nodenext",
"moduleResolution": "nodenext",
"target": "es2022",
"lib": ["es2022"],
"types": ["node"],
// For nodejs:
// "lib": ["esnext"],
// "types": ["node"],
// and npm install -D @types/node
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
"noEmitOnError": true,
// Other Outputs
"sourceMap": true,
"declaration": true,
"declarationMap": true,
// Stricter Typechecking Options
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
// Style Options
// "noImplicitReturns": true,
// "noImplicitOverride": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noFallthroughCasesInSwitch": true,
// "noPropertyAccessFromIndexSignature": true,
// Recommended Options
"strict": true,
"noUncheckedSideEffectImports": true,
"skipLibCheck": true,
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}