gearvn-pages-mcp-server 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.example ADDED
@@ -0,0 +1,3 @@
1
+ # Required: Your GearVN Pages API Key
2
+ # Get this by calling: POST https://pages.react.uat.gearvn.xyz/v1/api-keys
3
+ API_KEY=sk-your-api-key-here
package/README.md ADDED
@@ -0,0 +1,286 @@
1
+ # GearVN Pages MCP Server
2
+
3
+ MCP (Model Context Protocol) Server cho việc quản lý và deploy các trang HTML tĩnh lên GearVN Pages.
4
+
5
+ ## Tính năng
6
+
7
+ MCP server này cung cấp 2 tools:
8
+
9
+ ### 1. deploy-page
10
+ Deploy hoặc update một trang HTML tĩnh.
11
+
12
+ **Parameters:**
13
+ - `title` (string, required): Tiêu đề của trang
14
+ - `content` (string, required): Nội dung HTML đầy đủ của trang. Phải bao gồm complete HTML document với TailwindCSS CDN. JavaScript libraries được phép nhưng phải import qua CDN (ví dụ: Alpine.js, Chart.js, GSAP). Không dùng React, Vue, Angular hoặc build tools
15
+ - `slug` (string, required): URL slug cho trang (ví dụ: "my-page-123")
16
+
17
+ **Response:**
18
+ ```json
19
+ {
20
+ "success": true,
21
+ "message": "Page deployed successfully",
22
+ "url": "https://pages.react.uat.gearvn.xyz/pages/my-page-123"
23
+ }
24
+ ```
25
+
26
+ ### 2. list-pages
27
+ Liệt kê tất cả các pages của API key hiện tại.
28
+
29
+ **Parameters:** Không có
30
+
31
+ **Response:**
32
+ ```json
33
+ {
34
+ "success": true,
35
+ "count": 2,
36
+ "pages": [
37
+ {
38
+ "id": 1,
39
+ "slug": "my-page-123",
40
+ "title": "My Page",
41
+ "created_at": "2025-01-20T10:00:00Z",
42
+ "updated_at": "2025-01-20T10:00:00Z"
43
+ }
44
+ ]
45
+ }
46
+ ```
47
+
48
+ ## Cài đặt
49
+
50
+ ### Prerequisites
51
+ - Node.js 18 hoặc cao hơn
52
+ - npm hoặc pnpm
53
+ - API key từ GearVN Pages
54
+
55
+ ### Bước 1: Cài đặt dependencies
56
+
57
+ ```bash
58
+ cd mcp-server
59
+ npm install
60
+ # hoặc
61
+ pnpm install
62
+ ```
63
+
64
+ ### Bước 2: Build TypeScript
65
+
66
+ ```bash
67
+ npm run build
68
+ # hoặc
69
+ pnpm build
70
+ ```
71
+
72
+ ## Cấu hình
73
+
74
+ MCP server cần 1 environment variable:
75
+
76
+ - `API_KEY` (required): API key của bạn để authenticate với GearVN Pages API
77
+
78
+ ### Cách lấy API Key
79
+
80
+ Sử dụng API để tạo API key:
81
+
82
+ ```bash
83
+ curl -X POST https://pages.react.uat.gearvn.xyz/v1/api-keys \
84
+ -H "Content-Type: application/json" \
85
+ -d '{"name": "My MCP Key"}'
86
+ ```
87
+
88
+ Response sẽ trả về API key, lưu lại để sử dụng.
89
+
90
+ ## Sử dụng với Claude Desktop
91
+
92
+ ### Bước 1: Thêm vào Claude Desktop config
93
+
94
+ Mở file config của Claude Desktop:
95
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
96
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
97
+
98
+ ### Bước 2: Thêm MCP server config
99
+
100
+ **Cách 1: Sử dụng npx (Khuyến nghị)**
101
+
102
+ ```json
103
+ {
104
+ "mcpServers": {
105
+ "gearvn-pages": {
106
+ "command": "npx",
107
+ "args": [
108
+ "-y",
109
+ "/absolute/path/to/backend-webpage-go/mcp-server"
110
+ ],
111
+ "env": {
112
+ "API_KEY": "sk-your-api-key-here"
113
+ }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ **Cách 2: Chạy trực tiếp với node**
120
+
121
+ ```json
122
+ {
123
+ "mcpServers": {
124
+ "gearvn-pages": {
125
+ "command": "node",
126
+ "args": [
127
+ "/absolute/path/to/backend-webpage-go/mcp-server/dist/index.js"
128
+ ],
129
+ "env": {
130
+ "API_KEY": "sk-your-api-key-here"
131
+ }
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ **Lưu ý:** Thay `/absolute/path/to/` bằng đường dẫn tuyệt đối đến folder mcp-server của bạn.
138
+
139
+ ### Bước 3: Restart Claude Desktop
140
+
141
+ Sau khi thêm config, restart Claude Desktop để load MCP server.
142
+
143
+ ## Sử dụng với Cline (VS Code Extension)
144
+
145
+ ### Bước 1: Mở VS Code Settings
146
+
147
+ 1. Mở Command Palette (Cmd+Shift+P / Ctrl+Shift+P)
148
+ 2. Gõ "Preferences: Open User Settings (JSON)"
149
+
150
+ ### Bước 2: Thêm MCP server config
151
+
152
+ Tìm section `cline.mcpServers` và thêm:
153
+
154
+ **Cách 1: Sử dụng npx (Khuyến nghị)**
155
+
156
+ ```json
157
+ {
158
+ "cline.mcpServers": {
159
+ "gearvn-pages": {
160
+ "command": "npx",
161
+ "args": [
162
+ "-y",
163
+ "/absolute/path/to/backend-webpage-go/mcp-server"
164
+ ],
165
+ "env": {
166
+ "API_KEY": "sk-your-api-key-here"
167
+ }
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ **Cách 2: Chạy trực tiếp với node**
174
+
175
+ ```json
176
+ {
177
+ "cline.mcpServers": {
178
+ "gearvn-pages": {
179
+ "command": "node",
180
+ "args": [
181
+ "/absolute/path/to/backend-webpage-go/mcp-server/dist/index.js"
182
+ ],
183
+ "env": {
184
+ "API_KEY": "sk-your-api-key-here"
185
+ }
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ ### Bước 3: Reload VS Code
192
+
193
+ Reload window để Cline load MCP server.
194
+
195
+ ## Test MCP Server
196
+
197
+ Bạn có thể test MCP server bằng cách chạy trực tiếp:
198
+
199
+ ```bash
200
+ export API_KEY="sk-your-api-key-here"
201
+ npm start
202
+ ```
203
+
204
+ Server sẽ chạy trên stdio và chờ nhận input theo MCP protocol.
205
+
206
+ ## Ví dụ sử dụng trong Claude
207
+
208
+ Sau khi cấu hình xong, bạn có thể sử dụng trong Claude:
209
+
210
+ ### Deploy một trang mới
211
+
212
+ ```
213
+ Hãy deploy một trang HTML với:
214
+ - Title: "Test Page"
215
+ - Content: Một trang HTML hoàn chỉnh với TailwindCSS và một nút button
216
+ - Slug: "test-page-1"
217
+ ```
218
+
219
+ Claude sẽ tự động tạo HTML document hoàn chỉnh với TailwindCSS CDN, sử dụng tool `deploy-page` và trả về URL của trang.
220
+
221
+ **Lưu ý:** AI Agent sẽ tự động convert nội dung thành HTML chuẩn với:
222
+ - TailwindCSS CDN link trong `<head>`
223
+ - TailwindCSS utility classes cho styling
224
+ - JavaScript libraries qua CDN nếu cần (Alpine.js, Chart.js, etc.)
225
+ - Vanilla JavaScript cũng được (không dùng SPA frameworks như React/Vue/Angular)
226
+
227
+ ### List các pages
228
+
229
+ ```
230
+ Hãy liệt kê tất cả các pages của tôi
231
+ ```
232
+
233
+ Claude sẽ sử dụng tool `list-pages` và hiển thị danh sách pages.
234
+
235
+ ## Development
236
+
237
+ ### Watch mode
238
+
239
+ ```bash
240
+ npm run watch
241
+ # hoặc
242
+ pnpm watch
243
+ ```
244
+
245
+ Sẽ tự động rebuild khi có thay đổi trong source code.
246
+
247
+ ## Troubleshooting
248
+
249
+ ### MCP server không xuất hiện trong Claude
250
+
251
+ 1. Kiểm tra file config đã đúng format JSON
252
+ 2. Kiểm tra đường dẫn đến `dist/index.js` là absolute path
253
+ 3. Kiểm tra đã build project: `npm run build`
254
+ 4. Restart Claude Desktop
255
+
256
+ ### API Key không hoạt động
257
+
258
+ 1. Kiểm tra API key đã được set đúng trong env với key là `API_KEY`
259
+ 2. Kiểm tra API key còn valid bằng cách call API trực tiếp:
260
+
261
+ ```bash
262
+ curl -X GET https://pages.react.uat.gearvn.xyz/v1/pages \
263
+ -H "X-API-Key: your-api-key"
264
+ ```
265
+
266
+ ### Permission denied khi chạy
267
+
268
+ ```bash
269
+ chmod +x dist/index.js
270
+ ```
271
+
272
+ ## Cấu trúc project
273
+
274
+ ```
275
+ mcp-server/
276
+ ├── src/
277
+ │ └── index.ts # MCP server implementation
278
+ ├── dist/ # Compiled JavaScript (generated)
279
+ ├── package.json
280
+ ├── tsconfig.json
281
+ └── README.md
282
+ ```
283
+
284
+ ## License
285
+
286
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ // Configuration
6
+ const API_BASE_URL = "https://pages.react.uat.gearvn.xyz";
7
+ const API_KEY = process.env.API_KEY;
8
+ if (!API_KEY) {
9
+ console.error("Error: API_KEY environment variable is required");
10
+ process.exit(1);
11
+ }
12
+ // API helper function
13
+ async function callAPI(endpoint, method = "GET", body) {
14
+ const url = `${API_BASE_URL}${endpoint}`;
15
+ const headers = {
16
+ "X-API-Key": API_KEY,
17
+ };
18
+ if (body) {
19
+ headers["Content-Type"] = "application/json";
20
+ }
21
+ const response = await fetch(url, {
22
+ method,
23
+ headers,
24
+ body: body ? JSON.stringify(body) : undefined,
25
+ });
26
+ if (!response.ok) {
27
+ const errorText = await response.text();
28
+ throw new Error(`API request failed: ${response.status} ${errorText}`);
29
+ }
30
+ return response.json();
31
+ }
32
+ // Tool definitions
33
+ const DEPLOY_PAGE_TOOL = {
34
+ name: "deploy-page",
35
+ description: "Deploy or update a static HTML page. If the slug already exists, the page will be updated with the new content and title. IMPORTANT: The content must be a complete, valid HTML document with TailwindCSS for styling. JavaScript libraries are allowed but MUST be loaded via CDN (e.g., Alpine.js, Chart.js, GSAP, etc.). No build tools or npm packages. No React, Vue, or Angular frameworks allowed.",
36
+ inputSchema: {
37
+ type: "object",
38
+ properties: {
39
+ title: {
40
+ type: "string",
41
+ description: "The title of the page",
42
+ },
43
+ content: {
44
+ type: "string",
45
+ description: "The complete HTML content of the page. Must include <!DOCTYPE html>, <html>, <head> with TailwindCSS CDN link (https://cdn.tailwindcss.com), and <body> tags. Use TailwindCSS utility classes for all styling. JavaScript libraries are allowed but must be imported via CDN links in <script> tags (e.g., Alpine.js from CDN, Chart.js from CDN). Vanilla JavaScript is also acceptable. NO build tools, npm packages, or SPA frameworks (React/Vue/Angular).",
46
+ },
47
+ slug: {
48
+ type: "string",
49
+ description: "The URL slug for the page (e.g., 'my-page-123'). This will be used in the URL as /pages/{slug}",
50
+ },
51
+ },
52
+ required: ["title", "content", "slug"],
53
+ },
54
+ };
55
+ const LIST_PAGES_TOOL = {
56
+ name: "list-pages",
57
+ description: "List all pages associated with your API key. Returns a summary of each page including id, slug, title, and timestamps. Does not include page content.",
58
+ inputSchema: {
59
+ type: "object",
60
+ properties: {},
61
+ },
62
+ };
63
+ // Create MCP server
64
+ const server = new Server({
65
+ name: "gearvn-pages-mcp-server",
66
+ version: "1.0.0",
67
+ }, {
68
+ capabilities: {
69
+ tools: {},
70
+ },
71
+ });
72
+ // Handler for listing available tools
73
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
74
+ return {
75
+ tools: [DEPLOY_PAGE_TOOL, LIST_PAGES_TOOL],
76
+ };
77
+ });
78
+ // Handler for tool calls
79
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
80
+ const { name, arguments: args } = request.params;
81
+ try {
82
+ if (name === "deploy-page") {
83
+ const { title, content, slug } = args;
84
+ // Validate inputs
85
+ if (!title || !content || !slug) {
86
+ throw new Error("Missing required parameters: title, content, and slug are required");
87
+ }
88
+ // Call the deploy API
89
+ const response = await callAPI("/v1/deploy", "POST", {
90
+ title,
91
+ content,
92
+ slug,
93
+ });
94
+ return {
95
+ content: [
96
+ {
97
+ type: "text",
98
+ text: JSON.stringify({
99
+ success: true,
100
+ message: "Page deployed successfully",
101
+ url: response.url,
102
+ }, null, 2),
103
+ },
104
+ ],
105
+ };
106
+ }
107
+ else if (name === "list-pages") {
108
+ // Call the list pages API
109
+ const response = await callAPI("/v1/pages", "GET");
110
+ return {
111
+ content: [
112
+ {
113
+ type: "text",
114
+ text: JSON.stringify({
115
+ success: true,
116
+ count: response.count,
117
+ pages: response.pages,
118
+ }, null, 2),
119
+ },
120
+ ],
121
+ };
122
+ }
123
+ else {
124
+ throw new Error(`Unknown tool: ${name}`);
125
+ }
126
+ }
127
+ catch (error) {
128
+ const errorMessage = error instanceof Error ? error.message : String(error);
129
+ return {
130
+ content: [
131
+ {
132
+ type: "text",
133
+ text: JSON.stringify({
134
+ success: false,
135
+ error: errorMessage,
136
+ }, null, 2),
137
+ },
138
+ ],
139
+ isError: true,
140
+ };
141
+ }
142
+ });
143
+ // Start the server
144
+ async function main() {
145
+ const transport = new StdioServerTransport();
146
+ await server.connect(transport);
147
+ console.error("GearVN Pages MCP Server running on stdio");
148
+ }
149
+ main().catch((error) => {
150
+ console.error("Fatal error:", error);
151
+ process.exit(1);
152
+ });
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "gearvn-pages-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP Server for GearVN Pages deployment and management",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "gearvn-pages-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "watch": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "gearvn"
20
+ ],
21
+ "author": "GearVN",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.0.4"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^22.10.2",
28
+ "typescript": "^5.7.2"
29
+ }
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
+ Tool,
9
+ } from "@modelcontextprotocol/sdk/types.js";
10
+
11
+ // Configuration
12
+ const API_BASE_URL = "https://pages.react.uat.gearvn.xyz";
13
+ const API_KEY = process.env.API_KEY;
14
+
15
+ if (!API_KEY) {
16
+ console.error("Error: API_KEY environment variable is required");
17
+ process.exit(1);
18
+ }
19
+
20
+ // Types
21
+ interface DeployPageArgs {
22
+ title: string;
23
+ content: string;
24
+ slug: string;
25
+ }
26
+
27
+ interface DeployPageResponse {
28
+ url: string;
29
+ }
30
+
31
+ interface PageSummary {
32
+ id: number;
33
+ slug: string;
34
+ title: string;
35
+ created_at: string;
36
+ updated_at: string;
37
+ }
38
+
39
+ interface ListPagesResponse {
40
+ count: number;
41
+ pages: PageSummary[];
42
+ }
43
+
44
+ // API helper function
45
+ async function callAPI<T>(
46
+ endpoint: string,
47
+ method: string = "GET",
48
+ body?: unknown
49
+ ): Promise<T> {
50
+ const url = `${API_BASE_URL}${endpoint}`;
51
+ const headers: Record<string, string> = {
52
+ "X-API-Key": API_KEY!,
53
+ };
54
+
55
+ if (body) {
56
+ headers["Content-Type"] = "application/json";
57
+ }
58
+
59
+ const response = await fetch(url, {
60
+ method,
61
+ headers,
62
+ body: body ? JSON.stringify(body) : undefined,
63
+ });
64
+
65
+ if (!response.ok) {
66
+ const errorText = await response.text();
67
+ throw new Error(`API request failed: ${response.status} ${errorText}`);
68
+ }
69
+
70
+ return response.json() as Promise<T>;
71
+ }
72
+
73
+ // Tool definitions
74
+ const DEPLOY_PAGE_TOOL: Tool = {
75
+ name: "deploy-page",
76
+ description:
77
+ "Deploy or update a static HTML page. If the slug already exists, the page will be updated with the new content and title. IMPORTANT: The content must be a complete, valid HTML document with TailwindCSS for styling. JavaScript libraries are allowed but MUST be loaded via CDN (e.g., Alpine.js, Chart.js, GSAP, etc.). No build tools or npm packages. No React, Vue, or Angular frameworks allowed.",
78
+ inputSchema: {
79
+ type: "object",
80
+ properties: {
81
+ title: {
82
+ type: "string",
83
+ description: "The title of the page",
84
+ },
85
+ content: {
86
+ type: "string",
87
+ description: "The complete HTML content of the page. Must include <!DOCTYPE html>, <html>, <head> with TailwindCSS CDN link (https://cdn.tailwindcss.com), and <body> tags. Use TailwindCSS utility classes for all styling. JavaScript libraries are allowed but must be imported via CDN links in <script> tags (e.g., Alpine.js from CDN, Chart.js from CDN). Vanilla JavaScript is also acceptable. NO build tools, npm packages, or SPA frameworks (React/Vue/Angular).",
88
+ },
89
+ slug: {
90
+ type: "string",
91
+ description:
92
+ "The URL slug for the page (e.g., 'my-page-123'). This will be used in the URL as /pages/{slug}",
93
+ },
94
+ },
95
+ required: ["title", "content", "slug"],
96
+ },
97
+ };
98
+
99
+ const LIST_PAGES_TOOL: Tool = {
100
+ name: "list-pages",
101
+ description:
102
+ "List all pages associated with your API key. Returns a summary of each page including id, slug, title, and timestamps. Does not include page content.",
103
+ inputSchema: {
104
+ type: "object",
105
+ properties: {},
106
+ },
107
+ };
108
+
109
+ // Create MCP server
110
+ const server = new Server(
111
+ {
112
+ name: "gearvn-pages-mcp-server",
113
+ version: "1.0.0",
114
+ },
115
+ {
116
+ capabilities: {
117
+ tools: {},
118
+ },
119
+ }
120
+ );
121
+
122
+ // Handler for listing available tools
123
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
124
+ return {
125
+ tools: [DEPLOY_PAGE_TOOL, LIST_PAGES_TOOL],
126
+ };
127
+ });
128
+
129
+ // Handler for tool calls
130
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
131
+ const { name, arguments: args } = request.params;
132
+
133
+ try {
134
+ if (name === "deploy-page") {
135
+ const { title, content, slug } = args as unknown as DeployPageArgs;
136
+
137
+ // Validate inputs
138
+ if (!title || !content || !slug) {
139
+ throw new Error("Missing required parameters: title, content, and slug are required");
140
+ }
141
+
142
+ // Call the deploy API
143
+ const response = await callAPI<DeployPageResponse>("/v1/deploy", "POST", {
144
+ title,
145
+ content,
146
+ slug,
147
+ });
148
+
149
+ return {
150
+ content: [
151
+ {
152
+ type: "text",
153
+ text: JSON.stringify(
154
+ {
155
+ success: true,
156
+ message: "Page deployed successfully",
157
+ url: response.url,
158
+ },
159
+ null,
160
+ 2
161
+ ),
162
+ },
163
+ ],
164
+ };
165
+ } else if (name === "list-pages") {
166
+ // Call the list pages API
167
+ const response = await callAPI<ListPagesResponse>("/v1/pages", "GET");
168
+
169
+ return {
170
+ content: [
171
+ {
172
+ type: "text",
173
+ text: JSON.stringify(
174
+ {
175
+ success: true,
176
+ count: response.count,
177
+ pages: response.pages,
178
+ },
179
+ null,
180
+ 2
181
+ ),
182
+ },
183
+ ],
184
+ };
185
+ } else {
186
+ throw new Error(`Unknown tool: ${name}`);
187
+ }
188
+ } catch (error) {
189
+ const errorMessage = error instanceof Error ? error.message : String(error);
190
+ return {
191
+ content: [
192
+ {
193
+ type: "text",
194
+ text: JSON.stringify(
195
+ {
196
+ success: false,
197
+ error: errorMessage,
198
+ },
199
+ null,
200
+ 2
201
+ ),
202
+ },
203
+ ],
204
+ isError: true,
205
+ };
206
+ }
207
+ });
208
+
209
+ // Start the server
210
+ async function main() {
211
+ const transport = new StdioServerTransport();
212
+ await server.connect(transport);
213
+ console.error("GearVN Pages MCP Server running on stdio");
214
+ }
215
+
216
+ main().catch((error) => {
217
+ console.error("Fatal error:", error);
218
+ process.exit(1);
219
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }