trackswap 0.0.2 → 0.0.4

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 (83) hide show
  1. package/README.md +328 -281
  2. package/dist/FIT/base.d.ts +0 -30
  3. package/dist/FIT/decoder.d.ts +0 -48
  4. package/dist/FIT/encoder.d.ts +0 -97
  5. package/dist/FIT/mesg.d.ts +0 -3
  6. package/dist/FIT/middleware.d.ts +0 -15
  7. package/dist/FIT/plugins.d.ts +0 -9
  8. package/dist/FIT/processor.d.ts +0 -24
  9. package/dist/FIT/types/mesg_num.d.ts +102 -102
  10. package/dist/FIT/types/sport.d.ts +44 -44
  11. package/dist/FIT/types.d.ts +0 -35
  12. package/dist/GPX/base.d.ts +6 -50
  13. package/dist/GPX/converters.d.ts +0 -30
  14. package/dist/GPX/decoder.d.ts +0 -66
  15. package/dist/GPX/encoder.d.ts +0 -109
  16. package/dist/GPX/middleware.d.ts +0 -9
  17. package/dist/GPX/processor.d.ts +0 -18
  18. package/dist/GPX/types.d.ts +0 -108
  19. package/dist/TCX/base.d.ts +0 -33
  20. package/dist/TCX/converters.d.ts +0 -24
  21. package/dist/TCX/decoder.d.ts +0 -54
  22. package/dist/TCX/encoder.d.ts +0 -109
  23. package/dist/TCX/index.d.ts +4 -2
  24. package/dist/TCX/middleware.d.ts +11 -13
  25. package/dist/TCX/processor.d.ts +13 -21
  26. package/dist/TCX/types.d.ts +1 -13
  27. package/dist/TrackSwap.d.ts +37 -33
  28. package/dist/core/base.d.ts +0 -46
  29. package/dist/core/xml-utils.d.ts +0 -24
  30. package/dist/index.cjs +26 -0
  31. package/dist/index.js +26 -5
  32. package/dist/sport/base.d.ts +64 -0
  33. package/dist/sport/converters.d.ts +33 -0
  34. package/dist/sport/encoders.d.ts +34 -0
  35. package/dist/sport/index.d.ts +17 -0
  36. package/dist/sport/processor.d.ts +19 -0
  37. package/dist/types.d.ts +91 -13
  38. package/package.json +23 -5
  39. package/dist/FIT/base.js +0 -101
  40. package/dist/FIT/crc.js +0 -16
  41. package/dist/FIT/decoder.js +0 -323
  42. package/dist/FIT/encoder.js +0 -445
  43. package/dist/FIT/index.js +0 -13
  44. package/dist/FIT/mesg.js +0 -208
  45. package/dist/FIT/middleware.js +0 -206
  46. package/dist/FIT/plugins.js +0 -104
  47. package/dist/FIT/processor.js +0 -170
  48. package/dist/FIT/types/course_point.js +0 -58
  49. package/dist/FIT/types/event.js +0 -50
  50. package/dist/FIT/types/event_type.js +0 -19
  51. package/dist/FIT/types/file.js +0 -24
  52. package/dist/FIT/types/index.js +0 -297
  53. package/dist/FIT/types/mesg_num.js +0 -106
  54. package/dist/FIT/types/sport.js +0 -48
  55. package/dist/FIT/types.js +0 -1
  56. package/dist/FIT/util.js +0 -7
  57. package/dist/GPX/base.js +0 -56
  58. package/dist/GPX/converters.js +0 -352
  59. package/dist/GPX/decoder.js +0 -409
  60. package/dist/GPX/encoder.js +0 -513
  61. package/dist/GPX/extensions/index.js +0 -14
  62. package/dist/GPX/index.js +0 -11
  63. package/dist/GPX/middleware.js +0 -93
  64. package/dist/GPX/processor.js +0 -188
  65. package/dist/GPX/types.js +0 -1
  66. package/dist/GPX/util.js +0 -18
  67. package/dist/TCX/base.js +0 -62
  68. package/dist/TCX/converters.js +0 -333
  69. package/dist/TCX/decoder.js +0 -350
  70. package/dist/TCX/encoder.js +0 -513
  71. package/dist/TCX/index.js +0 -9
  72. package/dist/TCX/middleware.js +0 -93
  73. package/dist/TCX/processor.js +0 -197
  74. package/dist/TCX/types.js +0 -1
  75. package/dist/TrackSwap.js +0 -84
  76. package/dist/core/base.js +0 -138
  77. package/dist/core/decoder.d.ts +0 -66
  78. package/dist/core/decoder.js +0 -90
  79. package/dist/core/index.js +0 -4
  80. package/dist/core/processor.d.ts +0 -69
  81. package/dist/core/processor.js +0 -265
  82. package/dist/core/xml-utils.js +0 -140
  83. package/dist/types.js +0 -1
@@ -1,513 +0,0 @@
1
- import dayjs from "dayjs";
2
- import utc from "dayjs/plugin/utc.js";
3
- // 配置 dayjs UTC 插件
4
- dayjs.extend(utc);
5
- /**
6
- * TCX 编码器 - 将 TCXFileType 数据编码为 TCX XML 格式
7
- */
8
- export class TCXEncoder {
9
- /**
10
- * 构造函数
11
- * @param options 编码器选项
12
- */
13
- constructor(options = {}) {
14
- this.compact = options.compact ?? true;
15
- }
16
- // ==================== 公共接口 ====================
17
- /**
18
- * 编码 TCX 数据为 Buffer
19
- */
20
- async encode(tcxData) {
21
- const xmlContent = this.buildTCXXML(tcxData);
22
- return Buffer.from(xmlContent, "utf-8");
23
- }
24
- /**
25
- * 编码 TCX 数据为 XML 字符串
26
- */
27
- encodeToString(tcxData) {
28
- return this.buildTCXXML(tcxData);
29
- }
30
- // ==================== 核心构建方法 ====================
31
- /**
32
- * 构建完整的 TCX XML
33
- */
34
- buildTCXXML(tcxData) {
35
- const header = this.buildXMLHeader(tcxData);
36
- const content = this.buildTCXContent(tcxData);
37
- const footer = "</TrainingCenterDatabase>";
38
- const xml = `${header}${content}\n${footer}`;
39
- // 如果启用压缩模式,则压缩XML
40
- return this.compact ? this.compressXML(xml) : xml;
41
- }
42
- /**
43
- * 压缩XML字符串,去掉多余的换行和空格
44
- */
45
- compressXML(xml) {
46
- return (xml
47
- // 去掉标签之间的换行和多余空格
48
- .replace(/>\s+</g, "><")
49
- // 去掉行首行尾的空白字符
50
- .replace(/^\s+|\s+$/gm, "")
51
- // 将多个连续空格替换为单个空格
52
- .replace(/\s+/g, " ")
53
- // 去掉整体的首尾空白
54
- .trim());
55
- }
56
- /**
57
- * 构建 XML 头部
58
- */
59
- buildXMLHeader(tcxData) {
60
- const version = tcxData.version || "1.0";
61
- const creator = tcxData.creator || "trackswap";
62
- const xmlns = tcxData.xmlns ||
63
- "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2";
64
- const xmlnsXsi = tcxData["xmlns:xsi"] || "http://www.w3.org/2001/XMLSchema-instance";
65
- const xsiSchemaLocation = tcxData["xsi:schemaLocation"] ||
66
- "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www8.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd";
67
- return [
68
- '<?xml version="1.0" encoding="UTF-8"?>',
69
- `<TrainingCenterDatabase version="${this.escapeXMLAttribute(version)}"`,
70
- ` creator="${this.escapeXMLAttribute(creator)}"`,
71
- ` xmlns="${this.escapeXMLAttribute(xmlns)}"`,
72
- ` xmlns:xsi="${this.escapeXMLAttribute(xmlnsXsi)}"`,
73
- ` xsi:schemaLocation="${this.escapeXMLAttribute(xsiSchemaLocation)}">`,
74
- ].join("\n");
75
- }
76
- /**
77
- * 构建 TCX 内容
78
- */
79
- buildTCXContent(tcxData) {
80
- const parts = [];
81
- // 按标准 TCX 顺序构建内容
82
- if (tcxData.Folders) {
83
- parts.push(this.buildFolders(tcxData.Folders));
84
- }
85
- if (tcxData.Activities) {
86
- parts.push(this.buildActivities(tcxData.Activities));
87
- }
88
- if (tcxData.Workouts) {
89
- parts.push(this.buildWorkouts(tcxData.Workouts));
90
- }
91
- if (tcxData.Courses) {
92
- parts.push(this.buildCourses(tcxData.Courses));
93
- }
94
- if (tcxData.Author) {
95
- parts.push(this.buildAuthor(tcxData.Author));
96
- }
97
- if (tcxData.Extensions) {
98
- parts.push(this.buildExtensions(tcxData.Extensions));
99
- }
100
- return parts.join("\n");
101
- }
102
- // ==================== 结构元素构建 ====================
103
- /**
104
- * 构建 Folders 元素
105
- */
106
- buildFolders(folders) {
107
- const parts = ["<Folders>"];
108
- if (folders.History) {
109
- parts.push(this.buildHistory(folders.History));
110
- }
111
- if (folders.Workouts) {
112
- parts.push(this.buildWorkouts(folders.Workouts));
113
- }
114
- if (folders.Courses) {
115
- // TODO: 处理 Courses
116
- console.warn("Courses in Folders 暂未实现");
117
- }
118
- parts.push("</Folders>");
119
- return parts.join("\n ");
120
- }
121
- /**
122
- * 构建 History 元素
123
- */
124
- buildHistory(history) {
125
- const parts = ["<History>"];
126
- if (history.Running) {
127
- parts.push(`<Running>${this.buildHistoryFolder(history.Running)}</Running>`);
128
- }
129
- if (history.Biking) {
130
- parts.push(`<Biking>${this.buildHistoryFolder(history.Biking)}</Biking>`);
131
- }
132
- if (history.Other) {
133
- parts.push(`<Other>${this.buildHistoryFolder(history.Other)}</Other>`);
134
- }
135
- if (history.MultiSport) {
136
- parts.push(`<MultiSport>${this.buildHistoryFolder(history.MultiSport)}</MultiSport>`);
137
- }
138
- if (history.Extensions) {
139
- parts.push(this.buildExtensions(history.Extensions));
140
- }
141
- parts.push("</History>");
142
- return parts.join("\n ");
143
- }
144
- /**
145
- * 构建 HistoryFolder 元素
146
- */
147
- buildHistoryFolder(folder) {
148
- // TODO: 实现详细的 HistoryFolder 构建
149
- return "";
150
- }
151
- /**
152
- * 构建 Activities 元素
153
- */
154
- buildActivities(activities) {
155
- const parts = ["<Activities>"];
156
- if (activities.Activity) {
157
- activities.Activity.forEach((activity) => {
158
- parts.push(this.buildActivity(activity));
159
- });
160
- }
161
- if (activities.MultiSportSession) {
162
- activities.MultiSportSession.forEach((session) => {
163
- parts.push(this.buildMultiSportSession(session));
164
- });
165
- }
166
- parts.push("</Activities>");
167
- return parts.join("\n ");
168
- }
169
- /**
170
- * 构建单个 Activity 元素
171
- */
172
- buildActivity(activity) {
173
- const sport = activity.Sport || "Other";
174
- const parts = [`<Activity Sport="${sport}">`];
175
- // ID 是必需的
176
- parts.push(`<Id>${this.formatTime(activity.Id)}</Id>`);
177
- // Lap 是必需的
178
- if (activity.Lap && activity.Lap.length > 0) {
179
- activity.Lap.forEach((lap) => {
180
- parts.push(this.buildActivityLap(lap));
181
- });
182
- }
183
- // 可选元素
184
- this.addOptionalElement(parts, "Notes", activity.Notes);
185
- if (activity.Training) {
186
- // TODO: 实现 Training 元素
187
- console.warn("Activity Training 暂未实现");
188
- }
189
- if (activity.Creator) {
190
- // TODO: 实现 Creator 元素
191
- console.warn("Activity Creator 暂未实现");
192
- }
193
- if (activity.Extensions) {
194
- parts.push(this.buildExtensions(activity.Extensions));
195
- }
196
- parts.push("</Activity>");
197
- return parts.join("\n ");
198
- }
199
- /**
200
- * 构建 ActivityLap 元素
201
- */
202
- buildActivityLap(lap) {
203
- const parts = ["<Lap>"];
204
- // 时间相关
205
- this.addOptionalNumericElement(parts, "TotalTimeSeconds", lap.TotalTimeSeconds);
206
- this.addOptionalNumericElement(parts, "DistanceMeters", lap.DistanceMeters);
207
- this.addOptionalNumericElement(parts, "MaximumSpeed", lap.MaximumSpeed);
208
- this.addOptionalNumericElement(parts, "Calories", lap.Calories);
209
- // 心率
210
- if (lap.AverageHeartRateBpm) {
211
- parts.push(`<AverageHeartRateBpm xsi:type="HeartRateInBeatsPerMinute_t"><Value>${lap.AverageHeartRateBpm}</Value></AverageHeartRateBpm>`);
212
- }
213
- if (lap.MaximumHeartRateBpm) {
214
- parts.push(`<MaximumHeartRateBpm xsi:type="HeartRateInBeatsPerMinute_t"><Value>${lap.MaximumHeartRateBpm}</Value></MaximumHeartRateBpm>`);
215
- }
216
- // 其他属性
217
- this.addOptionalElement(parts, "Intensity", lap.Intensity);
218
- if (lap.Cadence) {
219
- parts.push(this.buildCadence(lap.Cadence));
220
- }
221
- this.addOptionalElement(parts, "TriggerMethod", lap.TriggerMethod);
222
- // Track 数据
223
- if (lap.Track) {
224
- lap.Track.forEach((track) => {
225
- parts.push(this.buildTrack(track));
226
- });
227
- }
228
- this.addOptionalElement(parts, "Notes", lap.Notes);
229
- if (lap.Extensions) {
230
- parts.push(this.buildExtensions(lap.Extensions));
231
- }
232
- parts.push("</Lap>");
233
- return parts.join("\n ");
234
- }
235
- /**
236
- * 构建 Track 元素
237
- */
238
- buildTrack(track) {
239
- const parts = ["<Track>"];
240
- if (track.Trackpoint) {
241
- track.Trackpoint.forEach((trackpoint) => {
242
- parts.push(this.buildTrackpoint(trackpoint));
243
- });
244
- }
245
- parts.push("</Track>");
246
- return parts.join("\n ");
247
- }
248
- /**
249
- * 构建 Trackpoint 元素
250
- */
251
- buildTrackpoint(trackpoint) {
252
- const parts = ["<Trackpoint>"];
253
- // 时间(通常是必需的)
254
- if (trackpoint.Time) {
255
- parts.push(`<Time>${this.formatTime(trackpoint.Time)}</Time>`);
256
- }
257
- // 位置
258
- if (trackpoint.Position) {
259
- parts.push(this.buildPosition(trackpoint.Position));
260
- }
261
- // 数值属性
262
- this.addOptionalNumericElement(parts, "AltitudeMeters", trackpoint.AltitudeMeters);
263
- this.addOptionalNumericElement(parts, "DistanceMeters", trackpoint.DistanceMeters);
264
- // 心率
265
- if (trackpoint.HeartRateBpm) {
266
- parts.push(`<HeartRateBpm xsi:type="HeartRateInBeatsPerMinute_t"><Value>${trackpoint.HeartRateBpm}</Value></HeartRateBpm>`);
267
- }
268
- // 踏频
269
- if (trackpoint.Cadence) {
270
- parts.push(this.buildCadence(trackpoint.Cadence));
271
- }
272
- // 传感器状态
273
- this.addOptionalElement(parts, "SensorState", trackpoint.SensorState);
274
- // 扩展
275
- if (trackpoint.Extensions) {
276
- parts.push(this.buildExtensions(trackpoint.Extensions));
277
- }
278
- parts.push("</Trackpoint>");
279
- return parts.join("\n ");
280
- }
281
- /**
282
- * 构建 Position 元素
283
- */
284
- buildPosition(position) {
285
- const parts = ["<Position>"];
286
- if (position.LatitudeDegrees) {
287
- parts.push(`<LatitudeDegrees>${this.formatCoordinate(parseFloat(position.LatitudeDegrees))}</LatitudeDegrees>`);
288
- }
289
- if (position.LongitudeDegrees) {
290
- parts.push(`<LongitudeDegrees>${this.formatCoordinate(parseFloat(position.LongitudeDegrees))}</LongitudeDegrees>`);
291
- }
292
- parts.push("</Position>");
293
- return parts.join("");
294
- }
295
- /**
296
- * 构建 Cadence 元素
297
- */
298
- buildCadence(cadence) {
299
- return `<Cadence><Low>${cadence.Low}</Low><High>${cadence.High}</High></Cadence>`;
300
- }
301
- /**
302
- * 构建 MultiSportSession 元素
303
- */
304
- buildMultiSportSession(session) {
305
- const parts = ["<MultiSportSession>"];
306
- if (session.Id) {
307
- parts.push(`<Id>${this.formatTime(session.Id)}</Id>`);
308
- }
309
- if (session.FirstSport) {
310
- parts.push(this.buildFirstSport(session.FirstSport));
311
- }
312
- if (session.NextSport) {
313
- session.NextSport.forEach((nextSport) => {
314
- parts.push(this.buildNextSport(nextSport));
315
- });
316
- }
317
- this.addOptionalElement(parts, "Notes", session.Notes);
318
- parts.push("</MultiSportSession>");
319
- return parts.join("\n ");
320
- }
321
- /**
322
- * 构建 FirstSport 元素
323
- */
324
- buildFirstSport(firstSport) {
325
- const parts = ["<FirstSport>"];
326
- if (firstSport.Activity) {
327
- parts.push(this.buildActivity(firstSport.Activity));
328
- }
329
- parts.push("</FirstSport>");
330
- return parts.join("\n ");
331
- }
332
- /**
333
- * 构建 NextSport 元素
334
- */
335
- buildNextSport(nextSport) {
336
- const parts = ["<NextSport>"];
337
- if (nextSport.Transition) {
338
- // Transition 使用 ActivityLap 结构
339
- parts.push(`<Transition>${this.buildActivityLap(nextSport.Transition)}</Transition>`);
340
- }
341
- if (nextSport.Activity) {
342
- parts.push(this.buildActivity(nextSport.Activity));
343
- }
344
- parts.push("</NextSport>");
345
- return parts.join("\n ");
346
- }
347
- /**
348
- * 构建 Workouts 元素
349
- */
350
- buildWorkouts(workouts) {
351
- // TODO: 实现 Workouts 构建
352
- console.warn("Workouts 构建暂未实现");
353
- return "<Workouts></Workouts>";
354
- }
355
- /**
356
- * 构建 Courses 元素
357
- */
358
- buildCourses(courses) {
359
- // TODO: 实现 Courses 构建
360
- console.warn("Courses 构建暂未实现");
361
- return "<Courses></Courses>";
362
- }
363
- /**
364
- * 构建 Author 元素
365
- */
366
- buildAuthor(author) {
367
- const parts = ["<Author>"];
368
- this.addOptionalElement(parts, "Name", author.Name);
369
- parts.push("</Author>");
370
- return parts.join("");
371
- }
372
- // ==================== 扩展处理 ====================
373
- /**
374
- * 构建扩展元素
375
- */
376
- buildExtensions(extensions) {
377
- const content = this.buildExtensionContent(extensions);
378
- return content ? `<Extensions>${content}</Extensions>` : "";
379
- }
380
- /**
381
- * 递归构建扩展内容
382
- */
383
- buildExtensionContent(extension) {
384
- return Object.entries(extension)
385
- .filter(([key]) => !key.startsWith("xmlns"))
386
- .map(([key, value]) => {
387
- if (value && typeof value === "object") {
388
- const content = this.buildExtensionContent(value);
389
- return `<${key}>${content}</${key}>`;
390
- }
391
- else if (value !== undefined && value !== null) {
392
- return `<${key}>${this.escapeXML(String(value))}</${key}>`;
393
- }
394
- return "";
395
- })
396
- .filter((content) => content)
397
- .join("");
398
- }
399
- // ==================== 辅助方法 ====================
400
- /**
401
- * 格式化时间
402
- */
403
- formatTime(time) {
404
- // 如果是字符串且已经是正确的ISO格式,直接使用
405
- if (typeof time === "string") {
406
- // 检查是否已经是标准的ISO 8601格式
407
- if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(time)) {
408
- return time;
409
- }
410
- }
411
- // 对于Date对象或其他格式的字符串,使用dayjs处理
412
- return dayjs(time).utc().format("YYYY-MM-DDTHH:mm:ss[Z]");
413
- }
414
- /**
415
- * 添加可选元素
416
- */
417
- addOptionalElement(parts, tagName, value, formatter) {
418
- if (value !== undefined && value !== null && value !== "") {
419
- const formattedValue = formatter
420
- ? formatter(value)
421
- : this.escapeXML(value);
422
- parts.push(`<${tagName}>${formattedValue}</${tagName}>`);
423
- }
424
- }
425
- /**
426
- * 添加可选数值元素
427
- */
428
- addOptionalNumericElement(parts, tagName, value) {
429
- if (value !== undefined && value !== null && !isNaN(value)) {
430
- parts.push(`<${tagName}>${value}</${tagName}>`);
431
- }
432
- }
433
- /**
434
- * 验证坐标有效性
435
- */
436
- isValidCoordinate(lat, lon) {
437
- return (lat !== undefined &&
438
- lat !== null &&
439
- lon !== undefined &&
440
- lon !== null &&
441
- !isNaN(lat) &&
442
- !isNaN(lon));
443
- }
444
- /**
445
- * 格式化坐标
446
- */
447
- formatCoordinate(coord) {
448
- return this.roundTo(coord, 6).toString();
449
- }
450
- /**
451
- * 数字四舍五入
452
- */
453
- roundTo(num, precision) {
454
- const factor = Math.pow(10, precision);
455
- return Math.round(num * factor) / factor;
456
- }
457
- /**
458
- * XML 转义
459
- */
460
- escapeXML(text) {
461
- return text
462
- .replace(/&/g, "&amp;")
463
- .replace(/</g, "&lt;")
464
- .replace(/>/g, "&gt;")
465
- .replace(/"/g, "&quot;")
466
- .replace(/'/g, "&apos;");
467
- }
468
- /**
469
- * XML 属性值转义(专门用于属性值的安全转义)
470
- */
471
- escapeXMLAttribute(text) {
472
- if (typeof text !== "string") {
473
- return String(text || "");
474
- }
475
- return text
476
- .replace(/&/g, "&amp;")
477
- .replace(/"/g, "&quot;")
478
- .replace(/'/g, "&apos;")
479
- .replace(/</g, "&lt;")
480
- .replace(/>/g, "&gt;")
481
- .replace(/\r/g, "&#13;")
482
- .replace(/\n/g, "&#10;")
483
- .replace(/\t/g, "&#9;");
484
- }
485
- /**
486
- * 深度拷贝
487
- */
488
- deepClone(obj) {
489
- if (obj === null || typeof obj !== "object") {
490
- return obj;
491
- }
492
- if (obj instanceof Date) {
493
- return new Date(obj.getTime());
494
- }
495
- if (Array.isArray(obj)) {
496
- return obj.map((item) => this.deepClone(item));
497
- }
498
- const cloned = {};
499
- for (const key in obj) {
500
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
501
- cloned[key] = this.deepClone(obj[key]);
502
- }
503
- }
504
- return cloned;
505
- }
506
- // ==================== 向后兼容 ====================
507
- /**
508
- * @deprecated 使用 encode() 替代
509
- */
510
- encoder(node) {
511
- return this.encode(node);
512
- }
513
- }
package/dist/TCX/index.js DELETED
@@ -1,9 +0,0 @@
1
- // ============ 核心类和接口 ============
2
- export { TCXDecoder } from "./decoder.js";
3
- export { AstGenerateProcessor, CompleteProcessor, ConvertProcessor, TokenizeProcessor, PipelineStage, } from "./processor.js";
4
- // ============ 基础架构 ============
5
- export * from "./base.js";
6
- // ============ 转换器 ============
7
- export * from "./converters.js";
8
- import { TCXDecoder } from "./decoder.js";
9
- export default { TCXDecoder };
@@ -1,93 +0,0 @@
1
- import { BaseTCXMiddleware } from "./base.js";
2
- // ============ 示例中间件 ============
3
- /**
4
- * 性能监控中间件
5
- */
6
- export class PerformanceMiddleware extends BaseTCXMiddleware {
7
- constructor() {
8
- super(...arguments);
9
- this.name = "performance-middleware";
10
- this.description = "监控解析性能";
11
- }
12
- async onTokenize(tokens, context) {
13
- console.log(`Token化完成,生成了 ${tokens.length} 个token`);
14
- return tokens;
15
- }
16
- async onAstGenerate(ast, context) {
17
- const nodeCount = this.countNodes(ast);
18
- console.log(`AST生成完成,包含 ${nodeCount} 个节点`);
19
- return ast;
20
- }
21
- async onComplete(result, context) {
22
- const performance = context.metadata.get("performance");
23
- console.log("解析性能统计:", performance);
24
- return result;
25
- }
26
- countNodes(ast) {
27
- let count = 1;
28
- if (ast.children) {
29
- for (const child of ast.children) {
30
- count += this.countNodes(child);
31
- }
32
- }
33
- return count;
34
- }
35
- }
36
- /**
37
- * 验证中间件
38
- */
39
- export class ValidationMiddleware extends BaseTCXMiddleware {
40
- constructor() {
41
- super(...arguments);
42
- this.name = "validation-middleware";
43
- this.description = "验证GPX数据的完整性";
44
- }
45
- async onComplete(result, context) {
46
- const errors = this.validateGPX(result);
47
- if (errors.length > 0) {
48
- console.warn("GPX验证发现问题:", errors);
49
- context.metadata.set("validation-errors", errors);
50
- }
51
- else {
52
- console.log("GPX验证通过");
53
- }
54
- return result;
55
- }
56
- validateGPX(gpx) {
57
- const errors = [];
58
- if (!gpx.version) {
59
- errors.push("缺少GPX版本信息");
60
- }
61
- if (!gpx.creator) {
62
- errors.push("缺少GPX创建者信息");
63
- }
64
- // 验证轨迹点
65
- if (gpx.trk) {
66
- for (const track of gpx.trk) {
67
- if (track.trkseg) {
68
- for (const segment of track.trkseg) {
69
- if (segment.trkpt) {
70
- for (const point of segment.trkpt) {
71
- if (typeof point.lat !== "number" ||
72
- typeof point.lon !== "number") {
73
- errors.push("轨迹点坐标无效");
74
- }
75
- }
76
- }
77
- }
78
- }
79
- }
80
- }
81
- return errors;
82
- }
83
- }
84
- // ============ 默认转换器注册函数 ============
85
- /**
86
- * 注册所有默认中间件
87
- */
88
- export async function registerDefaultMiddlewares(decoder) {
89
- const middlewares = [new PerformanceMiddleware(), new ValidationMiddleware()];
90
- for (const middleware of middlewares) {
91
- await decoder.registerMiddleware(middleware);
92
- }
93
- }