@douyinfe/semi-mcp 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,38 +1,40 @@
1
+ [中文](README-zh_CN.md) | [English](README.md)
2
+
1
3
  # Semi MCP Server
2
4
 
3
- 基于 Model Context Protocol (MCP) SDK 实现的 MCP 服务器,提供 Semi Design 组件文档和组件列表查询功能。
5
+ An MCP (Model Context Protocol) server implementation based on the MCP SDK, providing Semi Design component documentation and component list query functionality.
4
6
 
5
- ## 简介
7
+ ## Introduction
6
8
 
7
- Semi MCP Server 是一个 MCP (Model Context Protocol) 服务器,通过 stdio 传输层与支持 MCP 协议的客户端通信。它提供了获取 Semi Design 组件文档、组件列表等功能。
9
+ Semi MCP Server is an MCP (Model Context Protocol) server that communicates with MCP-compatible clients through stdio transport. It provides functionality to fetch Semi Design component documentation, component lists, and more.
8
10
 
9
- ## 安装
11
+ ## Installation
10
12
 
11
- ### 全局安装
13
+ ### Global Installation
12
14
 
13
15
  ```bash
14
16
  npm install -g @douyinfe/semi-mcp
15
17
  ```
16
18
 
17
- ### 本地安装
19
+ ### Local Installation
18
20
 
19
21
  ```bash
20
22
  npm install @douyinfe/semi-mcp
21
23
  ```
22
24
 
23
- ## 使用方法
25
+ ## Usage
24
26
 
25
- ### 作为命令行工具
27
+ ### As a Command Line Tool
26
28
 
27
- 全局安装后,可以直接使用:
29
+ After global installation, you can use it directly:
28
30
 
29
31
  ```bash
30
32
  semi-mcp
31
33
  ```
32
34
 
33
- ### MCP 客户端中配置
35
+ ### Configuration in MCP Clients
34
36
 
35
- 在支持 MCP 的客户端(如 Claude Desktop)中配置:
37
+ Configure in MCP-compatible clients (such as Claude Desktop):
36
38
 
37
39
  ```json
38
40
  {
@@ -45,7 +47,7 @@ semi-mcp
45
47
  }
46
48
  ```
47
49
 
48
- 或者如果已全局安装:
50
+ Or if installed globally:
49
51
 
50
52
  ```json
51
53
  {
@@ -57,28 +59,29 @@ semi-mcp
57
59
  }
58
60
  ```
59
61
 
60
- ## 功能
62
+ ## Features
61
63
 
62
- ### 工具 (Tools)
64
+ ### Tools
63
65
 
64
66
  #### `get_semi_document`
65
67
 
66
- 获取 Semi Design 组件文档或组件列表。
68
+ Get Semi Design component documentation or component list.
67
69
 
68
- **参数:**
69
- - `componentName` (可选): 组件名称,例如 `Button`、`Input` 等。如果不提供,则返回组件列表
70
- - `version` (可选): 版本号,例如 `2.89.2-alpha.3`。如果不提供,默认使用 `latest`
70
+ **Parameters:**
71
+ - `componentName` (optional): Component name, e.g., `Button`, `Input`, etc. If not provided, returns the component list
72
+ - `version` (optional): Version number, e.g., `2.89.2-alpha.3`. If not provided, defaults to `latest`
73
+ - `get_path` (optional): If `true`, saves documents to the system temporary directory and returns the path instead of returning document content in the response. Defaults to `false`
71
74
 
72
- **示例:**
75
+ **Examples:**
73
76
 
74
- 获取组件列表:
77
+ Get component list:
75
78
  ```json
76
79
  {
77
80
  "name": "get_semi_document"
78
81
  }
79
82
  ```
80
83
 
81
- 获取指定组件文档:
84
+ Get specific component documentation:
82
85
  ```json
83
86
  {
84
87
  "name": "get_semi_document",
@@ -89,9 +92,9 @@ semi-mcp
89
92
  }
90
93
  ```
91
94
 
92
- **返回格式:**
95
+ **Response Format:**
93
96
 
94
- 获取组件列表时:
97
+ When getting component list:
95
98
  ```json
96
99
  {
97
100
  "version": "2.89.2-alpha.3",
@@ -100,7 +103,7 @@ semi-mcp
100
103
  }
101
104
  ```
102
105
 
103
- 获取组件文档时:
106
+ When getting component documentation:
104
107
  ```json
105
108
  {
106
109
  "componentName": "button",
@@ -113,91 +116,94 @@ semi-mcp
113
116
  }
114
117
  ```
115
118
 
116
- ### 资源 (Resources)
119
+ **Note:** For large documents (over 888 lines), the tool will automatically save them to a temporary directory and return the file paths instead of content.
120
+
121
+ ### Resources
117
122
 
118
123
  #### `semi://components`
119
124
 
120
- Semi Design 组件列表资源。
125
+ Semi Design component list resource.
121
126
 
122
- ## 开发
127
+ ## Development
123
128
 
124
- ### 环境要求
129
+ ### Requirements
125
130
 
126
131
  - Node.js >= 18.0.0
127
- - npm yarn
132
+ - npm or yarn
128
133
 
129
- ### 安装依赖
134
+ ### Install Dependencies
130
135
 
131
136
  ```bash
132
137
  npm install
133
138
  ```
134
139
 
135
- ### 构建
140
+ ### Build
136
141
 
137
- 构建生产版本:
142
+ Build production version:
138
143
 
139
144
  ```bash
140
145
  npm run build
141
146
  ```
142
147
 
143
- 开发模式(监听文件变化并自动重建):
148
+ Development mode (watch for file changes and auto-rebuild):
144
149
 
145
150
  ```bash
146
151
  npm run dev
147
152
  ```
148
153
 
149
- ### 测试
154
+ ### Test
150
155
 
151
- 运行测试:
156
+ Run tests:
152
157
 
153
158
  ```bash
154
159
  npm test
155
160
  ```
156
161
 
157
- ### 运行
162
+ ### Run
158
163
 
159
- 构建完成后运行服务器:
164
+ Run the server after building:
160
165
 
161
166
  ```bash
162
167
  npm start
163
168
  ```
164
169
 
165
- 或者直接运行构建后的文件:
170
+ Or run the built file directly:
166
171
 
167
172
  ```bash
168
173
  node dist/index.js
169
174
  ```
170
175
 
171
- ## 技术栈
176
+ ## Tech Stack
172
177
 
173
- - **TypeScript**: 类型安全的 JavaScript
174
- - **Rslib**: 快速构建工具
175
- - **@modelcontextprotocol/sdk**: MCP 官方 SDK
178
+ - **TypeScript**: Type-safe JavaScript
179
+ - **Rslib**: Fast build tool
180
+ - **@modelcontextprotocol/sdk**: Official MCP SDK
176
181
 
177
- ## 项目结构
182
+ ## Project Structure
178
183
 
179
184
  ```
180
185
  semi-mcp/
181
186
  ├── src/
182
- │ ├── index.ts # 主入口文件
183
- │ ├── tools/ # 工具定义
187
+ │ ├── index.ts # Main entry file
188
+ │ ├── tools/ # Tool definitions
184
189
  │ │ ├── index.ts
185
190
  │ │ └── get-semi-document.ts
186
- │ └── utils/ # 工具函数
191
+ │ └── utils/ # Utility functions
187
192
  │ ├── fetch-directory-list.ts
188
- └── fetch-file-content.ts
189
- ├── tests/ # 测试文件
193
+ ├── fetch-file-content.ts
194
+ │ └── get-component-list.ts
195
+ ├── tests/ # Test files
190
196
  │ └── get-semi-document.test.ts
191
- ├── dist/ # 构建输出
197
+ ├── dist/ # Build output
192
198
  ├── package.json
193
199
  └── README.md
194
200
  ```
195
201
 
196
- ## 许可证
202
+ ## License
197
203
 
198
204
  MIT
199
205
 
200
- ## 相关链接
206
+ ## Related Links
201
207
 
202
- - [Semi Design 官网](https://semi.design)
203
- - [Model Context Protocol 文档](https://modelcontextprotocol.io)
208
+ - [Semi Design Official Website](https://semi.design)
209
+ - [Model Context Protocol Documentation](https://modelcontextprotocol.io)
package/dist/index.js CHANGED
@@ -2,10 +2,79 @@
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
+ import { mkdir, writeFile } from "fs/promises";
6
+ import { join } from "path";
7
+ import { tmpdir } from "os";
5
8
  const UNPKG_BASE_URL = 'https://unpkg.com';
6
9
  const NPMMIRROR_BASE_URL = 'https://registry.npmmirror.com';
7
- async function fetchFromSource(baseUrl, packageName, version, path, isNpmMirror = false) {
8
- const url = isNpmMirror ? `${baseUrl}/${packageName}/${version}/files/${path}/?meta` : `${baseUrl}/${packageName}@${version}/${path}/?meta`;
10
+ function flattenDirectoryStructure(item, result = []) {
11
+ result.push({
12
+ path: item.path,
13
+ type: item.type,
14
+ size: item.size
15
+ });
16
+ if (item.files && Array.isArray(item.files)) for (const file of item.files)flattenDirectoryStructure(file, result);
17
+ return result;
18
+ }
19
+ async function fetchNpmMirrorDirectoryRecursive(baseUrl, packageName, version, path, maxDepth = 10) {
20
+ if (maxDepth <= 0) return [];
21
+ const url = `${baseUrl}/${packageName}/${version}/files/${path}/?meta`;
22
+ const response = await fetch(url, {
23
+ headers: {
24
+ Accept: 'application/json'
25
+ }
26
+ });
27
+ if (!response.ok) throw new Error(`获取目录列表失败: ${response.status} ${response.statusText}`);
28
+ const contentType = response.headers.get('content-type') || '';
29
+ if (!contentType.includes('application/json')) throw new Error(`API 返回了非 JSON 格式: ${contentType}`);
30
+ const data = await response.json();
31
+ const normalizeType = (item)=>{
32
+ const path = item.path;
33
+ if (path.endsWith('/')) return {
34
+ path,
35
+ type: 'directory'
36
+ };
37
+ if (item.type && item.type.includes('/')) return {
38
+ path,
39
+ type: 'file'
40
+ };
41
+ if ('directory' === item.type) return {
42
+ path,
43
+ type: 'directory'
44
+ };
45
+ return {
46
+ path,
47
+ type: 'file'
48
+ };
49
+ };
50
+ const result = [];
51
+ if (data && 'object' == typeof data && 'files' in data && Array.isArray(data.files)) {
52
+ const promises = [];
53
+ for (const item of data.files){
54
+ const normalized = normalizeType(item);
55
+ result.push(normalized);
56
+ if ('directory' !== normalized.type || item.files && 0 !== item.files.length) {
57
+ if (item.files && Array.isArray(item.files) && item.files.length > 0) {
58
+ const flattened = [];
59
+ flattenDirectoryStructure(item, flattened);
60
+ const subFiles = flattened.filter((f)=>f.path !== normalized.path).map(normalizeType);
61
+ result.push(...subFiles);
62
+ }
63
+ } else {
64
+ const subPath = normalized.path.startsWith('/') ? normalized.path.slice(1) : normalized.path;
65
+ promises.push(fetchNpmMirrorDirectoryRecursive(baseUrl, packageName, version, subPath, maxDepth - 1).then((subFiles)=>subFiles.filter((f)=>f.path !== normalized.path)).catch(()=>[]));
66
+ }
67
+ }
68
+ if (promises.length > 0) {
69
+ const subResults = await Promise.all(promises);
70
+ for (const subFiles of subResults)result.push(...subFiles);
71
+ }
72
+ }
73
+ return result;
74
+ }
75
+ async function fetchDirectoryListFromSource(baseUrl, packageName, version, path, isNpmMirror = false) {
76
+ if (isNpmMirror) return fetchNpmMirrorDirectoryRecursive(baseUrl, packageName, version, path);
77
+ const url = `${baseUrl}/${packageName}@${version}/${path}/?meta`;
9
78
  const response = await fetch(url, {
10
79
  headers: {
11
80
  Accept: 'application/json'
@@ -35,15 +104,66 @@ async function fetchFromSource(baseUrl, packageName, version, path, isNpmMirror
35
104
  };
36
105
  };
37
106
  if (Array.isArray(data)) return data.map(normalizeType);
38
- if (data && 'object' == typeof data && 'files' in data && Array.isArray(data.files)) return data.files.map(normalizeType);
39
- if (data && 'object' == typeof data && 'path' in data) return [
40
- normalizeType(data)
41
- ];
107
+ if (data && 'object' == typeof data && 'files' in data) {
108
+ const filesData = data;
109
+ if (Array.isArray(filesData.files)) return filesData.files.map(normalizeType);
110
+ }
111
+ if (data && 'object' == typeof data && 'path' in data) {
112
+ const singleItem = data;
113
+ if (singleItem.files && Array.isArray(singleItem.files)) {
114
+ const flattened = [];
115
+ flattenDirectoryStructure(singleItem, flattened);
116
+ return flattened.map(normalizeType);
117
+ }
118
+ return [
119
+ normalizeType(singleItem)
120
+ ];
121
+ }
42
122
  throw new Error('无法解析目录列表数据格式');
43
123
  }
44
124
  async function fetchDirectoryList(packageName, version, path) {
45
- const unpkgPromise = fetchFromSource(UNPKG_BASE_URL, packageName, version, path, false);
46
- const npmmirrorPromise = fetchFromSource(NPMMIRROR_BASE_URL, packageName, version, path, true);
125
+ const unpkgPromise = fetchDirectoryListFromSource(UNPKG_BASE_URL, packageName, version, path, false);
126
+ const npmmirrorPromise = fetchDirectoryListFromSource(NPMMIRROR_BASE_URL, packageName, version, path, true);
127
+ const results = await Promise.allSettled([
128
+ unpkgPromise,
129
+ npmmirrorPromise
130
+ ]);
131
+ const successfulResults = [];
132
+ const errors = [];
133
+ if ('fulfilled' === results[0].status) successfulResults.push({
134
+ source: 'unpkg',
135
+ files: results[0].value
136
+ });
137
+ else errors.push(results[0].reason instanceof Error ? results[0].reason : new Error(String(results[0].reason)));
138
+ if ('fulfilled' === results[1].status) successfulResults.push({
139
+ source: 'npmmirror',
140
+ files: results[1].value
141
+ });
142
+ else errors.push(results[1].reason instanceof Error ? results[1].reason : new Error(String(results[1].reason)));
143
+ if (0 === successfulResults.length) throw new Error(`所有数据源都失败了: ${errors.map((e)=>e.message).join('; ')}`);
144
+ successfulResults.sort((a, b)=>{
145
+ if (b.files.length !== a.files.length) return b.files.length - a.files.length;
146
+ return 'unpkg' === a.source ? -1 : 1;
147
+ });
148
+ return successfulResults[0].files;
149
+ }
150
+ const fetch_file_content_UNPKG_BASE_URL = 'https://unpkg.com';
151
+ const fetch_file_content_NPMMIRROR_BASE_URL = 'https://registry.npmmirror.com';
152
+ async function fetchFileContentFromSource(baseUrl, packageName, version, filePath, isNpmMirror = false) {
153
+ const url = isNpmMirror ? `${baseUrl}/${packageName}/${version}/files/${filePath}` : `${baseUrl}/${packageName}@${version}/${filePath}`;
154
+ const response = await fetch(url, {
155
+ headers: {
156
+ Accept: 'text/plain, application/json, */*'
157
+ }
158
+ });
159
+ if (!response.ok) throw new Error(`获取文件失败: ${response.status} ${response.statusText}`);
160
+ const content = await response.text();
161
+ if (content.trim().startsWith('<!DOCTYPE html>') || content.includes('npmmirror 镜像站')) throw new Error('返回了 HTML 错误页面');
162
+ return content;
163
+ }
164
+ async function fetchFileContent(packageName, version, filePath) {
165
+ const unpkgPromise = fetchFileContentFromSource(fetch_file_content_UNPKG_BASE_URL, packageName, version, filePath, false);
166
+ const npmmirrorPromise = fetchFileContentFromSource(fetch_file_content_NPMMIRROR_BASE_URL, packageName, version, filePath, true);
47
167
  const unpkgWithFallback = unpkgPromise.catch(()=>new Promise(()=>{}));
48
168
  const npmmirrorWithFallback = npmmirrorPromise.catch(()=>new Promise(()=>{}));
49
169
  const raceResult = await Promise.race([
@@ -91,6 +211,11 @@ const getSemiDocumentTool = {
91
211
  version: {
92
212
  type: 'string',
93
213
  description: '版本号,例如 2.89.1。如果不提供,默认使用 latest'
214
+ },
215
+ get_path: {
216
+ type: 'boolean',
217
+ description: '如果为 true,将文档写入操作系统临时目录并返回路径,而不是在响应中返回文档内容。默认为 false',
218
+ default: false
94
219
  }
95
220
  },
96
221
  required: []
@@ -117,18 +242,36 @@ async function getComponentDocuments(componentName, version) {
117
242
  }
118
243
  if (-1 === categoryIndex || categoryIndex >= pathParts.length) return null;
119
244
  const category = pathParts[categoryIndex];
120
- const documents = componentFiles.map((file)=>{
245
+ const documentPromises = componentFiles.map(async (file)=>{
246
+ const filePath = file.path.startsWith('/') ? file.path.slice(1) : file.path;
121
247
  const parts = file.path.split('/');
122
- return parts[parts.length - 1].toLowerCase();
123
- }).filter((name)=>name);
248
+ const fileName = parts[parts.length - 1];
249
+ try {
250
+ const content = await fetchFileContent(packageName, version, filePath);
251
+ return {
252
+ name: fileName,
253
+ path: file.path,
254
+ content: content
255
+ };
256
+ } catch (error) {
257
+ const errorMessage = error instanceof Error ? error.message : String(error);
258
+ return {
259
+ name: fileName,
260
+ path: file.path,
261
+ content: `获取文档内容失败: ${errorMessage}`
262
+ };
263
+ }
264
+ });
265
+ const documents = await Promise.all(documentPromises);
124
266
  return {
125
267
  category,
126
- documents: Array.from(new Set(documents)).sort()
268
+ documents: documents.sort((a, b)=>a.name.localeCompare(b.name))
127
269
  };
128
270
  }
129
271
  async function handleGetSemiDocument(args) {
130
272
  const componentName = args?.componentName;
131
273
  const version = args?.version || 'latest';
274
+ const getPath = args?.get_path || false;
132
275
  try {
133
276
  if (componentName) {
134
277
  const result = await getComponentDocuments(componentName, version);
@@ -149,6 +292,60 @@ async function handleGetSemiDocument(args) {
149
292
  }
150
293
  ]
151
294
  };
295
+ const documentsWithLines = result.documents.map((doc)=>({
296
+ ...doc,
297
+ lines: doc.content.split('\n').length
298
+ }));
299
+ const hasLargeDocument = documentsWithLines.some((doc)=>doc.lines > 888);
300
+ const userExplicitlySetGetPath = 'get_path' in args;
301
+ const shouldUsePath = getPath || hasLargeDocument && !userExplicitlySetGetPath;
302
+ if (shouldUsePath) {
303
+ const baseTempDir = tmpdir();
304
+ const tempDirName = `semi-docs-${componentName.toLowerCase()}-${version}-${Date.now()}`;
305
+ const tempDir = join(baseTempDir, tempDirName);
306
+ await mkdir(tempDir, {
307
+ recursive: true
308
+ });
309
+ const filePaths = [];
310
+ for (const doc of result.documents){
311
+ const filePath = join(tempDir, doc.name);
312
+ await writeFile(filePath, doc.content, 'utf-8');
313
+ filePaths.push(filePath);
314
+ }
315
+ const largeDocs = documentsWithLines.filter((doc)=>doc.lines > 888);
316
+ let message = `文档已保存到临时目录: ${tempDir}\n请使用文件读取工具查看文档内容。`;
317
+ if (hasLargeDocument && !userExplicitlySetGetPath) {
318
+ const largeDocNames = largeDocs.map((doc)=>`${doc.name} (${doc.lines.toLocaleString()} 行)`).join(', ');
319
+ message = `文档已保存到临时目录: ${tempDir}\n注意:以下文档文件较大,已自动保存到临时目录:${largeDocNames}\n请使用文件读取工具查看文档内容。`;
320
+ } else if (hasLargeDocument) {
321
+ const largeDocNames = largeDocs.map((doc)=>`${doc.name} (${doc.lines.toLocaleString()} 行)`).join(', ');
322
+ message = `文档已保存到临时目录: ${tempDir}\n注意:以下文档文件较大:${largeDocNames}\n请使用文件读取工具查看文档内容。`;
323
+ }
324
+ return {
325
+ content: [
326
+ {
327
+ type: 'text',
328
+ text: JSON.stringify({
329
+ componentName: componentName.toLowerCase(),
330
+ version,
331
+ category: result.category,
332
+ tempDirectory: tempDir,
333
+ files: documentsWithLines.map((doc)=>({
334
+ name: doc.name,
335
+ path: join(tempDir, doc.name),
336
+ contentLength: doc.content.length,
337
+ lines: doc.lines
338
+ })),
339
+ count: result.documents.length,
340
+ message,
341
+ autoGetPath: hasLargeDocument && !userExplicitlySetGetPath,
342
+ allComponents,
343
+ allComponentsCount: allComponents.length
344
+ }, null, 2)
345
+ }
346
+ ]
347
+ };
348
+ }
152
349
  return {
153
350
  content: [
154
351
  {
@@ -157,7 +354,16 @@ async function handleGetSemiDocument(args) {
157
354
  componentName: componentName.toLowerCase(),
158
355
  version,
159
356
  category: result.category,
160
- documents: result.documents,
357
+ documents: result.documents.map((doc)=>({
358
+ name: doc.name,
359
+ path: doc.path,
360
+ contentLength: doc.content.length
361
+ })),
362
+ contents: result.documents.map((doc)=>({
363
+ name: doc.name,
364
+ path: doc.path,
365
+ content: doc.content
366
+ })),
161
367
  count: result.documents.length,
162
368
  allComponents,
163
369
  allComponentsCount: allComponents.length
@@ -2,9 +2,19 @@
2
2
  * 从 unpkg 或 npmmirror 获取目录列表
3
3
  * 同时向两个数据源发送请求,使用第一个成功返回的结果
4
4
  */
5
+ export declare const UNPKG_BASE_URL = "https://unpkg.com";
6
+ export declare const NPMMIRROR_BASE_URL = "https://registry.npmmirror.com";
7
+ /**
8
+ * 从单个源获取目录列表
9
+ * 导出用于测试
10
+ */
11
+ export declare function fetchDirectoryListFromSource(baseUrl: string, packageName: string, version: string, path: string, isNpmMirror?: boolean): Promise<Array<{
12
+ path: string;
13
+ type: string;
14
+ }>>;
5
15
  /**
6
16
  * 从 unpkg 或 npmmirror 获取目录列表
7
- * 同时向两个数据源发送请求,使用第一个成功返回的结果
17
+ * 同时向两个数据源发送请求,优先使用返回更多文件的结果
8
18
  */
9
19
  export declare function fetchDirectoryList(packageName: string, version: string, path: string): Promise<Array<{
10
20
  path: string;
@@ -2,6 +2,13 @@
2
2
  * 从 unpkg 或 npmmirror 获取具体文件内容
3
3
  * 同时向两个数据源发送请求,使用第一个成功返回的结果
4
4
  */
5
+ export declare const UNPKG_BASE_URL = "https://unpkg.com";
6
+ export declare const NPMMIRROR_BASE_URL = "https://registry.npmmirror.com";
7
+ /**
8
+ * 从单个源获取文件内容
9
+ * 导出用于测试
10
+ */
11
+ export declare function fetchFileContentFromSource(baseUrl: string, packageName: string, version: string, filePath: string, isNpmMirror?: boolean): Promise<string>;
5
12
  /**
6
13
  * 从 unpkg 或 npmmirror 获取具体文件内容
7
14
  * 同时向两个数据源发送请求,使用第一个成功返回的结果
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@douyinfe/semi-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Semi Design MCP Server - Model Context Protocol server for Semi Design components and documentation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",