npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

trackswap

v0.2.2

Published

A typed, adapter-based toolkit for decoding, normalizing and transcoding FIT, GPX and TCX data.

Downloads

689

Readme

TrackSwap

TrackSwap 是一个面向 Node.js 和 TypeScript 的运动、轨迹与健康数据工具包。它使用统一文档模型读取 FIT、GPX 和 TCX,并通过格式适配器隔离各协议的解析与编码实现。

安装

npm install trackswap

需要 Node.js 18 或更高版本。

核心模型

import TrackSwap from "trackswap";

const trackSwap = new TrackSwap();

try {
  const document = await trackSwap.decode(buffer);

  console.log(document.format);
  console.log(document.kinds);
  console.log(document.facets.activity);
  console.log(document.facets.health);
  console.log(document.metadata);
} finally {
  await trackSwap.dispose();
}

decode() 默认不会返回格式原生对象。只有调试或协议级处理确实需要时才启用:

const document = await trackSwap.decode(buffer, {
  format: "fit",
  includeNative: true,
  fit: {
    includeUnknownMessages: true,
  },
});

活动读取、编码与转换

const activity = await trackSwap.decodeActivity(input);

const gpx = await trackSwap.encodeActivity(activity, { format: "gpx" });
const fit = await trackSwap.encodeActivity(activity, { format: "fit" });
const tcx = await trackSwap.encodeActivity(activity, { format: "tcx" });

const converted = await trackSwap.transcode(input, {
  sourceFormat: "gpx",
  format: "tcx",
});

const course = await trackSwap.encodeCourse(input);

FIT 健康数据

FIT 文件只解码一次,活动与健康数据作为同一 TrackDocument 的 facet 返回。健康结构不直接绑定 Garmin Connect JSON,使用稳定的中间表示:

  • points:心率、压力、呼吸率、血氧、Body Battery、HRV、温度等时间点;
  • intervals:步数、距离、热量和活动累计量;
  • sessions:睡眠、小睡和 Health Snapshot;
  • summaries:日汇总、睡眠汇总及设备汇总;
  • diagnostics:FIT SDK profile、无效值、未知消息和解析警告。

标准化边界由 TrackSwap 负责:FIT epoch、timestamp_16、Garmin sentinel、 字段 scale/unit、时区偏移和 HSA 数组展开均不会泄漏给业务调用方。所有时区偏移 统一使用秒;sentinel 只保留在 status/attributes.rawValue,不会伪装成有效测量值。

const document = await trackSwap.decode(fitBuffer, {
  fit: {
    includeDeveloperFields: true,
    health: {
      reconstructIntervals: true,
      retainCumulativeValues: false,
    },
  },
});

错误处理

所有公共操作使用 TrackSwapError,可通过稳定错误码分类:

import { TrackSwapError } from "trackswap";

try {
  await trackSwap.decode(input);
} catch (error) {
  if (error instanceof TrackSwapError) {
    console.error(error.code, error.format, error.cause);
  }
}

错误码包括 FORMAT_UNKNOWNFORMAT_UNSUPPORTEDDECODE_FAILEDACTIVITY_NOT_FOUNDENCODE_FAILEDADAPTER_CONFLICTDISPOSED

自定义格式适配器

格式扩展发生在一个明确边界上,不需要访问 TrackSwap 内部解码器:

import TrackSwap, { type TrackFormatAdapter } from "trackswap";

const adapter: TrackFormatAdapter = {
  format: "geojson",
  matches: (input) => input.includes(Buffer.from('"FeatureCollection"')),
  decode: async (input, options) => ({
    kinds: ["route"],
    facets: { activity: myDecode(input, options) },
    native: {},
  }),
  encodeActivity: async (activity) => myEncode(activity),
};

const trackSwap = new TrackSwap({ adapters: [adapter] });

自定义适配器默认追加到 FIT、GPX、TCX 之后。每种格式只能注册一个适配器;重复注册会立即抛出 ADAPTER_CONFLICT。测试或完全定制运行时可设置 includeBuiltInAdapters: false

公共 API

| 方法 | 作用 | | --- | --- | | detect(input) | 检测 FIT、GPX 或 TCX | | decode(input, options?) | 返回统一 TrackDocument | | decodeActivity(input, options?) | 读取并要求存在活动 facet | | encodeActivity(activity, options) | 将统一活动编码为目标格式 | | transcode(input, options) | 通过统一活动模型转换格式 | | encodeCourse(inputOrActivity, options?) | 生成 FIT Course | | dispose() | 释放当前实例已创建的解码器 |

设计原则

  • façade 只负责编排,不包含协议逻辑;
  • 格式适配器负责检测、原生解码与编码;
  • 统一活动和健康模型是格式之间唯一的数据交换边界;
  • 默认惰性创建解码器,未使用的格式没有初始化成本;
  • 核心解析阶段 fail-fast,不返回部分损坏的文档;
  • 原生协议对象默认不进入公共结果,避免业务层依赖实现细节。

License

MIT