feat: migrate to TypeScript for WaterFlow framework

- Add TypeScript graph memory module (GraphDatabase, MemoryService)
- Add GraphMemoryTool for WaterFlow Tool interface
- Add bundled-skills for graph_memory, persona, task
- Remove Python code (core/, ui/, tests/, etc.)
- Remove redundant docs and build files
- Keep only ts/, docs/integration/, .gitignore, LICENSE
This commit is contained in:
root
2026-04-15 15:45:12 +08:00
parent 43172e257a
commit 904661d73f
98 changed files with 2584 additions and 9701 deletions

View File

@ -0,0 +1,102 @@
---
name: graph_memory
description: 图记忆工具 - 让 AI 拥有真正的长期记忆能力
when_to_use: 需要 AI 记住或回忆信息时
context: inline
allowed_tools:
- builtin:graph_memory
arguments:
- name: action
type: string
required: true
enum: [recall, commit, purge, introspect]
description: 记忆操作类型
- name: params
type: object
required: true
description: 操作参数
user_invocable: true
---
# GraphMemory 图记忆操作
你可以通过以下操作与图记忆系统交互。
## 核心操作
### 1. recall - 检索记忆
从记忆图中检索相关信息。
**参数**:
- `queryIntent`: 搜索意图/关键词
- `seedEntities`: 可选的种子实体名
- `depth`: 检索深度
- `sessionFilter`: 可选的会话ID过滤
**示例**:
```
action: recall
params:
queryIntent: "用户 喜欢 编程"
seedEntities: ["用户"]
```
### 2. commit - 写入记忆
将信息写入记忆图。
**参数**:
- `triplets`: 三元组数组,每个包含 subject, relation, object
- `sessionId`: 会话ID
- `turnId`: 轮次ID
**示例**:
```
action: commit
params:
triplets:
- subject: "用户"
relation: "喜欢"
object: "Python"
- subject: "用户"
relation: "正在学习"
object: "TypeScript"
```
### 3. purge - 删除记忆
从记忆图中删除信息。
**参数**:
- `criteria`: 删除条件 (subject, target, relation, sessionId)
- `mode`: 删除模式 (soft/hard/supersede)
- `newRelation`: 可选的替代关系
**示例**:
```
action: purge
params:
criteria:
subject: "旧信息"
mode: "soft"
```
### 4. introspect - 查看状态
查看当前记忆状态统计。
**参数**: 无
**示例**:
```
action: introspect
params: {}
```
## 使用原则
1. **选择性记忆**: 只记住重要和持久的信息
2. **结构化**: 使用三元组 (主体-关系-客体) 格式
3. **关联**: 通过关系连接相关实体
4. **定期清理**: 删除过时或错误的信息

View File

@ -0,0 +1,66 @@
---
name: graph_memory_persona
description: 管理 AI 人设 - 更新或清除 AI 角色特征
when_to_use: 需要修改 AI 的角色设定或清除人设时
context: inline
allowed_tools:
- builtin:graph_memory
arguments:
- name: action
type: string
required: true
enum: [persona_update, persona_clear]
description: 操作类型
- name: attributes
type: array
description: 属性数组 (用于 update)
- name: mode
type: string
enum: [merge, replace]
default: merge
description: 更新模式
- name: confirm
type: boolean
description: 确认清除 (用于 clear)
user_invocable: true
---
# GraphMemory Persona 人设管理
管理 AI 的人设/角色特征。
## 操作
### 1. persona_update - 更新人设
更新 AI 的角色特征。
**参数**:
- `attributes`: 属性数组,每个包含 attribute 和 value
- `mode`: 更新模式
- `merge`: 合并到现有属性
- `replace`: 替换所有现有属性
**示例**:
```
action: persona_update
attributes:
- attribute: "角色"
value: "猫娘"
- attribute: "性格"
value: "活泼"
mode: "merge"
```
### 2. persona_clear - 清除人设
清除 AI 的所有角色特征。
**参数**:
- `confirm`: 确认为 true 才能执行清除
**示例**:
```
action: persona_clear
confirm: true
```

View File

@ -0,0 +1,98 @@
---
name: graph_memory_task
description: 管理连续性任务 - 创建、更新、删除任务节点
when_to_use: 需要创建或管理长期任务时
context: inline
allowed_tools:
- builtin:graph_memory
arguments:
- name: action
type: string
required: true
enum: [task_create, task_set_state, task_delete, task_link_info]
description: 操作类型
- name: task_id
type: string
required: true
description: 任务ID
- name: description
type: string
description: 任务描述 (用于 create)
- name: state
type: string
enum: [进行中, 已完成, 已暂停, 已取消]
description: 任务状态 (用于 set_state)
- name: info_nodes
type: array
description: 信息节点数组 (用于 create)
- name: info_node
type: string
description: 信息节点 (用于 link_info)
user_invocable: true
---
# GraphMemory Task 任务管理
管理长期/连续性任务。
## 操作
### 1. task_create - 创建任务
创建新的任务节点。
**参数**:
- `task_id`: 唯一任务标识
- `description`: 任务描述
- `info_nodes`: 可选的相关信息节点
**示例**:
```
action: task_create
task_id: "Task_学习TypeScript"
description: "学习 TypeScript 并完成项目"
info_nodes: ["TypeScript文档", "教程链接"]
```
### 2. task_set_state - 设置状态
更新任务状态。
**参数**:
- `task_id`: 任务ID
- `state`: 新状态 (进行中/已完成/已暂停/已取消)
**示例**:
```
action: task_set_state
task_id: "Task_学习TypeScript"
state: "已完成"
```
### 3. task_delete - 删除任务
删除任务节点。
**参数**:
- `task_id`: 任务ID
**示例**:
```
action: task_delete
task_id: "Task_学习TypeScript"
```
### 4. task_link_info - 关联信息
将信息节点关联到任务。
**参数**:
- `task_id`: 任务ID
- `info_node`: 信息节点
**示例**:
```
action: task_link_info
task_id: "Task_学习TypeScript"
info_node: "新教程链接"
```

1378
ts/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

17
ts/package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "trulymem-waterflow",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "tsc",
"test": "vitest"
},
"dependencies": {
"yaml": "^2.8.3"
},
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.0.0",
"vitest": "^2.0.0"
}
}

View File

@ -0,0 +1,196 @@
import type { Entity, Relation, RecallParams, CommitParams, PurgeParams, RecallResult, CommitResult, PurgeResult, MemoryStats } from './types';
export class GraphDatabase {
private entities: Map<string, Entity> = new Map();
private relations: Map<string, Relation> = new Map();
private sessionId: string;
constructor(sessionId?: string) {
this.sessionId = sessionId || `session-${Date.now()}`;
}
async recall(params: RecallParams): Promise<RecallResult> {
const { queryIntent, seedEntities, sessionFilter } = params;
const keywords = queryIntent.split(/[,\s]+/).filter(k => k.length > 0);
const entities: Entity[] = [];
const relations: Relation[] = [];
const entityIds = new Set<string>();
for (const keyword of keywords) {
const lowerKeyword = keyword.toLowerCase();
for (const [_, entity] of this.entities) {
if (entity.name.toLowerCase().includes(lowerKeyword)) {
if (!entityIds.has(entity.id)) {
entityIds.add(entity.id);
entities.push(entity);
}
}
}
}
if (seedEntities && seedEntities.length > 0) {
for (const seedName of seedEntities) {
for (const [_, entity] of this.entities) {
if (entity.name.toLowerCase() === seedName.toLowerCase()) {
if (!entityIds.has(entity.id)) {
entityIds.add(entity.id);
entities.push(entity);
}
}
}
}
}
for (const [_, relation] of this.relations) {
if (entityIds.has(relation.sourceId) || entityIds.has(relation.targetId)) {
if (relation.status === 'active') {
if (!sessionFilter || relation.sessionId === sessionFilter) {
relations.push(relation);
}
}
}
}
return {
entities,
relations,
message: `找到 ${entities.length} 个实体, ${relations.length} 条关系`
};
}
async commit(params: CommitParams): Promise<CommitResult> {
const { triplets, sessionId, turnId } = params;
let createdEntities = 0;
let createdRelations = 0;
for (const triplet of triplets) {
const sourceId = this.upsertEntity(triplet.subject);
const targetId = this.upsertEntity(triplet.object);
const relationId = this.generateId();
const now = new Date();
const relation: Relation = {
id: relationId,
sourceId,
targetId,
relationType: triplet.relation,
confidence: triplet.confidence || 1.0,
status: 'active',
sessionId: sessionId || this.sessionId,
turnId: turnId || 0,
createdAt: now,
updatedAt: now,
dateBucket: this.getDateBucket(now)
};
this.relations.set(relationId, relation);
createdEntities += 2;
createdRelations++;
}
return { createdEntities, createdRelations };
}
async purge(params: PurgeParams): Promise<PurgeResult> {
const { criteria, mode = 'soft' } = params;
let deleted = 0;
for (const [id, relation] of this.relations) {
if (relation.status !== 'active') continue;
if (!criteria) {
continue;
}
let matches = true;
if (criteria.subject) {
const sourceEntity = this.entities.get(relation.sourceId);
matches = sourceEntity?.name.toLowerCase() === criteria.subject.toLowerCase();
}
if (matches && criteria.target) {
const targetEntity = this.entities.get(relation.targetId);
matches = targetEntity?.name.toLowerCase() === criteria.target.toLowerCase();
}
if (matches && criteria.relation) {
matches = relation.relationType.toLowerCase() === criteria.relation.toLowerCase();
}
if (matches && criteria.sessionId) {
matches = relation.sessionId === criteria.sessionId;
}
if (matches) {
if (mode === 'hard') {
this.relations.delete(id);
} else {
relation.status = 'deleted';
relation.updatedAt = new Date();
}
deleted++;
}
}
return { deleted, mode };
}
async introspect(): Promise<MemoryStats> {
let entityCount = 0;
for (const [_, entity] of this.entities) {
if (!this.isEntityDeleted(entity.id)) entityCount++;
}
let relationCount = 0;
for (const [_, relation] of this.relations) {
if (relation.status === 'active') relationCount++;
}
return { entityCount, relationCount, sessionId: this.sessionId };
}
private upsertEntity(name: string): string {
for (const [id, entity] of this.entities) {
if (entity.name === name && !this.isEntityDeleted(id)) {
entity.mentionCount++;
entity.updatedAt = new Date();
return id;
}
}
const id = this.generateId();
const now = new Date();
const entity: Entity = {
id,
name,
type: 'unknown',
mentionCount: 1,
createdAt: now,
updatedAt: now
};
this.entities.set(id, entity);
return id;
}
private isEntityDeleted(entityId: string): boolean {
for (const [_, relation] of this.relations) {
if ((relation.sourceId === entityId || relation.targetId === entityId) && relation.status === 'deleted') {
return true;
}
}
return false;
}
private generateId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
private getDateBucket(date: Date): string {
return date.toISOString().split('T')[0] ?? '';
}
setSessionId(sessionId: string): void {
this.sessionId = sessionId;
}
getSessionId(): string {
return this.sessionId;
}
}

View File

@ -0,0 +1,3 @@
export * from './types';
export * from './graph_database';
export * from './memory_service';

View File

@ -0,0 +1,129 @@
import { GraphDatabase } from './graph_database';
import type { RecallParams, CommitParams, PurgeParams, RecallResult, CommitResult, PurgeResult, MemoryStats } from './types';
export class MemoryService {
private db: GraphDatabase;
constructor(db: GraphDatabase) {
this.db = db;
}
async recall(params: RecallParams): Promise<RecallResult> {
return this.db.recall(params);
}
async commit(params: CommitParams): Promise<CommitResult> {
return this.db.commit(params);
}
async purge(params: PurgeParams): Promise<PurgeResult> {
return this.db.purge(params);
}
async introspect(): Promise<MemoryStats> {
return this.db.introspect();
}
async updatePersona(params: { attributes: Array<{ attribute: string; value: string }>; mode?: 'merge' | 'replace' }): Promise<{ status: string; updatedAttributes: number }> {
const { attributes, mode = 'merge' } = params;
if (mode === 'replace') {
await this.db.purge({
criteria: { subject: 'AI' },
mode: 'soft'
});
}
const triplets = attributes.map(attr => ({
subject: 'AI',
relation: attr.attribute,
object: attr.value,
confidence: 1.0
}));
await this.db.commit({ triplets });
return { status: 'success', updatedAttributes: attributes.length };
}
async clearPersona(params: { confirm: boolean }): Promise<{ status: string; deletedCount: number }> {
if (params.confirm === false) {
return { status: 'cancelled', deletedCount: 0 };
}
const result = await this.db.purge({
criteria: { subject: 'AI' },
mode: 'soft'
});
return { status: 'success', deletedCount: result.deleted };
}
async createTask(params: { task_id: string; description: string; info_nodes?: string[] | undefined }): Promise<{ status: string; taskId: string }> {
const { task_id, description, info_nodes = [] } = params;
await this.db.commit({
triplets: [
{ subject: task_id, relation: 'is_type', object: 'TaskNode' },
{ subject: task_id, relation: 'has_description', object: description },
{ subject: task_id, relation: 'HAS_STATE', object: 'State_进行中' }
]
});
if (info_nodes.length > 0) {
await this.db.commit({
triplets: info_nodes.map(node => ({
subject: task_id,
relation: 'CONTAINS_INFO',
object: node
}))
});
}
return { status: 'success', taskId: task_id };
}
async setTaskState(params: { task_id: string; state: string }): Promise<{ status: string; newState: string }> {
const { task_id, state } = params;
await this.db.purge({
criteria: { subject: task_id, relation: 'HAS_STATE' },
mode: 'soft'
});
await this.db.commit({
triplets: [{ subject: task_id, relation: 'HAS_STATE', object: `State_${state}` }]
});
return { status: 'success', newState: state };
}
async deleteTask(params: { task_id: string }): Promise<{ status: string; taskId: string }> {
const { task_id } = params;
await this.db.purge({
criteria: { subject: task_id },
mode: 'soft'
});
return { status: 'success', taskId: task_id };
}
async linkInfoToTask(params: { task_id: string; info_node: string }): Promise<{ status: string }> {
const { task_id, info_node } = params;
await this.db.commit({
triplets: [{ subject: task_id, relation: 'CONTAINS_INFO', object: info_node }]
});
return { status: 'success' };
}
setSessionId(sessionId: string): void {
this.db.setSessionId(sessionId);
}
getSessionId(): string {
return this.db.getSessionId();
}
}

View File

@ -0,0 +1,125 @@
export interface Entity {
id: string;
name: string;
type: string;
mentionCount: number;
createdAt: Date;
updatedAt: Date;
}
export type RelationStatus = 'active' | 'deleted' | 'archived' | 'superseded';
export interface Relation {
id: string;
sourceId: string;
targetId: string;
relationType: string;
confidence: number;
status: RelationStatus;
sessionId: string;
turnId: number;
createdAt: Date;
updatedAt: Date;
dateBucket: string;
}
export interface Triplet {
subject: string;
relation: string;
object: string;
confidence?: number;
}
export interface RecallParams {
queryIntent: string;
seedEntities?: string[] | undefined;
depth?: number | undefined;
timeRange?: { days: number } | undefined;
sessionFilter?: string | undefined;
}
export interface CommitParams {
triplets: Triplet[];
entityTypes?: Record<string, string> | undefined;
temporalTag?: string | undefined;
sessionId?: string | undefined;
turnId?: number | undefined;
}
export interface PurgeParams {
criteria?: {
subject?: string | undefined;
target?: string | undefined;
relation?: string | undefined;
sessionId?: string | undefined;
} | undefined;
mode?: 'soft' | 'hard' | 'supersede' | undefined;
newRelation?: { relation: string; target: string } | undefined;
}
export interface RecallResult {
entities: Entity[];
relations: Relation[];
message: string;
}
export interface CommitResult {
createdEntities: number;
createdRelations: number;
}
export interface PurgeResult {
deleted: number;
mode: string;
}
export type TaskState = '进行中' | '已完成' | '已暂停' | '已取消';
export interface Task {
taskId: string;
description: string;
state: TaskState;
infoNodes: string[];
createdAt: Date;
updatedAt: Date;
}
export interface MemoryStats {
entityCount: number;
relationCount: number;
sessionId?: string;
}
export interface PersonaAttribute {
attribute: string;
value: string;
}
export interface PersonaUpdateParams {
attributes: PersonaAttribute[];
mode?: 'merge' | 'replace';
}
export interface PersonaClearParams {
confirm: boolean;
}
export interface TaskCreateParams {
task_id: string;
description: string;
info_nodes?: string[] | undefined;
}
export interface TaskSetStateParams {
task_id: string;
state: string;
}
export interface TaskDeleteParams {
task_id: string;
}
export interface TaskLinkInfoParams {
task_id: string;
info_node: string;
}

View File

@ -0,0 +1,190 @@
import type { Tool, ToolCategory, PermissionLevel, ToolInputSchema, ToolExecutionContext, ToolOutput } from '../tool_interface';
import { GraphDatabase } from '../../graph_memory/graph_database';
import { MemoryService } from '../../graph_memory/memory_service';
const GRAPH_MEMORY_TOOL_ID = 'builtin:graph_memory';
const GRAPH_MEMORY_TOOL_DESCRIPTION = `图记忆工具 - 让 AI 拥有真正的长期记忆能力
操作:
- recall: 检索记忆
- commit: 写入记忆
- purge: 删除记忆
- introspect: 查看状态
- persona_update/clear: 人设管理
- task_create/set_state/delete: 任务管理`;
export class GraphMemoryTool implements Tool {
readonly id = GRAPH_MEMORY_TOOL_ID;
readonly name = 'GraphMemory';
readonly description = GRAPH_MEMORY_TOOL_DESCRIPTION;
readonly category: ToolCategory = 'analysis';
readonly permissionLevel: PermissionLevel = 'safe';
readonly inputSchema: ToolInputSchema = {
type: 'object',
properties: {
action: {
type: 'string',
enum: [
'recall', 'commit', 'purge', 'introspect',
'persona_update', 'persona_clear',
'task_create', 'task_set_state', 'task_delete', 'task_link_info'
],
description: '记忆操作类型'
},
params: {
type: 'object',
description: '操作参数',
properties: {
queryIntent: { type: 'string', description: '搜索意图' },
seedEntities: { type: 'array', items: { type: 'string', description: '实体' }, description: '种子实体' },
depth: { type: 'number', description: '检索深度' },
sessionFilter: { type: 'string', description: '会话ID过滤' },
triplets: {
type: 'array',
items: {
type: 'object',
description: '三元组',
properties: {
subject: { type: 'string', description: '主体' },
relation: { type: 'string', description: '关系' },
object: { type: 'string', description: '客体' },
confidence: { type: 'number', description: '置信度' }
}
},
description: '三元组数组'
},
sessionId: { type: 'string', description: '会话ID' },
turnId: { type: 'number', description: '轮次ID' },
criteria: {
type: 'object',
properties: {
subject: { type: 'string', description: '主体' },
target: { type: 'string', description: '客体' },
relation: { type: 'string', description: '关系' },
sessionId: { type: 'string', description: '会话ID' }
},
description: '删除条件'
},
mode: { type: 'string', enum: ['soft', 'hard', 'supersede'], description: '删除模式' },
attributes: {
type: 'array',
items: {
type: 'object',
description: '属性',
properties: {
attribute: { type: 'string', description: '属性名' },
value: { type: 'string', description: '属性值' }
}
},
description: '属性数组'
},
confirm: { type: 'boolean', description: '确认清除' },
task_id: { type: 'string', description: '任务ID' },
description: { type: 'string', description: '任务描述' },
state: { type: 'string', description: '任务状态' },
info_nodes: { type: 'array', items: { type: 'string', description: '节点' }, description: '信息节点' },
info_node: { type: 'string', description: '信息节点' }
}
}
},
required: ['action', 'params']
};
private db: GraphDatabase;
private service: MemoryService;
constructor(sessionId?: string) {
this.db = new GraphDatabase(sessionId);
this.service = new MemoryService(this.db);
}
async handler(params: Record<string, unknown>, _context: ToolExecutionContext): Promise<ToolOutput> {
const action = params.action as string;
const actionParams = params.params as Record<string, unknown>;
try {
const result = await this.executeAction(action, actionParams);
return JSON.stringify({ success: true, data: result }, null, 2);
} catch (error) {
return JSON.stringify({
success: false,
error: {
type: 'execution_error',
message: error instanceof Error ? error.message : String(error)
}
}, null, 2);
}
}
private async executeAction(action: string, params: Record<string, unknown>): Promise<unknown> {
switch (action) {
case 'recall':
return this.service.recall({
queryIntent: params.queryIntent as string || '',
seedEntities: params.seedEntities as string[] | undefined,
depth: params.depth as number | undefined,
sessionFilter: params.sessionFilter as string | undefined
});
case 'commit':
return this.service.commit({
triplets: params.triplets as Array<{ subject: string; relation: string; object: string; confidence?: number }>,
sessionId: params.sessionId as string | undefined,
turnId: params.turnId as number | undefined
});
case 'purge':
return this.service.purge({
criteria: params.criteria as { subject?: string | undefined; target?: string | undefined; relation?: string | undefined; sessionId?: string | undefined } | undefined,
mode: params.mode as 'soft' | 'hard' | 'supersede' | undefined
});
case 'introspect':
return this.service.introspect();
case 'persona_update':
return this.service.updatePersona({
attributes: params.attributes as Array<{ attribute: string; value: string }>,
mode: params.mode as 'merge' | 'replace'
});
case 'persona_clear':
return this.service.clearPersona({
confirm: params.confirm as boolean
});
case 'task_create':
return this.service.createTask({
task_id: params.task_id as string,
description: params.description as string,
info_nodes: params.info_nodes as string[] | undefined
});
case 'task_set_state':
return this.service.setTaskState({
task_id: params.task_id as string,
state: params.state as string
});
case 'task_delete':
return this.service.deleteTask({
task_id: params.task_id as string
});
case 'task_link_info':
return this.service.linkInfoToTask({
task_id: params.task_id as string,
info_node: params.info_node as string
});
default:
throw new Error(`Unknown action: ${action}`);
}
}
}
export function createGraphMemoryTool(sessionId?: string): GraphMemoryTool {
return new GraphMemoryTool(sessionId);
}

View File

@ -0,0 +1,59 @@
export type ToolCategory = 'file' | 'code' | 'search' | 'execute' | 'network' | 'analysis' | 'generation' | 'communication' | 'mcp' | 'custom';
export type PermissionLevel = 'safe' | 'moderate' | 'dangerous' | 'restricted';
export type SchemaType = 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object';
export interface SchemaProperty {
type: SchemaType;
description: string;
enum?: string[];
minimum?: number;
maximum?: number;
minLength?: number;
maxLength?: number;
pattern?: string;
default?: unknown;
examples?: unknown[];
items?: SchemaProperty;
properties?: Record<string, SchemaProperty>;
}
export interface ToolInputSchema {
type: 'object';
properties: Record<string, SchemaProperty>;
required?: string[];
additionalProperties?: boolean;
}
export interface ToolOutputSchema {
type: 'object';
properties: Record<string, SchemaProperty>;
format?: 'json' | 'text' | 'markdown' | 'binary';
maxSize?: number;
maxLines?: number;
}
export type ToolInput = Record<string, unknown>;
export type ToolOutput = string | Record<string, unknown> | void;
export interface ToolExecutionContext {
toolCallId: string;
workingDirectory: string;
abortController: { signal: AbortSignal };
config: { timeout?: number };
logger: { info: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void; debug: (...args: unknown[]) => void };
}
export type ToolHandler = (params: ToolInput, context: ToolExecutionContext) => Promise<ToolOutput>;
export interface Tool {
readonly id: string;
readonly name: string;
readonly description: string;
readonly category: ToolCategory;
readonly inputSchema: ToolInputSchema;
readonly outputSchema?: ToolOutputSchema;
readonly handler: ToolHandler;
readonly permissionLevel: PermissionLevel;
}

23
ts/tsconfig.json Normal file
View File

@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"exactOptionalPropertyTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}