trackswap 0.0.2 → 0.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.
Files changed (82) hide show
  1. package/README.md +322 -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 +0 -39
  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 -1
  24. package/dist/TCX/middleware.d.ts +0 -9
  25. package/dist/TCX/processor.d.ts +0 -18
  26. package/dist/TCX/types.d.ts +0 -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.js +24 -5
  31. package/dist/sport/base.d.ts +63 -0
  32. package/dist/sport/converters.d.ts +33 -0
  33. package/dist/sport/encoders.d.ts +34 -0
  34. package/dist/sport/index.d.ts +17 -0
  35. package/dist/sport/processor.d.ts +19 -0
  36. package/dist/types.d.ts +91 -13
  37. package/package.json +18 -3
  38. package/dist/FIT/base.js +0 -101
  39. package/dist/FIT/crc.js +0 -16
  40. package/dist/FIT/decoder.js +0 -323
  41. package/dist/FIT/encoder.js +0 -445
  42. package/dist/FIT/index.js +0 -13
  43. package/dist/FIT/mesg.js +0 -208
  44. package/dist/FIT/middleware.js +0 -206
  45. package/dist/FIT/plugins.js +0 -104
  46. package/dist/FIT/processor.js +0 -170
  47. package/dist/FIT/types/course_point.js +0 -58
  48. package/dist/FIT/types/event.js +0 -50
  49. package/dist/FIT/types/event_type.js +0 -19
  50. package/dist/FIT/types/file.js +0 -24
  51. package/dist/FIT/types/index.js +0 -297
  52. package/dist/FIT/types/mesg_num.js +0 -106
  53. package/dist/FIT/types/sport.js +0 -48
  54. package/dist/FIT/types.js +0 -1
  55. package/dist/FIT/util.js +0 -7
  56. package/dist/GPX/base.js +0 -56
  57. package/dist/GPX/converters.js +0 -352
  58. package/dist/GPX/decoder.js +0 -409
  59. package/dist/GPX/encoder.js +0 -513
  60. package/dist/GPX/extensions/index.js +0 -14
  61. package/dist/GPX/index.js +0 -11
  62. package/dist/GPX/middleware.js +0 -93
  63. package/dist/GPX/processor.js +0 -188
  64. package/dist/GPX/types.js +0 -1
  65. package/dist/GPX/util.js +0 -18
  66. package/dist/TCX/base.js +0 -62
  67. package/dist/TCX/converters.js +0 -333
  68. package/dist/TCX/decoder.js +0 -350
  69. package/dist/TCX/encoder.js +0 -513
  70. package/dist/TCX/index.js +0 -9
  71. package/dist/TCX/middleware.js +0 -93
  72. package/dist/TCX/processor.js +0 -197
  73. package/dist/TCX/types.js +0 -1
  74. package/dist/TrackSwap.js +0 -84
  75. package/dist/core/base.js +0 -138
  76. package/dist/core/decoder.d.ts +0 -66
  77. package/dist/core/decoder.js +0 -90
  78. package/dist/core/index.js +0 -4
  79. package/dist/core/processor.d.ts +0 -69
  80. package/dist/core/processor.js +0 -265
  81. package/dist/core/xml-utils.js +0 -140
  82. package/dist/types.js +0 -1
@@ -0,0 +1,19 @@
1
+ import { ISportConverterPlugin, ISportEncoderPlugin } from "./base.js";
2
+ import { SportFileType } from "../types.js";
3
+ import { GPX11Type } from "../GPX/types.js";
4
+ import { FITFileType } from "../FIT/types.js";
5
+ import { TCXFileType } from "../TCX/types.js";
6
+ export declare class SportProcessor {
7
+ private converters;
8
+ private encoders;
9
+ constructor();
10
+ private registerDefaultConverters;
11
+ private registerDefaultEncoders;
12
+ registerConverter(converter: ISportConverterPlugin): void;
13
+ registerEncoder(encoder: ISportEncoderPlugin): void;
14
+ convertToSport(sourceData: GPX11Type | FITFileType | TCXFileType, sourceFormat: "gpx" | "fit" | "tcx"): Promise<SportFileType>;
15
+ encodeSport(sportData: SportFileType, targetFormat: "gpx" | "fit" | "tcx"): Promise<GPX11Type | FITFileType | TCXFileType>;
16
+ private findConverter;
17
+ private findEncoder;
18
+ destroy(): Promise<void>;
19
+ }
package/dist/types.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { LapMesgType, RecordMesgType, SessionMesgType, UserProfileMesgType } from "./FIT/types.js";
2
+ import { IntensityType, TriggerMethodType } from "./TCX/types.js";
1
3
  export interface Token {
2
4
  type: "open" | "close" | "text";
3
5
  tag: string;
@@ -13,22 +15,98 @@ export interface TokenAST {
13
15
  export interface AttributeType {
14
16
  [key: string]: string;
15
17
  }
16
- /**
17
- * The latitude of the point. Decimal degrees, WGS84 datum.
18
- * @type {number} -90.0 <= value <= 90.0
19
- */
20
18
  export type LatitudeType = number;
21
- /**
22
- * The longitude of the point. Decimal degrees, WGS84 datum.
23
- * @type {number} -180.0 <= value < 180.0
24
- */
25
19
  export type LongitudeType = number;
26
- /**
27
- * Used for bearing, heading, course. Units are decimal degrees, true (not magnetic).
28
- * @type {number} 0.0 <= value < 360.0
29
-
30
- */
31
20
  export type DegreesType = number;
32
21
  export interface ExtensionsType {
33
22
  [key: string]: string | number | undefined | ExtensionsType;
34
23
  }
24
+ export interface SportFileType {
25
+ metadata?: {
26
+ originType: "GPX" | "FIT" | "TCX" | "HIJSON";
27
+ fileName?: string;
28
+ fileType?: string;
29
+ creator?: string;
30
+ date?: string;
31
+ };
32
+ points?: SportPointType[];
33
+ routes?: SportRouteSegType[];
34
+ tracks?: SportTrackType[];
35
+ __fit__UserProfileMesgs?: UserProfileMesgType[];
36
+ }
37
+ export interface SportPointType {
38
+ lat: LatitudeType;
39
+ lon: LongitudeType;
40
+ ele?: number;
41
+ time?: number;
42
+ heart?: number;
43
+ distance?: number;
44
+ accumulatedPower?: number;
45
+ speed?: number;
46
+ power?: number;
47
+ cadence?: number;
48
+ temperature?: number;
49
+ enhancedAltitude?: number;
50
+ enhancedSpeed?: number;
51
+ __fit__RecordMesgType?: RecordMesgType;
52
+ magvar?: DegreesType;
53
+ geoidheight?: number;
54
+ name?: string;
55
+ cmt?: string;
56
+ desc?: string;
57
+ src?: string;
58
+ sym?: string;
59
+ type?: string;
60
+ sat?: string;
61
+ hdop?: number;
62
+ vdop?: number;
63
+ pdop?: number;
64
+ ageofdgpsdata?: number;
65
+ dgpsid?: DegreesType;
66
+ turn?: number;
67
+ extensions?: ExtensionsType;
68
+ }
69
+ export interface SportTrackType {
70
+ name?: string;
71
+ sport?: string;
72
+ trackseg?: SportRouteSegType[];
73
+ extensions?: ExtensionsType;
74
+ startTime?: number;
75
+ startLat?: LatitudeType;
76
+ startLon?: LongitudeType;
77
+ duration?: number;
78
+ distance?: number;
79
+ endTime?: number;
80
+ endLat?: LatitudeType;
81
+ endLon?: LongitudeType;
82
+ __fit__sessionMesgType?: SessionMesgType;
83
+ }
84
+ export interface SportRouteSegType {
85
+ name?: string;
86
+ sport?: string;
87
+ points?: SportPointType[];
88
+ duration?: number;
89
+ distance?: number;
90
+ startTime?: number;
91
+ endTime?: number;
92
+ avgHeartRateBpm?: number;
93
+ maxHeartRateBpm?: number;
94
+ avgSpeed?: number;
95
+ maxSpeed?: number;
96
+ avgCadenceBpm?: number;
97
+ maxCadenceBpm?: number;
98
+ avgPower?: number;
99
+ maxPower?: number;
100
+ avgTemperature?: number;
101
+ maxTemperature?: number;
102
+ avgAltitude?: number;
103
+ maxEnhancedAltitude?: number;
104
+ totalAscent?: number;
105
+ totalDescent?: number;
106
+ calories?: number;
107
+ __tcx__Intensity?: IntensityType;
108
+ __tcx__TriggerMethod?: TriggerMethodType;
109
+ __tcx__Notes?: string;
110
+ __tcx__Extensions?: any;
111
+ __fit__lapMesgType?: LapMesgType;
112
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trackswap",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "A powerful and flexible GPX parser and encoder library for Node.js and TypeScript. TrackSwap provides comprehensive support for GPX 1.1 format with advanced features like plugin system, middleware support, and optimized performance.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,13 +11,26 @@
11
11
  }
12
12
  },
13
13
  "files": [
14
- "dist/**/*",
14
+ "dist/index.js",
15
+ "dist/index.d.ts",
16
+ "dist/TrackSwap.d.ts",
17
+ "dist/types.d.ts",
18
+ "dist/**/*.d.ts",
15
19
  "README.md",
16
20
  "LICENSE"
17
21
  ],
18
22
  "scripts": {
19
23
  "dev": "ts-node src/index.ts",
20
- "build": "tsc",
24
+ "clean": "rm -rf dist",
25
+ "build": "npm run clean && npm run build:types && npm run build:prod && npm run build:optimize",
26
+ "build:types": "tsc --emitDeclarationOnly",
27
+ "build:prod": "NODE_ENV=production node build.config.js",
28
+ "build:dev": "npm run clean && npm run build:types && NODE_ENV=development node build.config.js",
29
+ "build:optimize": "node scripts/optimize-dist.js",
30
+ "build:js": "esbuild src/index.ts --bundle --platform=node --target=node14 --format=esm --outfile=dist/index.js --minify --sourcemap",
31
+ "build:tsc": "tsc && npm run minify",
32
+ "minify": "terser dist/**/*.js --compress --mangle --output dist/index.min.js --source-map",
33
+ "analyze": "NODE_ENV=production ANALYZE=true node build.config.js",
21
34
  "start": "node dist/index.js",
22
35
  "test:gpx": "cd example/gpx && rm -f output* test_*.json *_report.json *_encoded.xml && node test.js",
23
36
  "test:fit": "cd example/FIT && rm -rf output && node test.js",
@@ -54,6 +67,8 @@
54
67
  },
55
68
  "devDependencies": {
56
69
  "@types/node": "^22.15.24",
70
+ "esbuild": "^0.19.12",
71
+ "terser": "^5.27.0",
57
72
  "ts-node": "^10.9.2",
58
73
  "typescript": "^5.8.3"
59
74
  },
package/dist/FIT/base.js DELETED
@@ -1,101 +0,0 @@
1
- // ============ 基础工具类 ============
2
- /**
3
- * FIT 消息转换器基类
4
- */
5
- export class BaseFITMessageConverter {
6
- constructor() {
7
- this.version = "1.0.0";
8
- this.priority = 100;
9
- }
10
- supports(messageType) {
11
- return this.supportedMessageTypes.includes(messageType);
12
- }
13
- async initialize(context) {
14
- context.metadata.set(`plugin:${this.name}:initialized`, true);
15
- }
16
- async destroy(context) {
17
- context.metadata.delete(`plugin:${this.name}:initialized`);
18
- }
19
- async validate(context) {
20
- return true;
21
- }
22
- // ============ FIT 特定工具方法 ============
23
- parseTimestamp(timestamp) {
24
- if (!timestamp)
25
- return undefined;
26
- const date = new Date(timestamp);
27
- return isNaN(date.getTime()) ? undefined : date;
28
- }
29
- calculateDuration(startTime, endTime) {
30
- if (!startTime || !endTime)
31
- return 0;
32
- const start = new Date(startTime).getTime();
33
- const end = new Date(endTime).getTime();
34
- return Math.max(0, (end - start) / 1000); // 返回秒数
35
- }
36
- filterMessagesByTimeRange(messages, startTime, endTime, tolerance = 2000 // 默认2秒容错
37
- ) {
38
- const start = new Date(startTime).getTime();
39
- const end = new Date(endTime).getTime();
40
- return messages.filter((message) => {
41
- if (!message.timestamp)
42
- return false;
43
- const messageTime = new Date(message.timestamp).getTime();
44
- return messageTime >= start - tolerance && messageTime <= end + tolerance;
45
- });
46
- }
47
- groupMessagesByLap(records, laps) {
48
- return laps.map((lap) => {
49
- const lapRecords = this.filterMessagesByTimeRange(records, lap.startTime, lap.timestamp);
50
- return { lap, records: lapRecords };
51
- });
52
- }
53
- }
54
- /**
55
- * FIT 数据结构化插件基类
56
- */
57
- export class BaseFITStructurePlugin {
58
- constructor() {
59
- this.version = "1.0.0";
60
- this.priority = 100;
61
- }
62
- async initialize(context) {
63
- context.metadata.set(`plugin:${this.name}:initialized`, true);
64
- }
65
- async destroy(context) {
66
- context.metadata.delete(`plugin:${this.name}:initialized`);
67
- }
68
- async validate(context) {
69
- return true;
70
- }
71
- }
72
- /**
73
- * FIT 中间件基类
74
- */
75
- export class BaseFITMiddleware {
76
- constructor() {
77
- this.version = "1.0.0";
78
- this.description = "";
79
- this.priority = 100;
80
- }
81
- async initialize(context) { }
82
- async destroy(context) { }
83
- async validate(context) {
84
- return true;
85
- }
86
- async onParse(buffer, context) {
87
- return buffer;
88
- }
89
- async onExtractMessages(messages, context) {
90
- return messages;
91
- }
92
- async onStructure(result, context) {
93
- return result;
94
- }
95
- async onComplete(result, context) {
96
- return result;
97
- }
98
- async onError(error, context) {
99
- console.error(`FIT中间件 ${this.name} 处理错误:`, error);
100
- }
101
- }
package/dist/FIT/crc.js DELETED
@@ -1,16 +0,0 @@
1
- // prettier-ignore
2
- const crc_table = [
3
- 0x0000, 0xcc01, 0xd801, 0x1400, 0xf001, 0x3c00, 0x2800, 0xe401,
4
- 0xa001, 0x6c00, 0x7800, 0xb401, 0x5000, 0x9c01, 0x8801, 0x4400
5
- ];
6
- const update_nibble = (crc, nibble) => ((crc >>> 4) ^ crc_table[(crc & 0xf) ^ nibble]) & 0xffff;
7
- const update_nibbles = (crc, lo, hi) => update_nibble(update_nibble(crc, lo), hi);
8
- const update = (crc, byte) => update_nibbles(crc, byte & 0xf, byte >>> 4);
9
- export function crc(buffer, initial = 0) {
10
- let crcValue = initial;
11
- const uint8Array = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;
12
- for (let i = 0; i < uint8Array.length; i++) {
13
- crcValue = update(crcValue, uint8Array[i]);
14
- }
15
- return crcValue;
16
- }
@@ -1,323 +0,0 @@
1
- import { ParseProcessor, ExtractProcessor, StructureProcessor, CompleteProcessor, FITPipelineStage, } from "./processor.js";
2
- import { SessionStructurePlugin, CourseStructurePlugin, FileHeaderPlugin, } from "./plugins.js";
3
- /**
4
- * FIT 解码器 - 遵循 GPX 风格的插件架构
5
- */
6
- export class FITDecoder {
7
- constructor() {
8
- this.converterPlugins = new Map();
9
- this.registeredPlugins = new Set(); // 跟踪已注册的插件名
10
- this.structurePlugins = [];
11
- this.middlewarePlugins = [];
12
- this.processors = []; // 固定的核心处理器
13
- this.initialized = false;
14
- this.defaultPluginsRegistered = false;
15
- this.initializePipeline();
16
- }
17
- /**
18
- * 初始化流水线 - 注册固定的核心处理器
19
- */
20
- initializePipeline() {
21
- // 固定的核心处理器,不允许用户修改
22
- this.processors = [
23
- new ParseProcessor(),
24
- new ExtractProcessor((messageType) => this.getConverters(messageType)),
25
- new StructureProcessor(() => this.structurePlugins),
26
- new CompleteProcessor(),
27
- ];
28
- console.log("🔧 FIT核心流水线处理器已初始化 (固定不可修改)");
29
- }
30
- /**
31
- * 自动注册默认插件
32
- */
33
- async registerDefaultPlugins() {
34
- if (this.defaultPluginsRegistered)
35
- return;
36
- const defaultStructurePlugins = [
37
- new FileHeaderPlugin(),
38
- new SessionStructurePlugin(),
39
- new CourseStructurePlugin(),
40
- ];
41
- for (const plugin of defaultStructurePlugins) {
42
- this.addStructurePlugin(plugin);
43
- this.registeredPlugins.add(plugin.name);
44
- }
45
- this.defaultPluginsRegistered = true;
46
- console.log("✅ 默认FIT结构化插件已自动注册 (FileHeader, Session, Course等)");
47
- }
48
- /**
49
- * 为指定消息类型添加转换器,按优先级排序
50
- */
51
- addConverterForMessageType(messageType, converter) {
52
- if (!this.converterPlugins.has(messageType)) {
53
- this.converterPlugins.set(messageType, []);
54
- }
55
- const converters = this.converterPlugins.get(messageType);
56
- converters.push(converter);
57
- // 按优先级排序(数字越小优先级越高)
58
- converters.sort((a, b) => (a.priority || 100) - (b.priority || 100));
59
- }
60
- /**
61
- * 添加结构化插件
62
- */
63
- addStructurePlugin(plugin) {
64
- this.structurePlugins.push(plugin);
65
- // 按优先级排序
66
- this.structurePlugins.sort((a, b) => (a.priority || 100) - (b.priority || 100));
67
- }
68
- /**
69
- * 注册消息转换器插件
70
- */
71
- async registerMessageConverter(plugin) {
72
- // 检查插件是否已注册
73
- if (this.registeredPlugins.has(plugin.name)) {
74
- throw new Error(`消息转换器插件 ${plugin.name} 已存在`);
75
- }
76
- // 注册支持的消息类型
77
- plugin.supportedMessageTypes.forEach((messageType) => {
78
- this.addConverterForMessageType(messageType, plugin);
79
- });
80
- this.registeredPlugins.add(plugin.name);
81
- // 显示优先级信息
82
- const priorityInfo = plugin.priority !== undefined ? ` (优先级: ${plugin.priority})` : "";
83
- console.log(`📦 已注册FIT消息转换器: ${plugin.name}${priorityInfo}, 支持消息类型: ${plugin.supportedMessageTypes.join(", ")}`);
84
- // 显示消息类型的转换器优先级顺序
85
- plugin.supportedMessageTypes.forEach((messageType) => {
86
- const converters = this.converterPlugins.get(messageType) || [];
87
- const converterNames = converters.map((c) => `${c.name}(${c.priority || 100})`);
88
- console.log(` 📋 消息类型 "${messageType}" 转换器优先级: ${converterNames.join(" > ")}`);
89
- });
90
- }
91
- /**
92
- * 注册结构化插件
93
- */
94
- async registerStructurePlugin(plugin) {
95
- if (this.registeredPlugins.has(plugin.name)) {
96
- throw new Error(`结构化插件 ${plugin.name} 已存在`);
97
- }
98
- this.addStructurePlugin(plugin);
99
- this.registeredPlugins.add(plugin.name);
100
- const priorityInfo = plugin.priority !== undefined ? ` (优先级: ${plugin.priority})` : "";
101
- console.log(`🏗️ 已注册FIT结构化插件: ${plugin.name}${priorityInfo}`);
102
- }
103
- /**
104
- * 注册中间件插件
105
- */
106
- async registerMiddleware(plugin) {
107
- const existingIndex = this.middlewarePlugins.findIndex((p) => p.name === plugin.name);
108
- if (existingIndex !== -1) {
109
- this.middlewarePlugins[existingIndex] = plugin;
110
- console.log(`🔄 已更新FIT中间件: ${plugin.name}`);
111
- }
112
- else {
113
- this.middlewarePlugins.push(plugin);
114
- // 按优先级排序
115
- this.middlewarePlugins.sort((a, b) => (a.priority || 100) - (b.priority || 100));
116
- console.log(`🔌 已注册FIT中间件: ${plugin.name}`);
117
- }
118
- // 显示中间件支持的钩子
119
- const hooks = [];
120
- if (plugin.onParse)
121
- hooks.push("onParse");
122
- if (plugin.onExtractMessages)
123
- hooks.push("onExtractMessages");
124
- if (plugin.onStructure)
125
- hooks.push("onStructure");
126
- if (plugin.onComplete)
127
- hooks.push("onComplete");
128
- if (plugin.onError)
129
- hooks.push("onError");
130
- if (hooks.length > 0) {
131
- console.log(` 🎣 支持的钩子: ${hooks.join(", ")}`);
132
- }
133
- }
134
- /**
135
- * 获取消息类型转换器(返回优先级最高的)
136
- */
137
- getConverter(messageType) {
138
- const converters = this.converterPlugins.get(messageType);
139
- return converters && converters.length > 0 ? converters[0] : undefined;
140
- }
141
- /**
142
- * 获取指定消息类型的所有转换器(按优先级排序)
143
- */
144
- getConverters(messageType) {
145
- return this.converterPlugins.get(messageType) || [];
146
- }
147
- /**
148
- * 移除插件
149
- */
150
- unregisterPlugin(pluginName) {
151
- if (!this.registeredPlugins.has(pluginName)) {
152
- console.warn(`FIT插件 ${pluginName} 不存在`);
153
- return false;
154
- }
155
- // 从消息转换器中移除
156
- for (const [messageType, converters] of this.converterPlugins) {
157
- const index = converters.findIndex((plugin) => plugin.name === pluginName);
158
- if (index !== -1) {
159
- converters.splice(index, 1);
160
- if (converters.length === 0) {
161
- this.converterPlugins.delete(messageType);
162
- }
163
- }
164
- }
165
- // 从结构化插件中移除
166
- const structureIndex = this.structurePlugins.findIndex((plugin) => plugin.name === pluginName);
167
- if (structureIndex !== -1) {
168
- this.structurePlugins.splice(structureIndex, 1);
169
- }
170
- // 从中间件中移除
171
- const middlewareIndex = this.middlewarePlugins.findIndex((plugin) => plugin.name === pluginName);
172
- if (middlewareIndex !== -1) {
173
- this.middlewarePlugins.splice(middlewareIndex, 1);
174
- }
175
- this.registeredPlugins.delete(pluginName);
176
- console.log(`🗑️ 已移除FIT插件: ${pluginName}`);
177
- return true;
178
- }
179
- /**
180
- * 初始化解码器
181
- */
182
- async initialize() {
183
- if (this.initialized)
184
- return;
185
- // 首先注册默认插件
186
- await this.registerDefaultPlugins();
187
- const context = this.createContext();
188
- // 初始化所有插件
189
- const allPlugins = [
190
- ...Array.from(this.converterPlugins.values()).flat(),
191
- ...this.structurePlugins,
192
- ...this.middlewarePlugins,
193
- ];
194
- for (const plugin of allPlugins) {
195
- try {
196
- await plugin.initialize?.(context);
197
- }
198
- catch (error) {
199
- console.error(`FIT插件 ${plugin.name} 初始化失败:`, error);
200
- }
201
- }
202
- this.initialized = true;
203
- }
204
- /**
205
- * 销毁解码器
206
- */
207
- async destroy() {
208
- if (!this.initialized)
209
- return;
210
- const context = this.createContext();
211
- // 销毁所有插件
212
- const allPlugins = [
213
- ...Array.from(this.converterPlugins.values()).flat(),
214
- ...this.structurePlugins,
215
- ...this.middlewarePlugins,
216
- ];
217
- for (const plugin of allPlugins) {
218
- try {
219
- await plugin.destroy?.(context);
220
- }
221
- catch (error) {
222
- console.error(`FIT插件 ${plugin.name} 销毁失败:`, error);
223
- }
224
- }
225
- this.initialized = false;
226
- }
227
- /**
228
- * 创建解码上下文
229
- */
230
- createContext() {
231
- return {
232
- metadata: new Map(),
233
- errors: [],
234
- warnings: [],
235
- stats: {
236
- startTime: Date.now(),
237
- processedTokens: 0,
238
- convertedElements: 0,
239
- },
240
- performance: {
241
- startTime: Date.now(),
242
- },
243
- };
244
- }
245
- /**
246
- * 执行中间件钩子
247
- */
248
- async executeMiddlewareHook(hookName, data, context) {
249
- let result = data;
250
- for (const middleware of this.middlewarePlugins) {
251
- const hook = middleware[hookName];
252
- if (hook) {
253
- try {
254
- result = (await hook.call(middleware, result, context)) || result;
255
- }
256
- catch (error) {
257
- context.errors.push(error);
258
- await middleware.onError?.(error, context);
259
- }
260
- }
261
- }
262
- return result;
263
- }
264
- /**
265
- * 主解析方法 - 固定的流水线 + 灵活的中间件
266
- */
267
- async parseByBuffer(buffer, options = {}) {
268
- await this.initialize();
269
- let context = this.createContext();
270
- context.rawData = buffer;
271
- context.userData = options.userData;
272
- // 在上下文中传递解码器实例引用
273
- context.metadata.set("decoder", this);
274
- try {
275
- // 固定的流水线阶段顺序执行
276
- for (const processor of this.processors) {
277
- try {
278
- // 1. 执行核心处理器
279
- context = await processor.process(context);
280
- // 2. 根据阶段执行对应的中间件钩子
281
- switch (processor.stage) {
282
- case FITPipelineStage.PARSE:
283
- if (context.rawData) {
284
- context.rawData = await this.executeMiddlewareHook("onParse", context.rawData, context);
285
- }
286
- break;
287
- case FITPipelineStage.EXTRACT:
288
- if (context.rawMessages) {
289
- context.rawMessages = await this.executeMiddlewareHook("onExtractMessages", context.rawMessages, context);
290
- }
291
- break;
292
- case FITPipelineStage.STRUCTURE:
293
- if (context.result) {
294
- context.result = await this.executeMiddlewareHook("onStructure", context.result, context);
295
- }
296
- break;
297
- case FITPipelineStage.COMPLETE:
298
- if (context.result) {
299
- context.result = await this.executeMiddlewareHook("onComplete", context.result, context);
300
- }
301
- break;
302
- }
303
- }
304
- catch (error) {
305
- context.errors.push(error);
306
- // 执行错误处理中间件
307
- for (const middleware of this.middlewarePlugins) {
308
- await middleware.onError?.(error, context);
309
- }
310
- console.error(`FIT流水线阶段 ${processor.stage} 处理失败:`, error);
311
- }
312
- }
313
- return context.result || {};
314
- }
315
- catch (error) {
316
- console.error("FIT解析失败:", error);
317
- throw error;
318
- }
319
- }
320
- }
321
- export default {
322
- FITDecoder,
323
- };