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

@kaiord/core

v1.0.0

Published

Core library for Kaiord workout data conversion

Readme

@kaiord/core

npm version License: MIT Build Status

Core library for Kaiord workout data conversion between FIT, TCX, ZWO, and KRD formats.

Features

  • FIT file support: Read and write Garmin FIT workout files
  • KRD canonical format: JSON-based workout representation
  • Schema validation: Zod schemas with TypeScript type inference
  • Round-trip safety: Lossless conversions with defined tolerances
  • Hexagonal architecture: Clean separation of concerns
  • Tree-shakeable: Import only what you need for minimal bundle size
  • Full TypeScript support: Complete type definitions and inference
  • Custom loggers: Integrate with your logging infrastructure
  • Dependency injection: Swap providers without changing code

Installation

npm install @kaiord/core

or with pnpm:

pnpm add @kaiord/core

or with yarn:

yarn add @kaiord/core

Quick Start

Basic FIT to KRD Conversion

import { createDefaultProviders } from "@kaiord/core";
import type { KRD } from "@kaiord/core";
import { readFile } from "fs/promises";

// Read FIT file
const fitBuffer = await readFile("workout.fit");

// Create providers with default configuration
const providers = createDefaultProviders();

// Convert FIT to KRD
const krd: KRD = await providers.convertFitToKrd({ fitBuffer });

console.log(krd.version); // "1.0"
console.log(krd.type); // "workout"

Basic KRD to FIT Conversion

import { createDefaultProviders } from "@kaiord/core";
import { writeFile } from "fs/promises";

const providers = createDefaultProviders();

// Convert KRD to FIT
const fitBuffer = await providers.convertKrdToFit({ krd });

// Write FIT file
await writeFile("output.fit", fitBuffer);

Schema Validation

import { krdSchema } from "@kaiord/core";

// Validate KRD data
const result = krdSchema.safeParse(data);

if (result.success) {
  console.log("Valid KRD:", result.data);
} else {
  console.error("Validation errors:", result.error.errors);
}

API Overview

Main Functions

createDefaultProviders(logger?: Logger)

Creates a set of providers with default implementations for FIT conversion.

import { createDefaultProviders } from "@kaiord/core";

const providers = createDefaultProviders();
// Returns: { convertFitToKrd, convertKrdToFit, logger }

convertFitToKrd({ fitBuffer })

Converts a FIT workout file to KRD format.

const krd = await providers.convertFitToKrd({ fitBuffer });

Parameters:

  • fitBuffer: Uint8Array - Binary FIT file data

Returns: Promise<KRD> - Validated KRD object

Throws:

  • FitParsingError - When FIT file is corrupted or invalid
  • KrdValidationError - When converted data fails schema validation

convertKrdToFit({ krd })

Converts a KRD object to FIT workout file format.

const fitBuffer = await providers.convertKrdToFit({ krd });

Parameters:

  • krd: KRD - Valid KRD object

Returns: Promise<Uint8Array> - Binary FIT file data

Throws:

  • KrdValidationError - When KRD data is invalid
  • FitParsingError - When FIT encoding fails

validateRoundTrip({ fitBuffer })

Validates that FIT → KRD → FIT conversion preserves data within tolerances.

import { validateRoundTrip, createToleranceChecker } from "@kaiord/core";

const checker = createToleranceChecker();
await validateRoundTrip(
  fitReader,
  fitWriter,
  validator,
  checker,
  logger
)({
  fitBuffer,
});

Throws:

  • ToleranceExceededError - When round-trip conversion exceeds tolerances

Schema Exports

All domain schemas are exported for validation and type inference:

import {
  krdSchema,
  workoutSchema,
  durationSchema,
  targetSchema,
  sportSchema,
  subSportSchema,
  intensitySchema,
} from "@kaiord/core";

// Validate data
const result = krdSchema.safeParse(data);

// Access enum values
const sport = sportSchema.enum.cycling;
const intensity = intensitySchema.enum.warmup;

Type Exports

All TypeScript types are inferred from Zod schemas:

import type {
  KRD,
  Workout,
  WorkoutStep,
  Duration,
  Target,
  Sport,
  SubSport,
  Intensity,
} from "@kaiord/core";

Error Types

import {
  FitParsingError,
  KrdValidationError,
  ToleranceExceededError,
} from "@kaiord/core";

For detailed API examples, see docs/api-examples.md (coming soon).

TypeScript Support

@kaiord/core is written in TypeScript and provides complete type definitions.

Type Imports

Import types separately from values for optimal tree-shaking:

import { createDefaultProviders, krdSchema } from "@kaiord/core";
import type { KRD, Workout, Duration } from "@kaiord/core";

Discriminated Unions

Duration and Target types use discriminated unions for type safety:

import type { Duration } from "@kaiord/core";

const duration: Duration =
  | { type: "time"; seconds: number }
  | { type: "distance"; meters: number }
  | { type: "open" };

// TypeScript narrows the type based on discriminator
if (duration.type === "time") {
  console.log(duration.seconds); // ✓ TypeScript knows this exists
}
import type { Target } from "@kaiord/core";

const target: Target =
  | { type: "power"; value: { unit: "watts"; value: number } }
  | { type: "heart_rate"; value: { unit: "bpm"; value: number } }
  | { type: "open" };

// Type narrowing works automatically
if (target.type === "power") {
  console.log(target.value.unit); // ✓ "watts" | "percent_ftp" | "zone" | "range"
}

Schema Validation with Type Inference

Zod schemas provide both runtime validation and TypeScript types:

import { krdSchema, workoutSchema } from "@kaiord/core";
import type { KRD, Workout } from "@kaiord/core";

// Parse with automatic type inference
const krd = krdSchema.parse(data); // Type: KRD

// Safe parse with error handling
const result = krdSchema.safeParse(data);
if (result.success) {
  const krd: KRD = result.data; // Type: KRD
} else {
  console.error(result.error.errors);
}

// Validate nested objects
const workout = workoutSchema.parse(data); // Type: Workout

Enum Values

Access enum values via schema .enum property:

import { sportSchema, intensitySchema } from "@kaiord/core";

// Access enum values
const sport = sportSchema.enum.cycling; // "cycling"
const intensity = intensitySchema.enum.warmup; // "warmup"

// Use in comparisons
if (workout.sport === sportSchema.enum.running) {
  console.log("Running workout");
}

Error Handling

@kaiord/core uses custom error classes for different failure scenarios.

Error Types

FitParsingError

Thrown when FIT file parsing fails due to corrupted or invalid data.

import { FitParsingError } from "@kaiord/core";

try {
  const krd = await providers.convertFitToKrd({ fitBuffer });
} catch (error) {
  if (error instanceof FitParsingError) {
    console.error("Failed to parse FIT file:", error.message);
    console.error("Original error:", error.cause);
  }
}

Properties:

  • message: string - Error description
  • cause?: unknown - Original error from FIT SDK

KrdValidationError

Thrown when KRD data fails schema validation.

import { KrdValidationError } from "@kaiord/core";

try {
  const krd = await providers.convertFitToKrd({ fitBuffer });
} catch (error) {
  if (error instanceof KrdValidationError) {
    console.error("KRD validation failed:");
    for (const err of error.errors) {
      console.error(`  - ${err.field}: ${err.message}`);
    }
  }
}

Properties:

  • message: string - Error description
  • errors: Array<{ field: string; message: string }> - Validation errors

ToleranceExceededError

Thrown when round-trip conversion exceeds defined tolerances.

import { ToleranceExceededError } from "@kaiord/core";

try {
  await validateRoundTrip(
    fitReader,
    fitWriter,
    validator,
    checker,
    logger
  )({
    fitBuffer,
  });
} catch (error) {
  if (error instanceof ToleranceExceededError) {
    console.error("Round-trip validation failed:");
    for (const violation of error.violations) {
      console.error(
        `  - ${violation.field}: expected ${violation.expected}, got ${violation.actual}`
      );
      console.error(
        `    Deviation: ${violation.deviation}, tolerance: ${violation.tolerance}`
      );
    }
  }
}

Properties:

  • message: string - Error description
  • violations: Array<{ field: string; expected: number; actual: number; deviation: number; tolerance: number }> - Tolerance violations

Complete Error Handling Example

import {
  createDefaultProviders,
  FitParsingError,
  KrdValidationError,
  ToleranceExceededError,
} from "@kaiord/core";

async function convertWorkout(fitBuffer: Uint8Array) {
  try {
    const providers = createDefaultProviders();
    const krd = await providers.convertFitToKrd({ fitBuffer });
    return krd;
  } catch (error) {
    if (error instanceof FitParsingError) {
      console.error("❌ FIT parsing failed:", error.message);
      if (error.cause) {
        console.error("   Cause:", error.cause);
      }
      throw new Error("Invalid FIT file");
    }

    if (error instanceof KrdValidationError) {
      console.error("❌ KRD validation failed:");
      for (const err of error.errors) {
        console.error(`   - ${err.field}: ${err.message}`);
      }
      throw new Error("Conversion produced invalid KRD");
    }

    // Unknown error - re-throw
    throw error;
  }
}

Documentation

Main Documentation

Package-Specific Documentation

Contributing

We welcome contributions! Please see:

Before contributing:

  1. Read the Architecture documentation
  2. Follow the Testing guidelines
  3. Ensure all tests pass: pnpm test
  4. Run linter: pnpm lint

Scripts

pnpm build                  # Build the library
pnpm test                   # Run tests once
pnpm test:watch             # Run tests in watch mode
pnpm test:coverage          # Run tests with coverage report
pnpm generate:schema        # Generate JSON Schema from Zod schemas
pnpm generate:krd-fixtures  # Generate KRD test fixtures from FIT files
pnpm clean                  # Clean build artifacts

Tree-Shaking

@kaiord/core is fully optimized for tree-shaking. Import only what you need:

// ✅ Good: Import specific items (smaller bundle)
import { krdSchema, sportSchema } from "@kaiord/core";
import type { KRD, Sport } from "@kaiord/core";

// Test utilities (separate export, not included in main bundle)
import { loadKrdFixture } from "@kaiord/core/test-utils";

// ❌ Avoid: Import everything (larger bundle)
import * as Kaiord from "@kaiord/core";

Bundle sizes (minified + gzipped):

  • Types only: 0 KB (compile-time)
  • Schema validation: ~15 KB
  • Full conversion: ~80 KB
  • Test utilities: Not included in production bundles

See docs/tree-shaking.md for detailed guide and best practices.

Test Utilities

The package exports test utilities for other packages to use:

import {
  loadFitFixture,
  loadKrdFixture,
  loadFixturePair,
  FIXTURE_NAMES,
} from "@kaiord/core/test-utils";

// Load fixtures for testing
const fitBuffer = loadFitFixture("WorkoutIndividualSteps.fit");
const krd = loadKrdFixture("WorkoutIndividualSteps.krd");

// Load both for round-trip tests
const { fit, krd } = loadFixturePair(FIXTURE_NAMES.INDIVIDUAL_STEPS);

See docs/krd-fixtures-generation.md for details on fixture generation.