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.
- package/README.md +322 -281
- package/dist/FIT/base.d.ts +0 -30
- package/dist/FIT/decoder.d.ts +0 -48
- package/dist/FIT/encoder.d.ts +0 -97
- package/dist/FIT/mesg.d.ts +0 -3
- package/dist/FIT/middleware.d.ts +0 -15
- package/dist/FIT/plugins.d.ts +0 -9
- package/dist/FIT/processor.d.ts +0 -24
- package/dist/FIT/types/mesg_num.d.ts +102 -102
- package/dist/FIT/types/sport.d.ts +44 -44
- package/dist/FIT/types.d.ts +0 -35
- package/dist/GPX/base.d.ts +0 -39
- package/dist/GPX/converters.d.ts +0 -30
- package/dist/GPX/decoder.d.ts +0 -66
- package/dist/GPX/encoder.d.ts +0 -109
- package/dist/GPX/middleware.d.ts +0 -9
- package/dist/GPX/processor.d.ts +0 -18
- package/dist/GPX/types.d.ts +0 -108
- package/dist/TCX/base.d.ts +0 -33
- package/dist/TCX/converters.d.ts +0 -24
- package/dist/TCX/decoder.d.ts +0 -54
- package/dist/TCX/encoder.d.ts +0 -109
- package/dist/TCX/index.d.ts +4 -1
- package/dist/TCX/middleware.d.ts +0 -9
- package/dist/TCX/processor.d.ts +0 -18
- package/dist/TCX/types.d.ts +0 -13
- package/dist/TrackSwap.d.ts +37 -33
- package/dist/core/base.d.ts +0 -46
- package/dist/core/xml-utils.d.ts +0 -24
- package/dist/index.js +24 -5
- package/dist/sport/base.d.ts +63 -0
- package/dist/sport/converters.d.ts +33 -0
- package/dist/sport/encoders.d.ts +34 -0
- package/dist/sport/index.d.ts +17 -0
- package/dist/sport/processor.d.ts +19 -0
- package/dist/types.d.ts +91 -13
- package/package.json +18 -3
- package/dist/FIT/base.js +0 -101
- package/dist/FIT/crc.js +0 -16
- package/dist/FIT/decoder.js +0 -323
- package/dist/FIT/encoder.js +0 -445
- package/dist/FIT/index.js +0 -13
- package/dist/FIT/mesg.js +0 -208
- package/dist/FIT/middleware.js +0 -206
- package/dist/FIT/plugins.js +0 -104
- package/dist/FIT/processor.js +0 -170
- package/dist/FIT/types/course_point.js +0 -58
- package/dist/FIT/types/event.js +0 -50
- package/dist/FIT/types/event_type.js +0 -19
- package/dist/FIT/types/file.js +0 -24
- package/dist/FIT/types/index.js +0 -297
- package/dist/FIT/types/mesg_num.js +0 -106
- package/dist/FIT/types/sport.js +0 -48
- package/dist/FIT/types.js +0 -1
- package/dist/FIT/util.js +0 -7
- package/dist/GPX/base.js +0 -56
- package/dist/GPX/converters.js +0 -352
- package/dist/GPX/decoder.js +0 -409
- package/dist/GPX/encoder.js +0 -513
- package/dist/GPX/extensions/index.js +0 -14
- package/dist/GPX/index.js +0 -11
- package/dist/GPX/middleware.js +0 -93
- package/dist/GPX/processor.js +0 -188
- package/dist/GPX/types.js +0 -1
- package/dist/GPX/util.js +0 -18
- package/dist/TCX/base.js +0 -62
- package/dist/TCX/converters.js +0 -333
- package/dist/TCX/decoder.js +0 -350
- package/dist/TCX/encoder.js +0 -513
- package/dist/TCX/index.js +0 -9
- package/dist/TCX/middleware.js +0 -93
- package/dist/TCX/processor.js +0 -197
- package/dist/TCX/types.js +0 -1
- package/dist/TrackSwap.js +0 -84
- package/dist/core/base.js +0 -138
- package/dist/core/decoder.d.ts +0 -66
- package/dist/core/decoder.js +0 -90
- package/dist/core/index.js +0 -4
- package/dist/core/processor.d.ts +0 -69
- package/dist/core/processor.js +0 -265
- package/dist/core/xml-utils.js +0 -140
- package/dist/types.js +0 -1
package/dist/core/processor.js
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/core/xml-utils.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
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, "&")
|
|
124
|
-
.replace(/</g, "<")
|
|
125
|
-
.replace(/>/g, ">")
|
|
126
|
-
.replace(/"/g, """)
|
|
127
|
-
.replace(/'/g, "'");
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* 反转义 XML 特殊字符
|
|
131
|
-
*/
|
|
132
|
-
static unescapeXML(text) {
|
|
133
|
-
return text
|
|
134
|
-
.replace(/</g, "<")
|
|
135
|
-
.replace(/>/g, ">")
|
|
136
|
-
.replace(/"/g, '"')
|
|
137
|
-
.replace(/'/g, "'")
|
|
138
|
-
.replace(/&/g, "&");
|
|
139
|
-
}
|
|
140
|
-
}
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|