trackswap 0.0.26 → 0.2.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 (70) hide show
  1. package/README.md +124 -431
  2. package/dist/FIT/base.d.ts +61 -0
  3. package/dist/FIT/course-plan.d.ts +3 -0
  4. package/dist/FIT/crc.d.ts +1 -0
  5. package/dist/FIT/decoder.d.ts +33 -0
  6. package/dist/FIT/encoder.d.ts +45 -0
  7. package/dist/FIT/encoderCourse.d.ts +37 -0
  8. package/dist/FIT/encoderWorkout.d.ts +41 -0
  9. package/dist/FIT/health.d.ts +121 -0
  10. package/dist/FIT/index.d.ts +15 -0
  11. package/dist/FIT/mesg.d.ts +37 -0
  12. package/dist/FIT/middleware.d.ts +33 -0
  13. package/dist/FIT/plugins.d.ts +17 -0
  14. package/dist/FIT/processor.d.ts +34 -0
  15. package/dist/FIT/types/checksum_type.d.ts +4 -0
  16. package/dist/FIT/types/course_point.d.ts +56 -0
  17. package/dist/FIT/types/date_time_type.d.ts +3 -0
  18. package/dist/FIT/types/event.d.ts +49 -0
  19. package/dist/FIT/types/event_type.d.ts +18 -0
  20. package/dist/FIT/types/file.d.ts +23 -0
  21. package/dist/FIT/types/file_flags_type.d.ts +5 -0
  22. package/dist/FIT/types/index.d.ts +119 -0
  23. package/dist/FIT/types/local_date_time_type.d.ts +3 -0
  24. package/dist/FIT/types/mesg_count_type.d.ts +5 -0
  25. package/dist/FIT/types/mesg_num.d.ts +125 -0
  26. package/dist/FIT/types/message_index_type.d.ts +4558 -0
  27. package/dist/FIT/types/sport.d.ts +47 -0
  28. package/dist/FIT/types.d.ts +516 -0
  29. package/dist/FIT/util.d.ts +1 -0
  30. package/dist/GPX/base.d.ts +16 -0
  31. package/dist/GPX/converters.d.ts +62 -0
  32. package/dist/GPX/decoder.d.ts +43 -0
  33. package/dist/GPX/encoder.d.ts +45 -0
  34. package/dist/GPX/extensions/index.d.ts +14 -0
  35. package/dist/GPX/index.d.ts +13 -0
  36. package/dist/GPX/middleware.d.ts +18 -0
  37. package/dist/GPX/processor.d.ts +30 -0
  38. package/dist/GPX/types.d.ts +124 -0
  39. package/dist/GPX/util.d.ts +1 -0
  40. package/dist/TCX/base.d.ts +19 -0
  41. package/dist/TCX/converters.d.ts +52 -0
  42. package/dist/TCX/decoder.d.ts +33 -0
  43. package/dist/TCX/encoder.d.ts +46 -0
  44. package/dist/TCX/index.d.ts +7 -0
  45. package/dist/TCX/middleware.d.ts +25 -0
  46. package/dist/TCX/processor.d.ts +39 -0
  47. package/dist/TCX/types.d.ts +192 -0
  48. package/dist/TrackSwap.d.ts +16 -0
  49. package/dist/activity/base.d.ts +62 -0
  50. package/dist/activity/converters.d.ts +40 -0
  51. package/dist/activity/encoders.d.ts +36 -0
  52. package/dist/activity/index.d.ts +17 -0
  53. package/dist/activity/metrics/MetricsCalculator.d.ts +18 -0
  54. package/dist/activity/metrics/index.d.ts +1 -0
  55. package/dist/activity/processor.d.ts +19 -0
  56. package/dist/core/base.d.ts +73 -0
  57. package/dist/core/builtin-adapters.d.ts +52 -0
  58. package/dist/core/format-adapter.d.ts +23 -0
  59. package/dist/core/format-registry.d.ts +9 -0
  60. package/dist/core/index.d.ts +2 -0
  61. package/dist/core/xml-utils.d.ts +17 -0
  62. package/dist/document.d.ts +57 -0
  63. package/dist/errors.d.ts +12 -0
  64. package/dist/fit-debug.d.ts +3 -0
  65. package/dist/index.cjs +29 -26
  66. package/dist/index.d.ts +9 -0
  67. package/dist/index.js +20 -17
  68. package/dist/types.d.ts +76 -0
  69. package/dist/util.d.ts +25 -0
  70. package/package.json +15 -6
package/README.md CHANGED
@@ -1,455 +1,148 @@
1
- # TrackSwap - 多格式轨迹处理库
1
+ # TrackSwap
2
2
 
3
- 一个强大的多格式轨迹文件处理库,支持 GPX、FIT、TCX 文件的解析、编码和格式转换。
3
+ TrackSwap 是一个面向 Node.js 和 TypeScript 的运动、轨迹与健康数据工具包。它使用统一文档模型读取 FIT、GPX 和 TCX,并通过格式适配器隔离各协议的解析与编码实现。
4
4
 
5
- ---
5
+ ## 安装
6
6
 
7
- **🌐 Language / 语言**: [English](README_EN.md) | [中文](README.md)
8
-
9
- ---
10
-
11
- ## 🚀 特性
12
-
13
- - 🔄 **多格式支持**: GPX 1.1、FIT、TCX 格式
14
- - 🎯 **统一转换**: 任意格式之间相互转换
15
- - 🤖 **自动检测**: 智能识别文件格式
16
- - 🧩 **扩展系统**: 灵活的扩展架构,支持自定义数据处理
17
- - 📊 **智能聚合**: 默认启用运动数据聚合(心率、功率、速度等)
18
- - 🛡️ **类型安全**: 完整的 TypeScript 支持
19
- - ⚡ **高性能**: 优化的解析和编码算法
20
-
21
- ## 📦 安装
22
-
23
- ```bash
7
+ ~~~bash
24
8
  npm install trackswap
25
- ```
9
+ ~~~
26
10
 
27
- ## 🎯 快速开始
11
+ 需要 Node.js 18 或更高版本。
28
12
 
29
- ### 基本使用
13
+ ## 核心模型
30
14
 
31
- ```typescript
32
- import { TrackSwap } from 'trackswap';
33
- import fs from 'fs';
15
+ ~~~ts
16
+ import TrackSwap from "trackswap";
34
17
 
35
18
  const trackSwap = new TrackSwap();
36
19
 
37
- // 解析 GPX 文件
38
- const gpxBuffer = fs.readFileSync('track.gpx');
39
- const gpxData = await trackSwap.parseGPX(gpxBuffer);
40
-
41
- // 解析 FIT 文件
42
- const fitBuffer = fs.readFileSync('activity.fit');
43
- const fitData = await trackSwap.parseFIT(fitBuffer);
44
-
45
- // 解析 TCX 文件
46
- const tcxBuffer = fs.readFileSync('workout.tcx');
47
- const tcxData = await trackSwap.parseTCX(tcxBuffer);
48
- ```
49
-
50
- ### 格式转换
51
-
52
- ```typescript
53
- // 🔄 自动检测格式并转换
54
- const gpxBuffer = await trackSwap.convertFile(anyBuffer, 'gpx');
55
- const fitBuffer = await trackSwap.convertFile(anyBuffer, 'fit');
56
- const tcxBuffer = await trackSwap.convertFile(anyBuffer, 'tcx');
57
-
58
- // 🎯 指定源格式转换(提升性能)
59
- const convertedBuffer = await trackSwap.convertFile(
60
- sourceBuffer,
61
- 'fit', // 目标格式
62
- 'gpx' // 源格式(可选)
63
- );
64
- ```
65
-
66
- ### 自动解析
67
-
68
- ```typescript
69
- // 🤖 自动检测格式并解析
70
- const parsedData = await trackSwap.parseFile(buffer);
71
-
72
- // 🏃‍♂️ 解析并转换为统一的运动数据格式
73
- const sportData = await trackSwap.parseToActivity(buffer);
74
- ```
75
-
76
- ### 扩展系统
77
-
78
- ```typescript
79
- import { TrackSwap } from 'trackswap';
80
- import { SlopeExtension } from 'trackswap/extensions';
81
-
82
- // 🧩 使用扩展增强功能
83
- const trackSwap = new TrackSwap()
84
- .extend(new SlopeExtension()); // 添加坡度计算
85
-
86
- // 📊 默认已启用基础指标聚合(心率、功率、速度、踏频等)
87
- const result = await trackSwap.parseGPX(gpxBuffer);
88
- // result 包含完整的聚合数据和坡度信息
89
-
90
- // 🔗 链式添加多个扩展
91
- const enhancedTrackSwap = new TrackSwap()
92
- .extend(new SlopeExtension())
93
- .extend(new CustomExtension());
94
- ```
95
-
96
- ## 📖 详细使用指南
97
-
98
- ### 扩展系统
99
-
100
- 详细的扩展开发和使用指南请参考:[扩展系统文档](EXTENSIONS.md)
101
-
102
- ### 1. 文件解析
103
-
104
- #### GPX 文件处理
105
-
106
- ```typescript
107
- // 从 Buffer 解析
108
- const gpxData = await trackSwap.parseGPX(buffer);
20
+ try {
21
+ const document = await trackSwap.decode(buffer);
109
22
 
110
- // 从字符串解析
111
- const gpxData = await trackSwap.parseGPXString(xmlString);
112
-
113
- // 编码为 Buffer
114
- const buffer = await trackSwap.encodeGPX(gpxData);
115
-
116
- // 编码为字符串
117
- const xmlString = trackSwap.encodeGPXString(gpxData);
118
- ```
119
-
120
- #### FIT 文件处理
121
-
122
- ```typescript
123
- // 解析 FIT 文件
124
- const fitData = await trackSwap.parseFIT(buffer);
125
-
126
- // 编码为 Activity 文件
127
- const activityBuffer = await trackSwap.encodeFIT(fitData);
128
-
129
- // 编码为 Course 文件
130
- const courseBuffer = await trackSwap.encodeFITCourse(fitData);
131
- ```
132
-
133
- #### TCX 文件处理
134
-
135
- ```typescript
136
- // 从 Buffer 解析
137
- const tcxData = await trackSwap.parseTCX(buffer);
138
-
139
- // 从字符串解析
140
- const tcxData = await trackSwap.parseTCXString(xmlString);
141
-
142
- // 编码为 Buffer
143
- const buffer = await trackSwap.encodeTCX(tcxData);
144
-
145
- // 编码为字符串
146
- const xmlString = await trackSwap.encodeTCXString(tcxData);
147
- ```
148
-
149
- ### 2. 格式转换
150
-
151
- #### 统一转换方法
152
-
153
- ```typescript
154
- // 🔄 自动检测并转换
155
- const result = await trackSwap.convertFile(sourceBuffer, 'gpx');
23
+ console.log(document.format);
24
+ console.log(document.kinds);
25
+ console.log(document.facets.activity);
26
+ console.log(document.facets.health);
27
+ console.log(document.metadata);
28
+ } finally {
29
+ await trackSwap.dispose();
30
+ }
31
+ ~~~
32
+
33
+ `decode()` 默认不会返回格式原生对象。只有调试或协议级处理确实需要时才启用:
34
+
35
+ ~~~ts
36
+ const document = await trackSwap.decode(buffer, {
37
+ format: "fit",
38
+ includeNative: true,
39
+ fit: {
40
+ includeUnknownMessages: true,
41
+ },
42
+ });
43
+ ~~~
44
+
45
+ ## 活动读取、编码与转换
46
+
47
+ ~~~ts
48
+ const activity = await trackSwap.decodeActivity(input);
49
+
50
+ const gpx = await trackSwap.encodeActivity(activity, { format: "gpx" });
51
+ const fit = await trackSwap.encodeActivity(activity, { format: "fit" });
52
+ const tcx = await trackSwap.encodeActivity(activity, { format: "tcx" });
53
+
54
+ const converted = await trackSwap.transcode(input, {
55
+ sourceFormat: "gpx",
56
+ format: "tcx",
57
+ });
58
+
59
+ const course = await trackSwap.encodeCourse(input);
60
+ ~~~
61
+
62
+ ## FIT 健康数据
63
+
64
+ FIT 文件只解码一次,活动与健康数据作为同一 `TrackDocument` 的 facet 返回。健康结构不直接绑定 Garmin Connect JSON,使用稳定的中间表示:
65
+
66
+ - `points`:心率、压力、呼吸率、血氧、Body Battery、HRV、温度等时间点;
67
+ - `intervals`:步数、距离、热量和活动累计量;
68
+ - `sessions`:睡眠、小睡和 Health Snapshot;
69
+ - `summaries`:日汇总、睡眠汇总及设备汇总;
70
+ - `diagnostics`:FIT SDK profile、无效值、未知消息和解析警告。
71
+
72
+ ~~~ts
73
+ const document = await trackSwap.decode(fitBuffer, {
74
+ fit: {
75
+ includeDeveloperFields: true,
76
+ health: {
77
+ reconstructIntervals: true,
78
+ retainCumulativeValues: false,
79
+ },
80
+ },
81
+ });
82
+ ~~~
83
+
84
+ ## 错误处理
85
+
86
+ 所有公共操作使用 `TrackSwapError`,可通过稳定错误码分类:
87
+
88
+ ~~~ts
89
+ import { TrackSwapError } from "trackswap";
90
+
91
+ try {
92
+ await trackSwap.decode(input);
93
+ } catch (error) {
94
+ if (error instanceof TrackSwapError) {
95
+ console.error(error.code, error.format, error.cause);
96
+ }
97
+ }
98
+ ~~~
156
99
 
157
- // 🎯 指定格式转换
158
- const result = await trackSwap.convertFile(sourceBuffer, 'fit', 'gpx');
100
+ 错误码包括 `FORMAT_UNKNOWN`、`FORMAT_UNSUPPORTED`、`DECODE_FAILED`、`ACTIVITY_NOT_FOUND`、`ENCODE_FAILED`、`ADAPTER_CONFLICT` 和 `DISPOSED`。
159
101
 
160
- // 📊 支持的转换组合
161
- // GPX ↔ FIT ↔ TCX (任意格式之间相互转换)
162
- ```
102
+ ## 自定义格式适配器
163
103
 
164
- #### ActivityType 统一格式
104
+ 格式扩展发生在一个明确边界上,不需要访问 TrackSwap 内部解码器:
165
105
 
166
- ```typescript
167
- // 转换为统一的运动数据格式
168
- const gpxActivity = await trackSwap.convertGPXToActivity(gpxData);
169
- const fitActivity = await trackSwap.convertFITToActivity(fitData);
170
- const tcxActivity = await trackSwap.convertTCXToActivity(tcxData);
106
+ ~~~ts
107
+ import TrackSwap, { type TrackFormatAdapter } from "trackswap";
171
108
 
172
- // 从统一格式转换回具体格式
173
- const gpxData = await trackSwap.convertActivityToGPX(sportData);
174
- const fitData = await trackSwap.convertActivityToFIT(sportData);
175
- const tcxData = await trackSwap.convertActivityToTCX(sportData);
176
- ```
109
+ const adapter: TrackFormatAdapter = {
110
+ format: "geojson",
111
+ matches: (input) => input.includes(Buffer.from('"FeatureCollection"')),
112
+ decode: async (input, options) => ({
113
+ kinds: ["route"],
114
+ facets: { activity: myDecode(input, options) },
115
+ native: {},
116
+ }),
117
+ encodeActivity: async (activity) => myEncode(activity),
118
+ };
177
119
 
178
- ### 3. 智能解析
120
+ const trackSwap = new TrackSwap({ adapters: [adapter] });
121
+ ~~~
179
122
 
180
- ```typescript
181
- // 🤖 自动检测格式
182
- const format = trackSwap.detectFormat(buffer);
183
- console.log(`检测到格式: ${format}`); // 'gpx' | 'fit' | 'tcx' | 'unknown'
123
+ 自定义适配器默认追加到 FIT、GPX、TCX 之后。每种格式只能注册一个适配器;重复注册会立即抛出 `ADAPTER_CONFLICT`。测试或完全定制运行时可设置 `includeBuiltInAdapters: false`。
184
124
 
185
- // 📂 统一解析方法
186
- const parsedData = await trackSwap.parseFile(buffer);
187
- const parsedData = await trackSwap.parseFile(buffer, 'gpx'); // 指定格式
125
+ ## 公共 API
188
126
 
189
- // 🏃‍♂️ 解析为统一格式
190
- const sportData = await trackSwap.parseToActivity(buffer);
191
- const sportData = await trackSwap.parseToActivity(buffer, 'fit'); // 指定格式
192
- ```
127
+ | 方法 | 作用 |
128
+ | --- | --- |
129
+ | `detect(input)` | 检测 FIT、GPX 或 TCX |
130
+ | `decode(input, options?)` | 返回统一 `TrackDocument` |
131
+ | `decodeActivity(input, options?)` | 读取并要求存在活动 facet |
132
+ | `encodeActivity(activity, options)` | 将统一活动编码为目标格式 |
133
+ | `transcode(input, options)` | 通过统一活动模型转换格式 |
134
+ | `encodeCourse(inputOrActivity, options?)` | 生成 FIT Course |
135
+ | `dispose()` | 释放当前实例已创建的解码器 |
193
136
 
194
- ## 🔧 API 参考
137
+ ## 设计原则
195
138
 
196
- ### TrackSwap 类
139
+ - façade 只负责编排,不包含协议逻辑;
140
+ - 格式适配器负责检测、原生解码与编码;
141
+ - 统一活动和健康模型是格式之间唯一的数据交换边界;
142
+ - 默认惰性创建解码器,未使用的格式没有初始化成本;
143
+ - 核心解析阶段 fail-fast,不返回部分损坏的文档;
144
+ - 原生协议对象默认不进入公共结果,避免业务层依赖实现细节。
197
145
 
198
- #### 解析方法
146
+ ## License
199
147
 
200
- | 方法 | 说明 | 参数 | 返回值 |
201
- |------|------|------|--------|
202
- | `parseGPX(buffer)` | 解析 GPX Buffer | `Buffer` | `Promise<GPX11Type \| undefined>` |
203
- | `parseGPXString(xml)` | 解析 GPX 字符串 | `string` | `Promise<GPX11Type \| undefined>` |
204
- | `parseFIT(buffer)` | 解析 FIT Buffer | `Buffer` | `Promise<FITFileType>` |
205
- | `parseTCX(buffer)` | 解析 TCX Buffer | `Buffer` | `Promise<TCXFileType>` |
206
- | `parseTCXString(xml)` | 解析 TCX 字符串 | `string` | `Promise<TCXFileType>` |
207
- | `parseFile(buffer, type?)` | 统一解析方法 | `Buffer, string?` | `Promise<GPX11Type \| FITFileType \| TCXFileType>` |
208
- | `parseToActivity(buffer, type?)` | 解析为统一格式 | `Buffer, string?` | `Promise<ActivityType>` |
209
-
210
- #### 编码方法
211
-
212
- | 方法 | 说明 | 参数 | 返回值 |
213
- |------|------|------|--------|
214
- | `encodeGPX(data)` | 编码 GPX 为 Buffer | `GPX11Type` | `Promise<Buffer>` |
215
- | `encodeGPXString(data)` | 编码 GPX 为字符串 | `GPX11Type` | `string` |
216
- | `encodeFIT(data)` | 编码 FIT 为 Buffer | `FITFileType` | `Promise<Buffer>` |
217
- | `encodeFITCourse(data)` | 编码 FIT Course | `FITFileType` | `Promise<Buffer>` |
218
- | `encodeTCX(data)` | 编码 TCX 为 Buffer | `TCXFileType` | `Promise<Buffer>` |
219
- | `encodeTCXString(data)` | 编码 TCX 为字符串 | `TCXFileType` | `Promise<string>` |
220
-
221
- #### 转换方法
222
-
223
- | 方法 | 说明 | 参数 | 返回值 |
224
- |------|------|------|--------|
225
- | `convertFile(buffer, target, source?)` | 统一转换方法 | `Buffer, string, string?` | `Promise<Buffer>` |
226
- | `convertGPXToActivity(data)` | GPX → Activity | `GPX11Type` | `Promise<ActivityType>` |
227
- | `convertFITToActivity(data)` | FIT → Activity | `FITFileType` | `Promise<ActivityType>` |
228
- | `convertTCXToActivity(data)` | TCX → Activity | `TCXFileType` | `Promise<ActivityType>` |
229
- | `convertActivityToGPX(data)` | Activity → GPX | `ActivityType` | `Promise<GPX11Type>` |
230
- | `convertActivityToFIT(data)` | Activity → FIT | `ActivityType` | `Promise<FITFileType>` |
231
- | `convertActivityToTCX(data)` | Activity → TCX | `ActivityType` | `Promise<TCXFileType>` |
232
-
233
- #### 工具方法
234
-
235
- | 方法 | 说明 | 参数 | 返回值 |
236
- |------|------|------|--------|
237
- | `detectFormat(buffer)` | 检测文件格式 | `Buffer` | `'gpx' \| 'fit' \| 'tcx' \| 'unknown'` |
238
- | `getActivityProcessor()` | 获取运动处理器 | - | `ActivityProcessor` |
239
- | `destroy()` | 清理资源 | - | `Promise<void>` |
240
-
241
- ## 🌟 实际应用示例
242
-
243
- ### 批量格式转换
244
-
245
- ```typescript
246
- import { TrackSwap } from 'trackswap';
247
- import fs from 'fs';
248
- import path from 'path';
249
-
250
- async function batchConvert(inputDir: string, outputFormat: 'gpx' | 'fit' | 'tcx') {
251
- const trackSwap = new TrackSwap();
252
- const files = fs.readdirSync(inputDir);
253
-
254
- for (const file of files) {
255
- try {
256
- const inputPath = path.join(inputDir, file);
257
- const buffer = fs.readFileSync(inputPath);
258
-
259
- // 🔄 自动检测并转换
260
- const converted = await trackSwap.convertFile(buffer, outputFormat);
261
-
262
- const outputPath = path.join(inputDir,
263
- `${path.parse(file).name}.${outputFormat}`);
264
- fs.writeFileSync(outputPath, converted);
265
-
266
- console.log(`✅ 转换完成: ${file} → ${outputFormat}`);
267
- } catch (error) {
268
- console.error(`❌ 转换失败: ${file}`, error);
269
- }
270
- }
271
-
272
- await trackSwap.destroy();
273
- }
274
-
275
- // 使用示例
276
- await batchConvert('./tracks', 'gpx');
277
- ```
278
-
279
- ### 轨迹数据分析
280
-
281
- ```typescript
282
- async function analyzeTrack(buffer: Buffer) {
283
- const trackSwap = new TrackSwap();
284
-
285
- // 🔍 检测格式
286
- const format = trackSwap.detectFormat(buffer);
287
- console.log(`文件格式: ${format}`);
288
-
289
- // 📊 转换为统一格式进行分析
290
- const sportData = await trackSwap.parseToActivity(buffer);
291
-
292
- // 分析数据
293
- const analysis = {
294
- totalDistance: sportData.summary?.totalDistance || 0,
295
- totalTime: sportData.summary?.totalTime || 0,
296
- avgSpeed: sportData.summary?.avgSpeed || 0,
297
- maxSpeed: sportData.summary?.maxSpeed || 0,
298
- trackPoints: sportData.tracks?.[0]?.segments?.[0]?.points?.length || 0
299
- };
300
-
301
- console.log('轨迹分析结果:', analysis);
302
-
303
- await trackSwap.destroy();
304
- return analysis;
305
- }
306
- ```
307
-
308
- ### 多格式数据融合
309
-
310
- ```typescript
311
- async function mergeMultiFormatTracks(files: { buffer: Buffer, name: string }[]) {
312
- const trackSwap = new TrackSwap();
313
- const allActivityData: ActivityType[] = [];
314
-
315
- // 🔄 将所有格式转换为统一格式
316
- for (const file of files) {
317
- try {
318
- const sportData = await trackSwap.parseToActivity(file.buffer);
319
- sportData.metadata = { ...sportData.metadata, originalFile: file.name };
320
- allActivityData.push(sportData);
321
- } catch (error) {
322
- console.error(`解析失败: ${file.name}`, error);
323
- }
324
- }
325
-
326
- // 融合数据逻辑
327
- const mergedData = mergeActivityData(allActivityData);
328
-
329
- // 输出为不同格式
330
- const results = {
331
- gpx: await trackSwap.convertActivityToGPX(mergedData),
332
- fit: await trackSwap.convertActivityToFIT(mergedData),
333
- tcx: await trackSwap.convertActivityToTCX(mergedData)
334
- };
335
-
336
- await trackSwap.destroy();
337
- return results;
338
- }
339
- ```
340
-
341
- ## 🏗️ 架构设计
342
-
343
- TrackSwap 采用模块化架构,核心组件包括:
344
-
345
- ```
346
- ┌─────────────────────────────────────────────────────────────┐
347
- │ TrackSwap 架构图 │
348
- ├─────────────────────────────────────────────────────────────┤
349
- │ │
350
- │ 🔧 核心处理模块 │
351
- │ ┌─────────────────────────────────────────────────────────┐ │
352
- │ │ • GPXDecoder/Encoder - GPX 文件处理 │ │
353
- │ │ • FITDecoder/Encoder - FIT 文件处理 │ │
354
- │ │ • TCXDecoder/Encoder - TCX 文件处理 │ │
355
- │ │ • ActivityProcessor - 统一格式转换处理 │ │
356
- │ └─────────────────────────────────────────────────────────┘ │
357
- │ │
358
- │ 🔄 转换引擎 │
359
- │ ┌─────────────────────────────────────────────────────────┐ │
360
- │ │ • GPXToActivityConverter - GPX → ActivityType │ │
361
- │ │ • FITToActivityConverter - FIT → ActivityType │ │
362
- │ │ • TCXToActivityConverter - TCX → ActivityType │ │
363
- │ │ • ActivityToGPXEncoder - ActivityType → GPX │ │
364
- │ │ • ActivityToFITEncoder - ActivityType → FIT │ │
365
- │ │ • ActivityToTCXEncoder - ActivityType → TCX │ │
366
- │ └─────────────────────────────────────────────────────────┘ │
367
- │ │
368
- │ 🎯 统一接口 │
369
- │ ┌─────────────────────────────────────────────────────────┐ │
370
- │ │ • 自动格式检测 │ │
371
- │ │ • 统一解析方法 │ │
372
- │ │ • 统一转换方法 │ │
373
- │ │ • 生命周期管理 │ │
374
- │ └─────────────────────────────────────────────────────────┘ │
375
- │ │
376
- └─────────────────────────────────────────────────────────────┘
377
- ```
378
-
379
- ## 🔍 支持的格式
380
-
381
- ### GPX (GPS Exchange Format)
382
- - ✅ GPX 1.1 标准
383
- - ✅ 航路点 (Waypoints)
384
- - ✅ 路线 (Routes)
385
- - ✅ 轨迹 (Tracks)
386
- - ✅ 元数据 (Metadata)
387
- - ✅ 扩展属性
388
-
389
- ### FIT (Flexible and Interoperable Data Transfer)
390
- - ✅ Activity 文件
391
- - ✅ Course 文件
392
- - ✅ 轨迹点数据
393
- - ✅ 心率、速度、海拔等传感器数据
394
- - ✅ Garmin 设备兼容
395
-
396
- ### TCX (Training Center XML)
397
- - ✅ 训练数据
398
- - ✅ 运动轨迹
399
- - ✅ 心率区间
400
- - ✅ 圈速数据
401
- - ✅ Garmin Connect 兼容
402
-
403
- ## 🛠️ 开发指南
404
-
405
- ### 环境要求
406
-
407
- - Node.js >= 16
408
- - TypeScript >= 4.5
409
-
410
- ### 项目结构
411
-
412
- ```
413
- src/
414
- ├── TrackSwap.ts # 主入口类
415
- ├── activity/ # 活动处理模块
416
- │ ├── extensions.ts # 扩展系统核心
417
- │ ├── slope-extension.ts # 坡度计算扩展
418
- │ ├── processor.ts # 活动处理器
419
- │ └── pipeline.ts # 数据处理管道
420
- ├── decoders/ # 解码器模块
421
- │ ├── GPXDecoder.ts
422
- │ ├── FITDecoder.ts
423
- │ └── TCXDecoder.ts
424
- ├── encoders/ # 编码器模块
425
- │ ├── GPXEncoder.ts
426
- │ ├── FITEncoder.ts
427
- │ └── TCXEncoder.ts
428
- ├── converters/ # 格式转换器
429
- │ ├── GPXConverter.ts
430
- │ ├── FITConverter.ts
431
- │ └── TCXConverter.ts
432
- └── types.ts # 公共类型定义
433
- ```
434
-
435
- ### 贡献指南
436
-
437
- 1. Fork 项目
438
- 2. 创建特性分支
439
- 3. 提交更改
440
- 4. 发起 Pull Request
441
-
442
- ## 📄 许可证
443
-
444
- MIT License
445
-
446
- ## 🙏 致谢
447
-
448
- 感谢所有贡献者和开源社区的支持!
449
-
450
- ---
451
-
452
- <div align="center">
453
- <strong>TrackSwap - 让轨迹数据处理变得简单</strong><br>
454
- 🚀 高效 • 🔄 统一 • 🛡️ 安全 • 📈 可扩展
455
- </div>
148
+ MIT
@@ -0,0 +1,61 @@
1
+ import { IPlugin } from "../core/base.js";
2
+ import { FITContext, FITDecoderMesgs, FITFileType } from "./types.js";
3
+ export interface IFITMessageConverter extends IPlugin<FITContext> {
4
+ supportedMessageTypes: string[];
5
+ convertMessages(messages: any[], context: FITContext): any;
6
+ supports(messageType: string): boolean;
7
+ priority?: number;
8
+ }
9
+ export interface IFITStructurePlugin extends IPlugin<FITContext> {
10
+ structureData(messages: FITDecoderMesgs, context: FITContext): Partial<FITFileType>;
11
+ priority?: number;
12
+ }
13
+ export interface IFITMiddlewarePlugin extends IPlugin<FITContext> {
14
+ priority?: number;
15
+ onParse?(buffer: Buffer, context: FITContext): Promise<Buffer> | Buffer;
16
+ onExtractMessages?(messages: FITDecoderMesgs, context: FITContext): Promise<FITDecoderMesgs> | FITDecoderMesgs;
17
+ onStructure?(result: Partial<FITFileType>, context: FITContext): Promise<Partial<FITFileType>> | Partial<FITFileType>;
18
+ onComplete?(result: FITFileType, context: FITContext): Promise<FITFileType> | FITFileType;
19
+ onError?(error: Error, context: FITContext): Promise<void> | void;
20
+ }
21
+ export declare abstract class BaseFITMessageConverter implements IFITMessageConverter {
22
+ abstract name: string;
23
+ abstract supportedMessageTypes: string[];
24
+ version: string;
25
+ priority: number;
26
+ abstract convertMessages(messages: any[], context: FITContext): any;
27
+ supports(messageType: string): boolean;
28
+ initialize(context: FITContext): Promise<void>;
29
+ destroy(context: FITContext): Promise<void>;
30
+ validate(context: FITContext): Promise<boolean>;
31
+ protected parseTimestamp(timestamp: string | undefined): Date | undefined;
32
+ protected calculateDuration(startTime?: string, endTime?: string): number;
33
+ protected filterMessagesByTimeRange(messages: any[], startTime: string, endTime: string, tolerance?: number): any[];
34
+ protected groupMessagesByLap(records: any[], laps: any[]): Array<{
35
+ lap: any;
36
+ records: any[];
37
+ }>;
38
+ }
39
+ export declare abstract class BaseFITStructurePlugin implements IFITStructurePlugin {
40
+ abstract name: string;
41
+ version: string;
42
+ priority: number;
43
+ abstract structureData(messages: FITDecoderMesgs, context: FITContext): Partial<FITFileType>;
44
+ initialize(context: FITContext): Promise<void>;
45
+ destroy(context: FITContext): Promise<void>;
46
+ validate(context: FITContext): Promise<boolean>;
47
+ }
48
+ export declare abstract class BaseFITMiddleware implements IFITMiddlewarePlugin {
49
+ abstract name: string;
50
+ version: string;
51
+ description: string;
52
+ priority: number;
53
+ initialize(context: FITContext): Promise<void>;
54
+ destroy(context: FITContext): Promise<void>;
55
+ validate(context: FITContext): Promise<boolean>;
56
+ onParse(buffer: Buffer, context: FITContext): Promise<Buffer>;
57
+ onExtractMessages(messages: FITDecoderMesgs, context: FITContext): Promise<FITDecoderMesgs>;
58
+ onStructure(result: Partial<FITFileType>, context: FITContext): Promise<Partial<FITFileType>>;
59
+ onComplete(result: FITFileType, context: FITContext): Promise<FITFileType>;
60
+ onError(error: Error, context: FITContext): Promise<void>;
61
+ }
@@ -0,0 +1,3 @@
1
+ import type { CoursePlan } from "./encoderCourse.js";
2
+ import type { FileType } from "../types.js";
3
+ export declare function buildCoursePlanFromActivity(file: FileType): CoursePlan;
@@ -0,0 +1 @@
1
+ export declare function crc(buffer: ArrayBuffer | Uint8Array, initial?: number): number;
@@ -0,0 +1,33 @@
1
+ import { IFITMessageConverter, IFITStructurePlugin, IFITMiddlewarePlugin } from "./base.js";
2
+ import { FITFileType } from "./types.js";
3
+ export declare class FITDecoder {
4
+ private converterPlugins;
5
+ private registeredPlugins;
6
+ private structurePlugins;
7
+ private middlewarePlugins;
8
+ private processors;
9
+ private initialized;
10
+ private defaultPluginsRegistered;
11
+ constructor();
12
+ private initializePipeline;
13
+ private registerDefaultPlugins;
14
+ private addConverterForMessageType;
15
+ private addStructurePlugin;
16
+ registerMessageConverter(plugin: IFITMessageConverter): Promise<void>;
17
+ registerStructurePlugin(plugin: IFITStructurePlugin): Promise<void>;
18
+ registerMiddleware(plugin: IFITMiddlewarePlugin): Promise<void>;
19
+ getConverter(messageType: string): IFITMessageConverter | undefined;
20
+ getConverters(messageType: string): IFITMessageConverter[];
21
+ unregisterPlugin(pluginName: string): boolean;
22
+ initialize(): Promise<void>;
23
+ destroy(): Promise<void>;
24
+ private createContext;
25
+ private executeMiddlewareHook;
26
+ parseByBuffer(buffer: Buffer, options?: {
27
+ userData?: Record<string, any>;
28
+ }): Promise<FITFileType | undefined>;
29
+ }
30
+ declare const _default: {
31
+ FITDecoder: typeof FITDecoder;
32
+ };
33
+ export default _default;