trackswap 0.0.1 → 0.0.2

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 (78) hide show
  1. package/dist/FIT/base.d.ts +91 -0
  2. package/dist/FIT/base.js +101 -0
  3. package/dist/FIT/crc.d.ts +1 -0
  4. package/dist/FIT/crc.js +16 -0
  5. package/dist/FIT/decoder.d.ts +83 -0
  6. package/dist/FIT/decoder.js +323 -0
  7. package/dist/FIT/encoder.d.ts +142 -0
  8. package/dist/FIT/encoder.js +445 -0
  9. package/dist/FIT/index.d.ts +12 -0
  10. package/dist/FIT/index.js +13 -0
  11. package/dist/FIT/mesg.d.ts +40 -0
  12. package/dist/FIT/mesg.js +208 -0
  13. package/dist/FIT/middleware.d.ts +48 -0
  14. package/dist/FIT/middleware.js +206 -0
  15. package/dist/FIT/plugins.d.ts +26 -0
  16. package/dist/FIT/plugins.js +104 -0
  17. package/dist/FIT/processor.d.ts +58 -0
  18. package/dist/FIT/processor.js +170 -0
  19. package/dist/FIT/types/course_point.d.ts +56 -0
  20. package/dist/FIT/types/course_point.js +58 -0
  21. package/dist/FIT/types/event.d.ts +49 -0
  22. package/dist/FIT/types/event.js +50 -0
  23. package/dist/FIT/types/event_type.d.ts +18 -0
  24. package/dist/FIT/types/event_type.js +19 -0
  25. package/dist/FIT/types/file.d.ts +23 -0
  26. package/dist/FIT/types/file.js +24 -0
  27. package/dist/FIT/types/index.d.ts +119 -0
  28. package/dist/FIT/types/index.js +297 -0
  29. package/dist/FIT/types/mesg_num.d.ts +105 -0
  30. package/dist/FIT/types/mesg_num.js +106 -0
  31. package/dist/FIT/types/sport.d.ts +47 -0
  32. package/dist/FIT/types/sport.js +48 -0
  33. package/dist/FIT/types.d.ts +231 -0
  34. package/dist/FIT/types.js +1 -0
  35. package/dist/FIT/util.d.ts +1 -0
  36. package/dist/FIT/util.js +7 -0
  37. package/dist/GPX/base.d.ts +43 -67
  38. package/dist/GPX/base.js +40 -67
  39. package/dist/GPX/converters.d.ts +12 -36
  40. package/dist/GPX/converters.js +11 -103
  41. package/dist/GPX/decoder.d.ts +5 -5
  42. package/dist/GPX/decoder.js +6 -0
  43. package/dist/GPX/encoder.d.ts +4 -0
  44. package/dist/GPX/encoder.js +3 -0
  45. package/dist/GPX/index.d.ts +1 -1
  46. package/dist/GPX/index.js +1 -1
  47. package/dist/GPX/middleware.d.ts +27 -0
  48. package/dist/GPX/middleware.js +93 -0
  49. package/dist/GPX/processor.d.ts +1 -1
  50. package/dist/GPX/processor.js +12 -11
  51. package/dist/GPX/types.d.ts +2 -3
  52. package/dist/TCX/base.d.ts +52 -0
  53. package/dist/TCX/base.js +62 -0
  54. package/dist/TCX/converters.d.ts +75 -0
  55. package/dist/TCX/converters.js +333 -0
  56. package/dist/TCX/decoder.d.ts +87 -0
  57. package/dist/TCX/decoder.js +350 -0
  58. package/dist/TCX/encoder.d.ts +150 -0
  59. package/dist/TCX/encoder.js +513 -0
  60. package/dist/TCX/index.d.ts +10 -0
  61. package/dist/TCX/index.js +9 -0
  62. package/dist/TCX/middleware.d.ts +27 -0
  63. package/dist/TCX/middleware.js +93 -0
  64. package/dist/TCX/processor.d.ts +48 -0
  65. package/dist/TCX/processor.js +197 -0
  66. package/dist/TCX/types.d.ts +203 -0
  67. package/dist/TCX/types.js +1 -0
  68. package/dist/core/base.d.ts +119 -0
  69. package/dist/core/base.js +138 -0
  70. package/dist/core/decoder.d.ts +66 -0
  71. package/dist/core/decoder.js +90 -0
  72. package/dist/core/index.d.ts +2 -0
  73. package/dist/core/index.js +4 -0
  74. package/dist/core/processor.d.ts +69 -0
  75. package/dist/core/processor.js +265 -0
  76. package/dist/core/xml-utils.d.ts +41 -0
  77. package/dist/core/xml-utils.js +140 -0
  78. package/package.json +8 -5
@@ -0,0 +1,66 @@
1
+ import { IPlugin, IProcessingContext } from "./base.js";
2
+ import { Processor } from "./processor.js";
3
+ /**
4
+ * 通用解码器基类
5
+ */
6
+ export declare abstract class BaseDecoder<TContext extends IProcessingContext = IProcessingContext, TResult = any> {
7
+ protected processor: Processor<TContext, TResult>;
8
+ protected initialized: boolean;
9
+ constructor();
10
+ /**
11
+ * 抽象方法:初始化默认转换器
12
+ */
13
+ protected abstract initializeDefaultConverters(): void;
14
+ /**
15
+ * 抽象方法:创建处理上下文
16
+ */
17
+ protected abstract createContext(options: {
18
+ skipValidation?: boolean;
19
+ enableStats?: boolean;
20
+ userData?: Record<string, any>;
21
+ }): TContext;
22
+ /**
23
+ * 解析 Buffer 数据
24
+ */
25
+ parseByBuffer(buffer: Buffer, options?: {
26
+ skipValidation?: boolean;
27
+ enableStats?: boolean;
28
+ userData?: Record<string, any>;
29
+ }): Promise<TResult>;
30
+ /**
31
+ * 解析 XML 字符串
32
+ */
33
+ parseByString(xmlContent: string, options?: {
34
+ skipValidation?: boolean;
35
+ enableStats?: boolean;
36
+ userData?: Record<string, any>;
37
+ }): Promise<TResult>;
38
+ /**
39
+ * 注册自定义插件
40
+ */
41
+ registerPlugin(plugin: IPlugin<TContext>): void;
42
+ /**
43
+ * 批量注册插件
44
+ */
45
+ registerPlugins(plugins: IPlugin<TContext>[]): void;
46
+ /**
47
+ * 移除插件
48
+ */
49
+ unregisterPlugin(name: string): boolean;
50
+ /**
51
+ * 获取已注册的插件列表
52
+ */
53
+ getRegisteredPlugins(): IPlugin<TContext>[];
54
+ /**
55
+ * 清空所有插件(保留默认转换器)
56
+ */
57
+ reset(): void;
58
+ /**
59
+ * 获取处理器实例(用于高级自定义)
60
+ */
61
+ getProcessor(): Processor<TContext, TResult>;
62
+ /**
63
+ * 获取转换器
64
+ */
65
+ getConverter(tag: string): import("./base.js").IConverterPlugin<TContext> | undefined;
66
+ }
@@ -0,0 +1,90 @@
1
+ import { Processor } from "./processor.js";
2
+ import { XMLUtils } from "./xml-utils.js";
3
+ /**
4
+ * 通用解码器基类
5
+ */
6
+ export class BaseDecoder {
7
+ constructor() {
8
+ this.initialized = false;
9
+ this.processor = new Processor();
10
+ }
11
+ /**
12
+ * 解析 Buffer 数据
13
+ */
14
+ async parseByBuffer(buffer, options = {}) {
15
+ const xmlContent = buffer.toString("utf-8");
16
+ return this.parseByString(xmlContent, options);
17
+ }
18
+ /**
19
+ * 解析 XML 字符串
20
+ */
21
+ async parseByString(xmlContent, options = {}) {
22
+ try {
23
+ // 确保转换器已初始化
24
+ if (!this.initialized) {
25
+ this.initializeDefaultConverters();
26
+ }
27
+ // 验证 XML 格式
28
+ if (!options.skipValidation) {
29
+ const validation = XMLUtils.validateXML(xmlContent);
30
+ if (!validation.isValid) {
31
+ throw new Error(`XML 格式错误: ${validation.error}`);
32
+ }
33
+ }
34
+ // 1. Tokenize XML
35
+ const tokens = XMLUtils.tokenizeXML(xmlContent);
36
+ // 2. 创建上下文
37
+ const context = this.createContext(options);
38
+ // 3. 使用处理器处理
39
+ const result = await this.processor.process(tokens, context, options);
40
+ return result;
41
+ }
42
+ catch (error) {
43
+ throw new Error(`解析失败: ${error.message}`);
44
+ }
45
+ }
46
+ /**
47
+ * 注册自定义插件
48
+ */
49
+ registerPlugin(plugin) {
50
+ this.processor.registerPlugin(plugin);
51
+ }
52
+ /**
53
+ * 批量注册插件
54
+ */
55
+ registerPlugins(plugins) {
56
+ this.processor.registerPlugins(plugins);
57
+ }
58
+ /**
59
+ * 移除插件
60
+ */
61
+ unregisterPlugin(name) {
62
+ return this.processor.unregisterPlugin(name);
63
+ }
64
+ /**
65
+ * 获取已注册的插件列表
66
+ */
67
+ getRegisteredPlugins() {
68
+ return this.processor.getRegisteredPlugins();
69
+ }
70
+ /**
71
+ * 清空所有插件(保留默认转换器)
72
+ */
73
+ reset() {
74
+ this.processor.clear();
75
+ this.initialized = false;
76
+ this.initializeDefaultConverters();
77
+ }
78
+ /**
79
+ * 获取处理器实例(用于高级自定义)
80
+ */
81
+ getProcessor() {
82
+ return this.processor;
83
+ }
84
+ /**
85
+ * 获取转换器
86
+ */
87
+ getConverter(tag) {
88
+ return this.processor.getConverter(tag);
89
+ }
90
+ }
@@ -0,0 +1,2 @@
1
+ export { IPlugin, IConverterPlugin, IMiddlewarePlugin, IProcessingContext, BaseConverter, BaseMiddleware, } from "./base.js";
2
+ export { XMLUtils } from "./xml-utils.js";
@@ -0,0 +1,4 @@
1
+ // ============ 基础架构 ============
2
+ export { BaseConverter, BaseMiddleware, } from "./base.js";
3
+ // ============ XML 工具 ============
4
+ export { XMLUtils } from "./xml-utils.js";
@@ -0,0 +1,69 @@
1
+ import { Token, TokenAST } from "../types.js";
2
+ import { IPlugin, IConverterPlugin, IProcessingContext } from "./base.js";
3
+ /**
4
+ * 通用处理器 - 负责管理插件和处理流程
5
+ */
6
+ export declare class Processor<TContext extends IProcessingContext = IProcessingContext, TResult = any> {
7
+ private converters;
8
+ private middlewares;
9
+ private plugins;
10
+ /**
11
+ * 注册插件
12
+ */
13
+ registerPlugin(plugin: IPlugin<TContext>): void;
14
+ /**
15
+ * 批量注册插件
16
+ */
17
+ registerPlugins(plugins: IPlugin<TContext>[]): void;
18
+ /**
19
+ * 获取转换器
20
+ */
21
+ getConverter(tag: string): IConverterPlugin<TContext> | undefined;
22
+ /**
23
+ * 移除插件
24
+ */
25
+ unregisterPlugin(name: string): boolean;
26
+ /**
27
+ * 获取已注册的插件列表
28
+ */
29
+ getRegisteredPlugins(): IPlugin<TContext>[];
30
+ /**
31
+ * 清空所有插件
32
+ */
33
+ clear(): void;
34
+ /**
35
+ * 处理数据 - 四阶段处理流程
36
+ */
37
+ process(tokens: Token[], context: TContext, options?: {
38
+ skipValidation?: boolean;
39
+ enableStats?: boolean;
40
+ }): Promise<TResult>;
41
+ /**
42
+ * 生成 AST
43
+ */
44
+ generateAST(tokens: Token[]): TokenAST;
45
+ /**
46
+ * 转换 AST
47
+ */
48
+ convertAST(ast: TokenAST, context: TContext): any;
49
+ /**
50
+ * 通用转换逻辑
51
+ */
52
+ private convertGeneric;
53
+ /**
54
+ * 初始化插件
55
+ */
56
+ private initializePlugins;
57
+ /**
58
+ * 销毁插件
59
+ */
60
+ private destroyPlugins;
61
+ /**
62
+ * 检查是否为转换器插件
63
+ */
64
+ private isConverterPlugin;
65
+ /**
66
+ * 检查是否为中间件插件
67
+ */
68
+ private isMiddlewarePlugin;
69
+ }
@@ -0,0 +1,265 @@
1
+ /**
2
+ * 通用处理器 - 负责管理插件和处理流程
3
+ */
4
+ export class Processor {
5
+ constructor() {
6
+ this.converters = new Map();
7
+ this.middlewares = [];
8
+ this.plugins = new Map();
9
+ }
10
+ /**
11
+ * 注册插件
12
+ */
13
+ registerPlugin(plugin) {
14
+ if (this.plugins.has(plugin.name)) {
15
+ throw new Error(`插件 ${plugin.name} 已存在`);
16
+ }
17
+ this.plugins.set(plugin.name, plugin);
18
+ // 根据插件类型注册到相应的集合
19
+ if (this.isConverterPlugin(plugin)) {
20
+ plugin.supportedTags?.forEach((tag) => {
21
+ this.converters.set(tag, plugin);
22
+ });
23
+ }
24
+ if (this.isMiddlewarePlugin(plugin)) {
25
+ this.middlewares.push(plugin);
26
+ // 按优先级排序
27
+ this.middlewares.sort((a, b) => (a.priority || 100) - (b.priority || 100));
28
+ }
29
+ }
30
+ /**
31
+ * 批量注册插件
32
+ */
33
+ registerPlugins(plugins) {
34
+ plugins.forEach((plugin) => this.registerPlugin(plugin));
35
+ }
36
+ /**
37
+ * 获取转换器
38
+ */
39
+ getConverter(tag) {
40
+ return this.converters.get(tag);
41
+ }
42
+ /**
43
+ * 移除插件
44
+ */
45
+ unregisterPlugin(name) {
46
+ const plugin = this.plugins.get(name);
47
+ if (!plugin)
48
+ return false;
49
+ // 从转换器中移除
50
+ if (this.isConverterPlugin(plugin)) {
51
+ plugin.supportedTags?.forEach((tag) => {
52
+ if (this.converters.get(tag) === plugin) {
53
+ this.converters.delete(tag);
54
+ }
55
+ });
56
+ }
57
+ // 从中间件中移除
58
+ if (this.isMiddlewarePlugin(plugin)) {
59
+ const index = this.middlewares.indexOf(plugin);
60
+ if (index !== -1) {
61
+ this.middlewares.splice(index, 1);
62
+ }
63
+ }
64
+ this.plugins.delete(name);
65
+ return true;
66
+ }
67
+ /**
68
+ * 获取已注册的插件列表
69
+ */
70
+ getRegisteredPlugins() {
71
+ return Array.from(this.plugins.values());
72
+ }
73
+ /**
74
+ * 清空所有插件
75
+ */
76
+ clear() {
77
+ this.plugins.clear();
78
+ this.converters.clear();
79
+ this.middlewares.length = 0;
80
+ }
81
+ /**
82
+ * 处理数据 - 四阶段处理流程
83
+ */
84
+ async process(tokens, context, options = {}) {
85
+ try {
86
+ // 初始化插件
87
+ await this.initializePlugins(context);
88
+ // 1. Tokenize 阶段
89
+ let processedTokens = tokens;
90
+ for (const middleware of this.middlewares) {
91
+ if (middleware.onTokenize) {
92
+ processedTokens = await middleware.onTokenize(processedTokens, context);
93
+ }
94
+ }
95
+ // 2. AST 生成阶段
96
+ let ast = this.generateAST(processedTokens);
97
+ for (const middleware of this.middlewares) {
98
+ if (middleware.onAstGenerate) {
99
+ ast = await middleware.onAstGenerate(ast, context);
100
+ }
101
+ }
102
+ // 3. 转换阶段
103
+ let result = this.convertAST(ast, context);
104
+ for (const middleware of this.middlewares) {
105
+ if (middleware.onConvert) {
106
+ result = await middleware.onConvert(result, context);
107
+ }
108
+ }
109
+ // 4. 完成阶段
110
+ let finalResult = result;
111
+ for (const middleware of this.middlewares) {
112
+ if (middleware.onComplete) {
113
+ finalResult = await middleware.onComplete(finalResult, context);
114
+ }
115
+ }
116
+ // 更新统计信息
117
+ if (options.enableStats) {
118
+ context.stats.endTime = Date.now();
119
+ context.stats.processedTokens = processedTokens.length;
120
+ }
121
+ return finalResult;
122
+ }
123
+ catch (error) {
124
+ // 错误处理
125
+ const err = error;
126
+ context.errors.push(err);
127
+ for (const middleware of this.middlewares) {
128
+ if (middleware.onError) {
129
+ await middleware.onError(err, context);
130
+ }
131
+ }
132
+ throw err;
133
+ }
134
+ finally {
135
+ // 销毁插件
136
+ await this.destroyPlugins(context);
137
+ }
138
+ }
139
+ /**
140
+ * 生成 AST
141
+ */
142
+ generateAST(tokens) {
143
+ const stack = [];
144
+ let root = undefined;
145
+ let current = null;
146
+ for (const token of tokens) {
147
+ if (token.type === "open") {
148
+ const newElement = {
149
+ tag: token.tag,
150
+ attributes: token.attributes || {},
151
+ children: [],
152
+ };
153
+ if (!root) {
154
+ root = newElement;
155
+ }
156
+ else {
157
+ current?.children?.push(newElement);
158
+ }
159
+ stack.push(newElement);
160
+ current = newElement;
161
+ }
162
+ else if (token.type === "text") {
163
+ if (current) {
164
+ current.value = token.value;
165
+ }
166
+ }
167
+ else if (token.type === "close") {
168
+ stack.pop();
169
+ current = stack[stack.length - 1] || null;
170
+ }
171
+ }
172
+ if (!root) {
173
+ throw new Error("无法生成有效的 AST");
174
+ }
175
+ return root;
176
+ }
177
+ /**
178
+ * 转换 AST
179
+ */
180
+ convertAST(ast, context) {
181
+ const converter = this.getConverter(ast.tag);
182
+ if (converter) {
183
+ return converter.convert(ast, context);
184
+ }
185
+ // 如果没有专门的转换器,使用通用转换逻辑
186
+ return this.convertGeneric(ast, context);
187
+ }
188
+ /**
189
+ * 通用转换逻辑
190
+ */
191
+ convertGeneric(ast, context) {
192
+ const result = {};
193
+ // 复制属性
194
+ if (ast.attributes) {
195
+ Object.entries(ast.attributes).forEach(([key, value]) => {
196
+ result[key] = value;
197
+ });
198
+ }
199
+ // 如果有值,直接返回值
200
+ if (ast.value !== undefined &&
201
+ (!ast.children || ast.children.length === 0)) {
202
+ return ast.value;
203
+ }
204
+ // 处理子元素
205
+ if (ast.children) {
206
+ ast.children.forEach((child) => {
207
+ if (child.tag) {
208
+ const converted = this.convertAST(child, context);
209
+ if (result[child.tag]) {
210
+ // 如果已存在,转换为数组
211
+ if (!Array.isArray(result[child.tag])) {
212
+ result[child.tag] = [result[child.tag]];
213
+ }
214
+ result[child.tag].push(converted);
215
+ }
216
+ else {
217
+ result[child.tag] = converted;
218
+ }
219
+ }
220
+ });
221
+ }
222
+ return result;
223
+ }
224
+ /**
225
+ * 初始化插件
226
+ */
227
+ async initializePlugins(context) {
228
+ for (const plugin of this.plugins.values()) {
229
+ if (plugin.initialize) {
230
+ await plugin.initialize(context);
231
+ }
232
+ }
233
+ }
234
+ /**
235
+ * 销毁插件
236
+ */
237
+ async destroyPlugins(context) {
238
+ for (const plugin of this.plugins.values()) {
239
+ if (plugin.destroy) {
240
+ try {
241
+ await plugin.destroy(context);
242
+ }
243
+ catch (error) {
244
+ console.warn(`销毁插件 ${plugin.name} 时出错:`, error);
245
+ }
246
+ }
247
+ }
248
+ }
249
+ /**
250
+ * 检查是否为转换器插件
251
+ */
252
+ isConverterPlugin(plugin) {
253
+ return "convert" in plugin && typeof plugin.convert === "function";
254
+ }
255
+ /**
256
+ * 检查是否为中间件插件
257
+ */
258
+ isMiddlewarePlugin(plugin) {
259
+ return ("onTokenize" in plugin ||
260
+ "onAstGenerate" in plugin ||
261
+ "onConvert" in plugin ||
262
+ "onComplete" in plugin ||
263
+ "onError" in plugin);
264
+ }
265
+ }
@@ -0,0 +1,41 @@
1
+ import { Token } from "../types.js";
2
+ /**
3
+ * XML 解析工具类
4
+ */
5
+ export declare class XMLUtils {
6
+ /**
7
+ * 将 XML 内容转换为 Token 数组
8
+ */
9
+ static tokenizeXML(xmlContent: string): Token[];
10
+ /**
11
+ * 压缩 XML 字符串(移除多余空白)
12
+ */
13
+ static compressXML(xmlContent: string): string;
14
+ /**
15
+ * 格式化 XML 字符串(添加缩进)
16
+ */
17
+ static formatXML(xmlContent: string, indentSize?: number): string;
18
+ /**
19
+ * 验证 XML 格式
20
+ */
21
+ static validateXML(xmlContent: string): {
22
+ isValid: boolean;
23
+ error?: string;
24
+ };
25
+ /**
26
+ * 提取 XML 声明信息
27
+ */
28
+ static extractXMLDeclaration(xmlContent: string): {
29
+ version?: string;
30
+ encoding?: string;
31
+ standalone?: string;
32
+ };
33
+ /**
34
+ * 转义 XML 特殊字符
35
+ */
36
+ static escapeXML(text: string): string;
37
+ /**
38
+ * 反转义 XML 特殊字符
39
+ */
40
+ static unescapeXML(text: string): string;
41
+ }
@@ -0,0 +1,140 @@
1
+ import { Parser } from "htmlparser2";
2
+ /**
3
+ * XML 解析工具类
4
+ */
5
+ export class XMLUtils {
6
+ /**
7
+ * 将 XML 内容转换为 Token 数组
8
+ */
9
+ static tokenizeXML(xmlContent) {
10
+ const tokens = [];
11
+ const parser = new Parser({
12
+ onopentag(name, attribs) {
13
+ tokens.push({
14
+ type: "open",
15
+ tag: name,
16
+ attributes: attribs,
17
+ });
18
+ },
19
+ ontext(text) {
20
+ const filteredText = text.replace(/\n/g, "").trim();
21
+ if (filteredText) {
22
+ tokens.push({ type: "text", tag: "text", value: filteredText });
23
+ }
24
+ },
25
+ onclosetag(name) {
26
+ tokens.push({ type: "close", tag: name });
27
+ },
28
+ }, { xmlMode: true });
29
+ parser.write(xmlContent);
30
+ parser.end();
31
+ return tokens;
32
+ }
33
+ /**
34
+ * 压缩 XML 字符串(移除多余空白)
35
+ */
36
+ static compressXML(xmlContent) {
37
+ return xmlContent
38
+ .replace(/>\s+</g, "><") // 移除标签间的空白
39
+ .replace(/\s+/g, " ") // 将多个空白字符替换为单个空格
40
+ .trim();
41
+ }
42
+ /**
43
+ * 格式化 XML 字符串(添加缩进)
44
+ */
45
+ static formatXML(xmlContent, indentSize = 2) {
46
+ const indent = " ".repeat(indentSize);
47
+ let formatted = "";
48
+ let depth = 0;
49
+ let inTag = false;
50
+ for (let i = 0; i < xmlContent.length; i++) {
51
+ const char = xmlContent[i];
52
+ const nextChar = xmlContent[i + 1];
53
+ if (char === "<") {
54
+ if (nextChar === "/") {
55
+ // 闭合标签
56
+ depth--;
57
+ if (!inTag) {
58
+ formatted += "\n" + indent.repeat(depth);
59
+ }
60
+ }
61
+ else if (nextChar !== "?" && nextChar !== "!") {
62
+ // 开始标签
63
+ if (!inTag) {
64
+ formatted += "\n" + indent.repeat(depth);
65
+ }
66
+ depth++;
67
+ }
68
+ inTag = true;
69
+ }
70
+ else if (char === ">") {
71
+ inTag = false;
72
+ if (nextChar === "<" && xmlContent[i + 2] === "/") {
73
+ // 自闭合标签或紧跟闭合标签
74
+ depth--;
75
+ }
76
+ }
77
+ formatted += char;
78
+ }
79
+ return formatted.trim();
80
+ }
81
+ /**
82
+ * 验证 XML 格式
83
+ */
84
+ static validateXML(xmlContent) {
85
+ try {
86
+ const parser = new Parser({}, { xmlMode: true });
87
+ parser.write(xmlContent);
88
+ parser.end();
89
+ return { isValid: true };
90
+ }
91
+ catch (error) {
92
+ return {
93
+ isValid: false,
94
+ error: error.message,
95
+ };
96
+ }
97
+ }
98
+ /**
99
+ * 提取 XML 声明信息
100
+ */
101
+ static extractXMLDeclaration(xmlContent) {
102
+ const declarationMatch = xmlContent.match(/<\?xml\s+([^>]+)\?>/);
103
+ if (!declarationMatch)
104
+ return {};
105
+ const declaration = declarationMatch[1];
106
+ const result = {};
107
+ const versionMatch = declaration.match(/version=["']([^"']+)["']/);
108
+ if (versionMatch)
109
+ result.version = versionMatch[1];
110
+ const encodingMatch = declaration.match(/encoding=["']([^"']+)["']/);
111
+ if (encodingMatch)
112
+ result.encoding = encodingMatch[1];
113
+ const standaloneMatch = declaration.match(/standalone=["']([^"']+)["']/);
114
+ if (standaloneMatch)
115
+ result.standalone = standaloneMatch[1];
116
+ return result;
117
+ }
118
+ /**
119
+ * 转义 XML 特殊字符
120
+ */
121
+ static escapeXML(text) {
122
+ return text
123
+ .replace(/&/g, "&amp;")
124
+ .replace(/</g, "&lt;")
125
+ .replace(/>/g, "&gt;")
126
+ .replace(/"/g, "&quot;")
127
+ .replace(/'/g, "&#39;");
128
+ }
129
+ /**
130
+ * 反转义 XML 特殊字符
131
+ */
132
+ static unescapeXML(text) {
133
+ return text
134
+ .replace(/&lt;/g, "<")
135
+ .replace(/&gt;/g, ">")
136
+ .replace(/&quot;/g, '"')
137
+ .replace(/&#39;/g, "'")
138
+ .replace(/&amp;/g, "&");
139
+ }
140
+ }