trackswap 0.0.1

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.
@@ -0,0 +1,403 @@
1
+ import { BoundsConverter, CopyrightConverter, EmailConverter, LinkConverter, MetadataConverter, PersonConverter, RteConverter, TrkConverter, TrksegConverter, WptConverter, } from "./converters.js";
2
+ import { AstGenerateProcessor, CompleteProcessor, ConvertProcessor, TokenizeProcessor, PipelineStage, } from "./processor.js";
3
+ // ============ 主解码器类 ============
4
+ /**
5
+ * GPX解码器主类
6
+ */
7
+ export class GPXDecoder {
8
+ constructor() {
9
+ this.converterPlugins = new Map();
10
+ this.registeredPlugins = new Set(); // 跟踪已注册的插件名
11
+ this.middlewarePlugins = [];
12
+ this.processors = []; // 固定的核心处理器
13
+ this.initialized = false;
14
+ this.defaultConvertersRegistered = false;
15
+ this.initializePipeline();
16
+ }
17
+ /**
18
+ * 初始化流水线 - 注册固定的核心处理器
19
+ */
20
+ initializePipeline() {
21
+ // 固定的核心处理器,不允许用户修改
22
+ this.processors = [
23
+ new TokenizeProcessor(),
24
+ new AstGenerateProcessor(),
25
+ new ConvertProcessor((tag) => this.getConverter(tag)),
26
+ new CompleteProcessor(),
27
+ ];
28
+ console.log("🔧 核心流水线处理器已初始化 (固定不可修改)");
29
+ }
30
+ /**
31
+ * 自动注册默认转换器 - 处理标准GPX标签
32
+ */
33
+ async registerDefaultConverters() {
34
+ if (this.defaultConvertersRegistered)
35
+ return;
36
+ const defaultConverters = [
37
+ new WptConverter(),
38
+ new RteConverter(),
39
+ new TrkConverter(),
40
+ new TrksegConverter(),
41
+ new LinkConverter(),
42
+ new MetadataConverter(),
43
+ new PersonConverter(),
44
+ new EmailConverter(),
45
+ new CopyrightConverter(),
46
+ new BoundsConverter(),
47
+ ];
48
+ for (const converter of defaultConverters) {
49
+ // 内部注册,不显示日志避免干扰
50
+ converter.supportedTags?.forEach((tag) => {
51
+ this.addConverterForTag(tag, converter);
52
+ });
53
+ this.registeredPlugins.add(converter.name);
54
+ }
55
+ this.defaultConvertersRegistered = true;
56
+ console.log("✅ 默认GPX转换器已自动注册 (wpt, rte, trk, metadata等)");
57
+ }
58
+ /**
59
+ * 为指定标签添加转换器,按优先级排序
60
+ */
61
+ addConverterForTag(tag, converter) {
62
+ if (!this.converterPlugins.has(tag)) {
63
+ this.converterPlugins.set(tag, []);
64
+ }
65
+ const converters = this.converterPlugins.get(tag);
66
+ converters.push(converter);
67
+ // 按优先级排序(数字越小优先级越高)
68
+ converters.sort((a, b) => (a.priority || 100) - (b.priority || 100));
69
+ }
70
+ /**
71
+ * 注册转换器插件
72
+ */
73
+ async registerConverter(plugin) {
74
+ // 检查插件是否已注册
75
+ if (this.registeredPlugins.has(plugin.name)) {
76
+ throw new Error(`转换器插件 ${plugin.name} 已存在`);
77
+ }
78
+ // 注册支持的标签(允许多个转换器处理同一标签)
79
+ plugin.supportedTags?.forEach((tag) => {
80
+ this.addConverterForTag(tag, plugin);
81
+ });
82
+ this.registeredPlugins.add(plugin.name);
83
+ // 显示优先级信息
84
+ const priorityInfo = plugin.priority !== undefined ? ` (优先级: ${plugin.priority})` : "";
85
+ console.log(`📦 已注册自定义转换器: ${plugin.name}${priorityInfo}, 支持标签: ${plugin.supportedTags?.join(", ")}`);
86
+ // 显示标签的转换器优先级顺序
87
+ plugin.supportedTags?.forEach((tag) => {
88
+ const converters = this.converterPlugins.get(tag) || [];
89
+ const converterNames = converters.map((c) => `${c.name}(${c.priority || 100})`);
90
+ console.log(` 📋 标签 "${tag}" 转换器优先级: ${converterNames.join(" > ")}`);
91
+ });
92
+ }
93
+ /**
94
+ * 注册中间件插件 - 真正的扩展点
95
+ */
96
+ async registerMiddleware(plugin) {
97
+ const existingIndex = this.middlewarePlugins.findIndex((p) => p.name === plugin.name);
98
+ if (existingIndex !== -1) {
99
+ this.middlewarePlugins[existingIndex] = plugin;
100
+ console.log(`🔄 已更新中间件: ${plugin.name}`);
101
+ }
102
+ else {
103
+ this.middlewarePlugins.push(plugin);
104
+ console.log(`🔌 已注册中间件: ${plugin.name}`);
105
+ }
106
+ // 显示中间件支持的钩子
107
+ const hooks = [];
108
+ if (plugin.onTokenize)
109
+ hooks.push("onTokenize");
110
+ if (plugin.onAstGenerate)
111
+ hooks.push("onAstGenerate");
112
+ if (plugin.onConvert)
113
+ hooks.push("onConvert");
114
+ if (plugin.onComplete)
115
+ hooks.push("onComplete");
116
+ if (plugin.onError)
117
+ hooks.push("onError");
118
+ if (hooks.length > 0) {
119
+ console.log(` 🎣 支持的钩子: ${hooks.join(", ")}`);
120
+ }
121
+ }
122
+ /**
123
+ * 获取转换器(返回优先级最高的)
124
+ */
125
+ getConverter(tag) {
126
+ const converters = this.converterPlugins.get(tag);
127
+ return converters && converters.length > 0 ? converters[0] : undefined;
128
+ }
129
+ /**
130
+ * 获取指定标签的所有转换器(按优先级排序)
131
+ */
132
+ getAllConverters(tag) {
133
+ return this.converterPlugins.get(tag) || [];
134
+ }
135
+ /**
136
+ * 移除转换器
137
+ */
138
+ unregisterConverter(pluginName) {
139
+ // 找到插件实例
140
+ let pluginToRemove;
141
+ for (const [tag, converters] of this.converterPlugins) {
142
+ const index = converters.findIndex((plugin) => plugin.name === pluginName);
143
+ if (index !== -1) {
144
+ pluginToRemove = converters[index];
145
+ converters.splice(index, 1);
146
+ // 如果该标签没有转换器了,删除整个条目
147
+ if (converters.length === 0) {
148
+ this.converterPlugins.delete(tag);
149
+ }
150
+ }
151
+ }
152
+ if (!pluginToRemove) {
153
+ console.warn(`转换器插件 ${pluginName} 不存在`);
154
+ return;
155
+ }
156
+ this.registeredPlugins.delete(pluginName);
157
+ console.log(`🗑️ 已移除转换器: ${pluginName}`);
158
+ }
159
+ /**
160
+ * 移除中间件
161
+ */
162
+ unregisterMiddleware(name) {
163
+ const index = this.middlewarePlugins.findIndex((p) => p.name === name);
164
+ if (index !== -1) {
165
+ this.middlewarePlugins.splice(index, 1);
166
+ console.log(`🗑️ 已移除中间件: ${name}`);
167
+ }
168
+ else {
169
+ console.warn(`中间件 ${name} 不存在`);
170
+ }
171
+ }
172
+ /**
173
+ * 列出所有已注册的转换器标签
174
+ */
175
+ listConverters() {
176
+ return Array.from(this.converterPlugins.keys());
177
+ }
178
+ /**
179
+ * 列出所有已注册的转换器插件
180
+ */
181
+ listConverterPlugins() {
182
+ return Array.from(this.registeredPlugins);
183
+ }
184
+ /**
185
+ * 列出所有已注册的中间件
186
+ */
187
+ listMiddlewares() {
188
+ return this.middlewarePlugins.map((p) => p.name);
189
+ }
190
+ /**
191
+ * 列出核心处理器(只读)
192
+ */
193
+ listProcessors() {
194
+ return this.processors.map((p) => p.constructor.name);
195
+ }
196
+ /**
197
+ * 获取标签的转换器详情(包括优先级信息)
198
+ */
199
+ getConverterDetails(tag) {
200
+ const result = {};
201
+ if (tag) {
202
+ const converters = this.converterPlugins.get(tag);
203
+ if (converters) {
204
+ result[tag] = converters.map((c) => ({
205
+ name: c.name,
206
+ priority: c.priority || 100,
207
+ }));
208
+ }
209
+ }
210
+ else {
211
+ for (const [tagName, converters] of this.converterPlugins) {
212
+ result[tagName] = converters.map((c) => ({
213
+ name: c.name,
214
+ priority: c.priority || 100,
215
+ }));
216
+ }
217
+ }
218
+ return result;
219
+ }
220
+ /**
221
+ * 获取中间件详情(包括支持的钩子)
222
+ */
223
+ getMiddlewareDetails() {
224
+ return this.middlewarePlugins.map((plugin) => {
225
+ const hooks = [];
226
+ if (plugin.onTokenize)
227
+ hooks.push("onTokenize");
228
+ if (plugin.onAstGenerate)
229
+ hooks.push("onAstGenerate");
230
+ if (plugin.onConvert)
231
+ hooks.push("onConvert");
232
+ if (plugin.onComplete)
233
+ hooks.push("onComplete");
234
+ if (plugin.onError)
235
+ hooks.push("onError");
236
+ return {
237
+ name: plugin.name,
238
+ hooks,
239
+ };
240
+ });
241
+ }
242
+ /**
243
+ * 初始化解码器
244
+ */
245
+ async initialize() {
246
+ if (this.initialized)
247
+ return;
248
+ // 首先注册默认转换器
249
+ await this.registerDefaultConverters();
250
+ const context = this.createContext();
251
+ // 初始化所有插件
252
+ const allPlugins = [
253
+ ...Array.from(this.converterPlugins.values()).flat(),
254
+ ...this.middlewarePlugins,
255
+ ];
256
+ for (const plugin of allPlugins) {
257
+ try {
258
+ await plugin.initialize?.(context);
259
+ }
260
+ catch (error) {
261
+ console.error(`插件 ${plugin.name} 初始化失败:`, error);
262
+ }
263
+ }
264
+ this.initialized = true;
265
+ }
266
+ /**
267
+ * 销毁解码器
268
+ */
269
+ async destroy() {
270
+ if (!this.initialized)
271
+ return;
272
+ const context = this.createContext();
273
+ // 销毁所有插件
274
+ const allPlugins = [
275
+ ...Array.from(this.converterPlugins.values()).flat(),
276
+ ...this.middlewarePlugins,
277
+ ];
278
+ for (const plugin of allPlugins) {
279
+ try {
280
+ await plugin.destroy?.(context);
281
+ }
282
+ catch (error) {
283
+ console.error(`插件 ${plugin.name} 销毁失败:`, error);
284
+ }
285
+ }
286
+ this.initialized = false;
287
+ }
288
+ /**
289
+ * 创建解码上下文
290
+ */
291
+ createContext() {
292
+ return {
293
+ metadata: new Map(),
294
+ errors: [],
295
+ performance: {
296
+ startTime: Date.now(),
297
+ },
298
+ };
299
+ }
300
+ /**
301
+ * 执行中间件钩子
302
+ */
303
+ async executeMiddlewareHook(hookName, data, context) {
304
+ let result = data;
305
+ for (const middleware of this.middlewarePlugins) {
306
+ const hook = middleware[hookName];
307
+ if (hook) {
308
+ try {
309
+ result = (await hook.call(middleware, result, context)) || result;
310
+ }
311
+ catch (error) {
312
+ context.errors.push(error);
313
+ await middleware.onError?.(error, context);
314
+ }
315
+ }
316
+ }
317
+ return result;
318
+ }
319
+ /**
320
+ * 主解析方法 - 固定的流水线 + 灵活的中间件
321
+ */
322
+ async parserByBuffer(buffer) {
323
+ await this.initialize();
324
+ let context = this.createContext();
325
+ context.rawData = buffer;
326
+ context.xmlContent = buffer.toString("utf-8");
327
+ // 在上下文中传递解码器实例引用
328
+ context.metadata.set("decoder", this);
329
+ try {
330
+ // 固定的流水线阶段顺序执行
331
+ for (const processor of this.processors) {
332
+ try {
333
+ // 1. 执行核心处理器
334
+ context = await processor.process(context);
335
+ // 2. 根据阶段执行对应的中间件钩子
336
+ switch (processor.stage) {
337
+ case PipelineStage.TOKENIZE:
338
+ if (context.tokens) {
339
+ context.tokens = await this.executeMiddlewareHook("onTokenize", context.tokens, context);
340
+ }
341
+ break;
342
+ case PipelineStage.AST_GENERATE:
343
+ if (context.ast) {
344
+ context.ast = await this.executeMiddlewareHook("onAstGenerate", context.ast, context);
345
+ }
346
+ break;
347
+ case PipelineStage.CONVERT:
348
+ if (context.result) {
349
+ context.result = await this.executeMiddlewareHook("onConvert", context.result, context);
350
+ }
351
+ break;
352
+ case PipelineStage.COMPLETE:
353
+ if (context.result) {
354
+ context.result = await this.executeMiddlewareHook("onComplete", context.result, context);
355
+ }
356
+ break;
357
+ }
358
+ }
359
+ catch (error) {
360
+ context.errors.push(error);
361
+ // 执行错误处理中间件
362
+ for (const middleware of this.middlewarePlugins) {
363
+ await middleware.onError?.(error, context);
364
+ }
365
+ console.error(`流水线阶段 ${processor.stage} 处理失败:`, error);
366
+ }
367
+ }
368
+ return context.result;
369
+ }
370
+ catch (error) {
371
+ console.error("GPX解析失败:", error);
372
+ throw error;
373
+ }
374
+ }
375
+ /**
376
+ * 转换Extensions(向后兼容)
377
+ */
378
+ convertExtensions(extensionsAST) {
379
+ const extensions = {};
380
+ const { attributes, tag, value } = extensionsAST;
381
+ // 处理属性
382
+ if (attributes) {
383
+ Object.entries(attributes).forEach(([key, value]) => {
384
+ extensions[key] = value;
385
+ });
386
+ }
387
+ // 如果有子节点,直接处理子节点,不再包装外层标签
388
+ if (extensionsAST?.children?.length) {
389
+ extensionsAST.children?.forEach((child) => {
390
+ extensions[child.tag] =
391
+ this.convertExtensions(child);
392
+ });
393
+ }
394
+ else if (value !== undefined) {
395
+ // 如果没有子节点但有值,直接返回值
396
+ return value;
397
+ }
398
+ return extensions;
399
+ }
400
+ }
401
+ export default {
402
+ GPXDecoder,
403
+ };
@@ -0,0 +1,150 @@
1
+ import { GPX11Type } from "./types.js";
2
+ /**
3
+ * GPX 编码器 - 将 GPX11Type 数据编码为 GPX XML 格式
4
+ */
5
+ export declare class GPXEncoder {
6
+ private compact;
7
+ /**
8
+ * 构造函数
9
+ * @param options 编码器选项
10
+ */
11
+ constructor(options?: {
12
+ compact?: boolean;
13
+ });
14
+ /**
15
+ * 编码 GPX 数据为 Buffer
16
+ */
17
+ encode(gpxData: GPX11Type): Promise<Buffer>;
18
+ /**
19
+ * 编码 GPX 数据为 XML 字符串
20
+ */
21
+ encodeToString(gpxData: GPX11Type): string;
22
+ /**
23
+ * 构建完整的 GPX XML
24
+ */
25
+ private buildGPXXML;
26
+ /**
27
+ * 压缩XML字符串,去掉多余的换行和空格
28
+ */
29
+ private compressXML;
30
+ /**
31
+ * 构建 XML 头部
32
+ */
33
+ private buildXMLHeader;
34
+ /**
35
+ * 构建 GPX 内容
36
+ */
37
+ private buildGPXContent;
38
+ /**
39
+ * 构建 metadata 元素
40
+ */
41
+ private buildMetadata;
42
+ /**
43
+ * 构建 author 元素
44
+ */
45
+ private buildAuthor;
46
+ /**
47
+ * 构建 copyright 元素
48
+ */
49
+ private buildCopyright;
50
+ /**
51
+ * 构建 bounds 元素
52
+ */
53
+ private buildBounds;
54
+ /**
55
+ * 构建航路点元素
56
+ */
57
+ private buildWaypoint;
58
+ /**
59
+ * 添加标准航路点元素
60
+ */
61
+ private addStandardWaypointElements;
62
+ /**
63
+ * 构建 rte 元素
64
+ */
65
+ private buildRoute;
66
+ /**
67
+ * 构建轨迹元素
68
+ */
69
+ private buildTrack;
70
+ /**
71
+ * 构建轨迹段元素
72
+ */
73
+ private buildTrackSegment;
74
+ /**
75
+ * 添加路线和轨迹的公共元素
76
+ */
77
+ private addRouteTrackCommonElements;
78
+ /**
79
+ * 处理点的扩展属性
80
+ */
81
+ private processPointExtensions;
82
+ /**
83
+ * 构建 TrackPoint 扩展
84
+ */
85
+ private buildTrackPointExtension;
86
+ /**
87
+ * 构建 Power 扩展
88
+ */
89
+ private buildPowerExtension;
90
+ /**
91
+ * 构建 GPX 扩展
92
+ */
93
+ private buildGpxExtensions;
94
+ /**
95
+ * 构建扩展元素
96
+ */
97
+ private buildExtensions;
98
+ /**
99
+ * 递归构建扩展内容
100
+ */
101
+ private buildExtensionContent;
102
+ /**
103
+ * 构建 link 元素
104
+ */
105
+ private buildLink;
106
+ /**
107
+ * 添加多个 link 元素
108
+ */
109
+ private addLinks;
110
+ /**
111
+ * 构建时间元素
112
+ */
113
+ private buildTimeElement;
114
+ /**
115
+ * 添加可选元素
116
+ */
117
+ private addOptionalElement;
118
+ /**
119
+ * 验证坐标有效性
120
+ */
121
+ private isValidCoordinate;
122
+ /**
123
+ * 格式化坐标
124
+ */
125
+ private formatCoordinate;
126
+ /**
127
+ * 格式化高程
128
+ */
129
+ private formatElevation;
130
+ /**
131
+ * 数字四舍五入
132
+ */
133
+ private roundTo;
134
+ /**
135
+ * XML 转义
136
+ */
137
+ private escapeXML;
138
+ /**
139
+ * XML 属性值转义(专门用于属性值的安全转义)
140
+ */
141
+ private escapeXMLAttribute;
142
+ /**
143
+ * 深度拷贝
144
+ */
145
+ private deepClone;
146
+ /**
147
+ * @deprecated 使用 encode() 替代
148
+ */
149
+ encoder(node: GPX11Type): Promise<Buffer>;
150
+ }