ai-yuca 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.
Files changed (62) hide show
  1. package/.eslintrc.js +25 -0
  2. package/CONFIG_UPLOAD.md +154 -0
  3. package/CONTRIBUTING.md +58 -0
  4. package/INSTALLATION.md +192 -0
  5. package/README.md +80 -0
  6. package/bin/cli.js +85 -0
  7. package/bin/cli.ts +302 -0
  8. package/dist/bin/cli.d.ts +2 -0
  9. package/dist/bin/cli.js +297 -0
  10. package/dist/package.json +51 -0
  11. package/dist/src/config.d.ts +56 -0
  12. package/dist/src/config.js +101 -0
  13. package/dist/src/download.d.ts +30 -0
  14. package/dist/src/download.js +214 -0
  15. package/dist/src/index.d.ts +18 -0
  16. package/dist/src/index.js +126 -0
  17. package/dist/src/types/analyze.d.ts +33 -0
  18. package/dist/src/types/analyze.js +5 -0
  19. package/dist/src/types/download.d.ts +60 -0
  20. package/dist/src/types/download.js +2 -0
  21. package/dist/src/types/index.d.ts +8 -0
  22. package/dist/src/types/index.js +28 -0
  23. package/dist/src/types/upload.d.ts +89 -0
  24. package/dist/src/types/upload.js +2 -0
  25. package/dist/src/upload.d.ts +24 -0
  26. package/dist/src/upload.js +252 -0
  27. package/dist/src/uploadWithConfig.d.ts +34 -0
  28. package/dist/src/uploadWithConfig.js +82 -0
  29. package/dist/src/utils/compression.d.ts +16 -0
  30. package/dist/src/utils/compression.js +85 -0
  31. package/dist/test/compression.test.d.ts +1 -0
  32. package/dist/test/compression.test.js +109 -0
  33. package/dist/test/download.test.d.ts +1 -0
  34. package/dist/test/download.test.js +168 -0
  35. package/dist/test/index.test.d.ts +1 -0
  36. package/dist/test/index.test.js +33 -0
  37. package/dist/test/upload.test.d.ts +1 -0
  38. package/dist/test/upload.test.js +140 -0
  39. package/docs/usage.md +223 -0
  40. package/examples/sample.txt +7 -0
  41. package/examples/upload-example.js +53 -0
  42. package/out/test.txt +1 -0
  43. package/package.json +51 -0
  44. package/src/config.ts +104 -0
  45. package/src/download.ts +216 -0
  46. package/src/index.js +88 -0
  47. package/src/index.ts +98 -0
  48. package/src/types/analyze.ts +37 -0
  49. package/src/types/download.ts +67 -0
  50. package/src/types/index.ts +16 -0
  51. package/src/types/upload.ts +97 -0
  52. package/src/upload.js +197 -0
  53. package/src/upload.ts +254 -0
  54. package/src/uploadWithConfig.ts +122 -0
  55. package/src/utils/compression.ts +61 -0
  56. package/test/compression.test.ts +88 -0
  57. package/test/download.test.ts +162 -0
  58. package/test/index.test.js +38 -0
  59. package/test/index.test.ts +39 -0
  60. package/test/upload.test.ts +131 -0
  61. package/tsconfig.json +17 -0
  62. package/vs.config.json +42 -0
@@ -0,0 +1,7 @@
1
+ 这是一个示例文本文件,用于测试AI-Yuca工具的文本分析功能。
2
+
3
+ AI-Yuca是一个简单而强大的文本分析工具,它可以帮助用户快速获取文本的基本统计信息,如字符数、单词数和行数。
4
+
5
+ 此外,AI-Yuca还能够提取文本中的关键词,帮助用户了解文本的主要内容和焦点。
6
+
7
+ 希望这个工具能够为您的文本分析工作带来便利!
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 上传功能示例
3
+ */
4
+ const { createStorageClient, uploadFiles } = require('../src/upload');
5
+
6
+ async function uploadExample() {
7
+ try {
8
+ // 创建存储客户端
9
+ const storageClient = createStorageClient({
10
+ keyFilename: './path/to/your-keyfile.json'
11
+ });
12
+
13
+ // 上传单个文件
14
+ console.log('上传单个文件...');
15
+ const singleFileResult = await uploadFiles({
16
+ bucketName: 'your-bucket-name',
17
+ sourcePath: './examples/sample.txt',
18
+ destination: 'uploads/sample.txt',
19
+ storageClient
20
+ });
21
+
22
+ console.log('单个文件上传结果:', JSON.stringify(singleFileResult, null, 2));
23
+
24
+ // 批量上传多个文件
25
+ console.log('\n批量上传多个文件...');
26
+ const multipleFilesResult = await uploadFiles({
27
+ bucketName: 'your-bucket-name',
28
+ sourcePath: ['./examples/sample.txt', './README.md'],
29
+ destination: 'uploads',
30
+ storageClient
31
+ });
32
+
33
+ console.log('批量上传结果:', JSON.stringify(multipleFilesResult, null, 2));
34
+
35
+ // 递归上传目录
36
+ console.log('\n递归上传目录...');
37
+ const directoryResult = await uploadFiles({
38
+ bucketName: 'your-bucket-name',
39
+ sourcePath: './examples',
40
+ destination: 'uploads/examples',
41
+ storageClient,
42
+ recursive: true
43
+ });
44
+
45
+ console.log('目录上传结果:', JSON.stringify(directoryResult, null, 2));
46
+
47
+ } catch (error) {
48
+ console.error('上传示例错误:', error.message);
49
+ }
50
+ }
51
+
52
+ // 运行示例
53
+ uploadExample();
package/out/test.txt ADDED
@@ -0,0 +1 @@
1
+ Hello World from config upload test
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "ai-yuca",
3
+ "version": "1.0.0",
4
+ "description": "一个实用的AI辅助工具",
5
+ "main": "dist/src/index.js",
6
+ "types": "dist/src/index.d.ts",
7
+ "bin": {
8
+ "ai-yuca": "./dist/bin/cli.js"
9
+ },
10
+ "directories": {
11
+ "test": "test"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "mocha -r ts-node/register 'test/**/*.ts'",
16
+ "start": "ts-node src/index.ts",
17
+ "lint": "eslint . --ext .ts",
18
+ "dev": "ts-node bin/cli.ts",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "ai",
23
+ "tool",
24
+ "utility",
25
+ "cli"
26
+ ],
27
+ "author": "",
28
+ "license": "MIT",
29
+ "engines": {
30
+ "node": ">=14.0.0"
31
+ },
32
+ "dependencies": {
33
+ "@google-cloud/storage": "^7.17.1",
34
+ "commander": "^10.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/chai": "^5.2.2",
38
+ "@types/google-cloud__storage": "^2.3.1",
39
+ "@types/mocha": "^10.0.10",
40
+ "@types/node": "^24.3.1",
41
+ "@types/sinon": "^17.0.4",
42
+ "@typescript-eslint/eslint-plugin": "^8.43.0",
43
+ "@typescript-eslint/parser": "^8.43.0",
44
+ "chai": "^4.3.4",
45
+ "eslint": "^9.35.0",
46
+ "mocha": "^11.7.2",
47
+ "sinon": "^21.0.0",
48
+ "ts-node": "^10.9.2",
49
+ "typescript": "^5.9.2"
50
+ }
51
+ }
package/src/config.ts ADDED
@@ -0,0 +1,104 @@
1
+ /**
2
+ * 配置文件读取模块
3
+ */
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+
7
+ /**
8
+ * 配置文件接口
9
+ */
10
+ export interface VSConfig {
11
+ upload: {
12
+ uploadPath: string;
13
+ s3Static: string;
14
+ };
15
+ aws: {
16
+ Bucket: string;
17
+ prefix: string;
18
+ Region: string;
19
+ HostName: string;
20
+ };
21
+ deploy?: {
22
+ baseUrl: string[];
23
+ host: string;
24
+ testHost: string;
25
+ };
26
+ crowdin?: {
27
+ project: string;
28
+ langMap: string[];
29
+ workDir: string;
30
+ reg: string;
31
+ keysDir: string;
32
+ Bucket: string;
33
+ prefix: string;
34
+ Region: string;
35
+ FromIni: string;
36
+ HostName: string;
37
+ };
38
+ }
39
+
40
+ /**
41
+ * 读取配置文件
42
+ * @param configPath - 配置文件路径,默认为项目根目录下的vs.config.json
43
+ * @returns 配置对象
44
+ */
45
+ export function loadConfig(configPath?: string): VSConfig {
46
+ const defaultConfigPath = path.join(process.cwd(), 'vs.config.json');
47
+ const finalConfigPath = configPath || defaultConfigPath;
48
+
49
+ if (!fs.existsSync(finalConfigPath)) {
50
+ throw new Error(`配置文件不存在: ${finalConfigPath}`);
51
+ }
52
+
53
+ try {
54
+ const configContent = fs.readFileSync(finalConfigPath, 'utf-8');
55
+ const config = JSON.parse(configContent) as VSConfig;
56
+
57
+ // 验证必要的配置项
58
+ if (!config.upload || !config.aws) {
59
+ throw new Error('配置文件缺少必要的upload或aws配置项');
60
+ }
61
+
62
+ if (!config.upload.uploadPath || !config.upload.s3Static) {
63
+ throw new Error('配置文件缺少必要的upload.uploadPath或upload.s3Static配置项');
64
+ }
65
+
66
+ if (!config.aws.Bucket || !config.aws.prefix) {
67
+ throw new Error('配置文件缺少必要的aws.Bucket或aws.prefix配置项');
68
+ }
69
+
70
+ return config;
71
+ } catch (error) {
72
+ if (error instanceof SyntaxError) {
73
+ throw new Error(`配置文件格式错误: ${error.message}`);
74
+ }
75
+ throw error;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * 获取上传桶名称
81
+ * @param config - 配置对象
82
+ * @returns 桶名称
83
+ */
84
+ export function getBucketName(config: VSConfig): string {
85
+ return config.aws.Bucket;
86
+ }
87
+
88
+ /**
89
+ * 获取上传目标路径
90
+ * @param config - 配置对象
91
+ * @returns 目标路径
92
+ */
93
+ export function getUploadDestination(config: VSConfig): string {
94
+ return `${config.aws.prefix}/${config.upload.s3Static}`;
95
+ }
96
+
97
+ /**
98
+ * 获取本地上传源路径
99
+ * @param config - 配置对象
100
+ * @returns 本地源路径
101
+ */
102
+ export function getUploadSourcePath(config: VSConfig): string {
103
+ return path.join(process.cwd(), config.upload.uploadPath);
104
+ }
@@ -0,0 +1,216 @@
1
+ /**
2
+ * GCP下载功能模块
3
+ */
4
+ import { Storage, Bucket, File } from '@google-cloud/storage';
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import * as util from 'util';
8
+ import {
9
+ DownloadFileOptions,
10
+ DownloadFilesOptions,
11
+ DownloadSuccessResult,
12
+ DownloadFailedResult,
13
+ DownloadResult,
14
+ DownloadFilesResult
15
+ } from './types/download';
16
+ import { StorageClientOptions } from './types/upload';
17
+
18
+ const mkdir = util.promisify(fs.mkdir);
19
+
20
+ /**
21
+ * 创建GCP存储客户端
22
+ * @param options - 配置选项
23
+ * @returns GCP存储客户端实例
24
+ */
25
+ function createStorageClient(options: Partial<StorageClientOptions> = {}): Storage {
26
+ const { keyFilename, projectId, credentials } = options;
27
+
28
+ // 如果提供了keyFilename,使用密钥文件认证
29
+ if (keyFilename) {
30
+ return new Storage({
31
+ keyFilename
32
+ });
33
+ }
34
+
35
+ // 如果提供了credentials,使用凭证对象认证
36
+ if (credentials) {
37
+ return new Storage({
38
+ projectId,
39
+ credentials
40
+ });
41
+ }
42
+
43
+ // 使用应用默认凭证(ADC)进行免密认证
44
+ return new Storage();
45
+ }
46
+
47
+ /**
48
+ * 确保目录存在,如果不存在则创建
49
+ * @param dirPath - 目录路径
50
+ */
51
+ async function ensureDirectoryExists(dirPath: string): Promise<void> {
52
+ try {
53
+ if (!fs.existsSync(dirPath)) {
54
+ await mkdir(dirPath, { recursive: true });
55
+ }
56
+ } catch (error) {
57
+ throw new Error(`创建目录失败: ${error instanceof Error ? error.message : String(error)}`);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * 下载单个文件从GCP存储桶
63
+ * @param options - 下载选项
64
+ * @returns 下载结果
65
+ */
66
+ async function downloadFile(options: DownloadFileOptions): Promise<DownloadResult> {
67
+ const { bucketName, sourcePath, destinationPath, storageClient } = options;
68
+
69
+ if (!bucketName || !sourcePath || !destinationPath || !storageClient) {
70
+ throw new Error('缺少必要的下载参数');
71
+ }
72
+
73
+ try {
74
+ // 获取存储桶
75
+ const bucket: Bucket = storageClient.bucket(bucketName);
76
+ const file: File = bucket.file(sourcePath);
77
+
78
+ // 检查文件是否存在
79
+ const [exists] = await file.exists();
80
+ if (!exists) {
81
+ throw new Error(`文件不存在: ${sourcePath}`);
82
+ }
83
+
84
+ // 确保目标目录存在
85
+ const destDir = path.dirname(destinationPath);
86
+ await ensureDirectoryExists(destDir);
87
+
88
+ // 下载文件
89
+ await file.download({
90
+ destination: destinationPath
91
+ });
92
+
93
+ return {
94
+ success: true,
95
+ file: sourcePath,
96
+ localPath: destinationPath
97
+ };
98
+ } catch (error) {
99
+ return {
100
+ success: false,
101
+ file: sourcePath,
102
+ error: error instanceof Error ? error.message : String(error)
103
+ };
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 列出存储桶中的文件和目录
109
+ * @param bucket - 存储桶
110
+ * @param prefix - 前缀路径
111
+ * @returns 文件和目录列表
112
+ */
113
+ async function listFiles(bucket: Bucket, prefix: string = ''): Promise<string[]> {
114
+ try {
115
+ const options = {
116
+ prefix: prefix,
117
+ delimiter: '/'
118
+ };
119
+
120
+ const [files, , apiResponse] = await bucket.getFiles(options);
121
+
122
+ // 获取文件路径
123
+ const filePaths = files.map(file => file.name);
124
+
125
+ // 获取目录前缀
126
+ const prefixes = apiResponse && typeof apiResponse === 'object' && 'prefixes' in apiResponse
127
+ ? (apiResponse.prefixes as string[] || [])
128
+ : [];
129
+
130
+ return [...filePaths, ...prefixes];
131
+ } catch (error) {
132
+ throw new Error(`列出文件失败: ${error instanceof Error ? error.message : String(error)}`);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * 批量下载文件从GCP存储桶
138
+ * @param options - 下载选项
139
+ * @returns 下载结果
140
+ */
141
+ async function downloadFiles(options: DownloadFilesOptions): Promise<DownloadFilesResult> {
142
+ const { bucketName, sourcePath, destinationPath, storageClient, recursive = false } = options;
143
+
144
+ if (!bucketName || !sourcePath || !destinationPath || !storageClient) {
145
+ throw new Error('缺少必要的下载参数');
146
+ }
147
+
148
+ const results: DownloadFilesResult = {
149
+ success: [],
150
+ failed: []
151
+ };
152
+
153
+ try {
154
+ // 获取存储桶
155
+ const bucket: Bucket = storageClient.bucket(bucketName);
156
+
157
+ // 确保目标目录存在
158
+ await ensureDirectoryExists(destinationPath);
159
+
160
+ // 列出源路径下的所有文件
161
+ const files = await listFiles(bucket, sourcePath);
162
+
163
+ for (const file of files) {
164
+ // 如果是目录(以/结尾)
165
+ if (file.endsWith('/')) {
166
+ if (recursive) {
167
+ // 递归下载子目录
168
+ const subSourcePath = file;
169
+ const subDestinationPath = path.join(destinationPath, path.basename(file));
170
+
171
+ const subResults = await downloadFiles({
172
+ bucketName,
173
+ sourcePath: subSourcePath,
174
+ destinationPath: subDestinationPath,
175
+ storageClient,
176
+ recursive
177
+ });
178
+
179
+ results.success = results.success.concat(subResults.success);
180
+ results.failed = results.failed.concat(subResults.failed);
181
+ }
182
+ } else {
183
+ // 下载文件
184
+ const relativePath = file.startsWith(sourcePath)
185
+ ? file.substring(sourcePath.length)
186
+ : file;
187
+
188
+ const destFilePath = path.join(destinationPath, relativePath);
189
+
190
+ const result = await downloadFile({
191
+ bucketName,
192
+ sourcePath: file,
193
+ destinationPath: destFilePath,
194
+ storageClient
195
+ });
196
+
197
+ if (result.success) {
198
+ results.success.push(result);
199
+ } else {
200
+ results.failed.push(result);
201
+ }
202
+ }
203
+ }
204
+
205
+ return results;
206
+ } catch (error) {
207
+ throw new Error(`下载文件失败: ${error instanceof Error ? error.message : String(error)}`);
208
+ }
209
+ }
210
+
211
+ export {
212
+ createStorageClient,
213
+ downloadFile,
214
+ downloadFiles,
215
+ ensureDirectoryExists
216
+ };
package/src/index.js ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * AI-Yuca 核心功能模块
3
+ */
4
+
5
+ /**
6
+ * 分析文本内容
7
+ * @param {Object} options - 分析选项
8
+ * @param {string} [options.file] - 要分析的文件路径
9
+ * @param {string} [options.text] - 要分析的文本内容
10
+ * @returns {Object} 分析结果
11
+ */
12
+ function analyze(options) {
13
+ const { file, text } = options;
14
+
15
+ if (!file && !text) {
16
+ console.error('错误: 必须提供文件路径或文本内容');
17
+ process.exit(1);
18
+ }
19
+
20
+ let content = '';
21
+
22
+ if (file) {
23
+ try {
24
+ const fs = require('fs');
25
+ content = fs.readFileSync(file, 'utf8');
26
+ } catch (err) {
27
+ console.error(`错误: 无法读取文件 ${file}`);
28
+ console.error(err.message);
29
+ process.exit(1);
30
+ }
31
+ } else if (text) {
32
+ content = text;
33
+ }
34
+
35
+ // 执行分析
36
+ const result = analyzeContent(content);
37
+
38
+ // 输出结果
39
+ console.log('分析结果:');
40
+ console.log(JSON.stringify(result, null, 2));
41
+
42
+ return result;
43
+ }
44
+
45
+ /**
46
+ * 分析文本内容的核心功能
47
+ * @param {string} content - 要分析的文本内容
48
+ * @returns {Object} 分析结果
49
+ */
50
+ function analyzeContent(content) {
51
+ // 基本统计
52
+ const charCount = content.length;
53
+ // 改进的单词计数,同时支持英文和中文
54
+ const wordCount = content.match(/[\w\u4e00-\u9fa5]+/g)?.length || 0;
55
+ const lineCount = content.split('\n').length;
56
+
57
+ // 关键词提取 (简单实现)
58
+ const words = content.toLowerCase()
59
+ .replace(/[^\w\s]/g, '')
60
+ .split(/\s+/)
61
+ .filter(word => word.length > 3);
62
+
63
+ const wordFrequency = {};
64
+ words.forEach(word => {
65
+ wordFrequency[word] = (wordFrequency[word] || 0) + 1;
66
+ });
67
+
68
+ // 获取前5个最常见的词
69
+ const topKeywords = Object.entries(wordFrequency)
70
+ .sort((a, b) => b[1] - a[1])
71
+ .slice(0, 5)
72
+ .map(([word, count]) => ({ word, count }));
73
+
74
+ return {
75
+ statistics: {
76
+ charCount,
77
+ wordCount,
78
+ lineCount
79
+ },
80
+ keywords: topKeywords,
81
+ summary: `文本包含 ${charCount} 个字符, ${wordCount} 个单词, ${lineCount} 行。`
82
+ };
83
+ }
84
+
85
+ module.exports = {
86
+ analyze,
87
+ analyzeContent
88
+ };
package/src/index.ts ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * AI-Yuca 核心功能模块
3
+ */
4
+ import * as fs from 'fs';
5
+ import {
6
+ AnalyzeOptions,
7
+ Statistics,
8
+ Keyword,
9
+ AnalysisResult
10
+ } from './types/analyze';
11
+
12
+ /**
13
+ * 分析文本内容
14
+ * @param options - 分析选项
15
+ * @returns 分析结果
16
+ */
17
+ function analyze(options: AnalyzeOptions): AnalysisResult {
18
+ const { file, text } = options;
19
+
20
+ if (!file && !text) {
21
+ console.error('错误: 必须提供文件路径或文本内容');
22
+ process.exit(1);
23
+ }
24
+
25
+ let content = '';
26
+
27
+ if (file) {
28
+ try {
29
+ content = fs.readFileSync(file, 'utf8');
30
+ } catch (err) {
31
+ console.error(`错误: 无法读取文件 ${file}`);
32
+ if (err instanceof Error) {
33
+ console.error(err.message);
34
+ }
35
+ process.exit(1);
36
+ }
37
+ } else if (text) {
38
+ content = text;
39
+ }
40
+
41
+ // 执行分析
42
+ const result = analyzeContent(content);
43
+
44
+ // 输出结果
45
+ console.log('分析结果:');
46
+ console.log(JSON.stringify(result, null, 2));
47
+
48
+ return result;
49
+ }
50
+
51
+ /**
52
+ * 分析文本内容的核心功能
53
+ * @param content - 要分析的文本内容
54
+ * @returns 分析结果
55
+ */
56
+ function analyzeContent(content: string): AnalysisResult {
57
+ // 基本统计
58
+ const charCount = content.length;
59
+ // 改进的单词计数,同时支持英文和中文
60
+ const wordCount = content.match(/[\w\u4e00-\u9fa5]+/g)?.length || 0;
61
+ const lineCount = content.split('\n').length;
62
+
63
+ // 关键词提取 (简单实现)
64
+ const words = content.toLowerCase()
65
+ .replace(/[^\w\s]/g, '')
66
+ .split(/\s+/)
67
+ .filter(word => word.length > 3);
68
+
69
+ const wordFrequency: Record<string, number> = {};
70
+ words.forEach(word => {
71
+ wordFrequency[word] = (wordFrequency[word] || 0) + 1;
72
+ });
73
+
74
+ // 获取前5个最常见的词
75
+ const topKeywords = Object.entries(wordFrequency)
76
+ .sort((a, b) => b[1] - a[1])
77
+ .slice(0, 5)
78
+ .map(([word, count]) => ({ word, count }));
79
+
80
+ return {
81
+ statistics: {
82
+ charCount,
83
+ wordCount,
84
+ lineCount
85
+ },
86
+ keywords: topKeywords,
87
+ summary: `文本包含 ${charCount} 个字符, ${wordCount} 个单词, ${lineCount} 行。`
88
+ };
89
+ }
90
+
91
+ export {
92
+ analyze,
93
+ analyzeContent
94
+ };
95
+ export { createStorageClient, uploadFile, uploadFiles } from './upload';
96
+ export { downloadFiles } from './download';
97
+ export { uploadFilesWithConfig, getConfigSummary } from './uploadWithConfig';
98
+ export { loadConfig, getBucketName, getUploadDestination, getUploadSourcePath } from './config';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 分析功能相关类型定义
3
+ */
4
+
5
+ /**
6
+ * 分析选项接口
7
+ */
8
+ export interface AnalyzeOptions {
9
+ file?: string;
10
+ text?: string;
11
+ }
12
+
13
+ /**
14
+ * 分析结果统计接口
15
+ */
16
+ export interface Statistics {
17
+ charCount: number;
18
+ wordCount: number;
19
+ lineCount: number;
20
+ }
21
+
22
+ /**
23
+ * 关键词接口
24
+ */
25
+ export interface Keyword {
26
+ word: string;
27
+ count: number;
28
+ }
29
+
30
+ /**
31
+ * 分析结果接口
32
+ */
33
+ export interface AnalysisResult {
34
+ statistics: Statistics;
35
+ keywords: Keyword[];
36
+ summary: string;
37
+ }