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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@wavespec/kit

v1.0.0

Published

Shared helpers and utilities for Harness adapters

Readme

@wavespec/kit

Shared utilities and helpers for building test adapters. Simplifies adapter implementation by providing reusable response normalization, error handling, discovery, and VCR (cassette-based replay) helpers.

Current Status: Phase 1 Complete - Type definitions and architecture ready. Phase 2 coming soon (implementations).

Overview

@wavespec/kit provides specialized subpath exports for different aspects of adapter development:

  • @wavespec/kit/response - Response normalization and content building
  • @wavespec/kit/error - Error mapping and pattern-based error handling
  • @wavespec/kit/discovery - Fuzzy matching and tool/resource discovery
  • @wavespec/kit/vcr - Request extraction for cassette-based replay
  • @wavespec/kit/fixtures - Test fixtures for creating test data
  • @wavespec/kit/testing - Testing utilities for adapter development

This modular approach lets you use only the helpers you need while keeping related functionality together.

Installation

npm install @wavespec/kit @wavespec/types

Or with your favorite package manager:

# pnpm
pnpm add @wavespec/kit @wavespec/types

# yarn
yarn add @wavespec/kit @wavespec/types

Peer Dependencies

@wavespec/kit depends on @wavespec/types@^0.1.0 as a peer dependency. When you install the kit, you must also have adapter-types installed.

{
  "peerDependencies": {
    "@wavespec/types": "^0.1.0"
  },
  "dependencies": {
    "@wavespec/kit": "^0.1.0"
  }
}

Environment Support

  • Runtime: Node.js 18+ (ES2022+)
  • Module System: ESM (no CommonJS support)
  • TypeScript: 5.0+ (with strict mode enabled)

Quick Start

Minimal Adapter (Types Only)

If you only need types from @wavespec/types, you don't need the kit:

import type {
  Adapter,
  TestContext,
  TestResult,
  ContentItem,
} from "@wavespec/types";

export class MinimalAdapter implements Adapter {
  readonly name = "minimal";
  readonly version = "0.1.0";

  async run(spec: TestSpec, ctx: TestContext): Promise<TestResult> {
    const content: ContentItem[] = [
      { type: "text", text: "Hello, world!" },
    ];

    return {
      response: {
        content,
        output_text: "Hello, world!",
        isError: false,
        raw: {},
      },
      durationMs: 0,
    };
  }
}

Standard Adapter (With Response + Error Helpers)

Use the kit to reduce boilerplate:

import type {
  Adapter,
  TestContext,
  TestResult,
} from "@wavespec/types";
import { textContent, computeOutputText } from "@wavespec/kit/response";
import {
  ErrorMapper,
  type ErrorPattern,
} from "@wavespec/kit/error";

export class StandardAdapter implements Adapter {
  readonly name = "standard";
  readonly version = "0.1.0";

  private errorMapper = new ErrorMapper({
    defaultCode: "EXECUTION_ERROR",
    defaultHint: "An unexpected error occurred",
  });

  constructor() {
    this.errorMapper.addPattern({
      match: /timeout|timed out/i,
      code: "OPERATION_TIMEOUT",
      hint: "The operation took too long to complete",
      fix: "Increase the timeout or check for performance issues",
    } as ErrorPattern);
  }

  async run(spec: TestSpec, ctx: TestContext): Promise<TestResult> {
    try {
      const result = await this.executeOperation(spec);
      const content = [textContent(JSON.stringify(result))];

      return {
        response: {
          content,
          output_text: computeOutputText(content),
          isError: false,
          raw: result,
        },
        durationMs: 0,
      };
    } catch (error) {
      throw this.errorMapper.map(error, { operation: spec.operation });
    }
  }

  private async executeOperation(spec: TestSpec): Promise<unknown> {
    // Implementation
    return {};
  }
}

Documentation

Comprehensive Utility Documentation

For detailed documentation on all 7 utility categories, see:


Subpath Exports

Response Helpers (@wavespec/kit/response)

Purpose: Normalize responses from different protocols (HTTP, MCP, OpenAI, etc.) into a universal ContentItem[] format.

When to use: Every adapter needs to normalize responses into the standard format.

Full Documentation

Available Types

From @wavespec/kit/response:

  • NormalizedResponse - The normalized output with content, output_text, and error flag
  • ContentBuilder<T> - Function type for converting protocol-specific items to ContentItems
  • PathExtractor<T> - Flexible path specification (string, array, or function)
  • ResponseNormalizerConfig<T> - Schema-driven configuration for response transformation
  • OutputTextComputer - Function type for computing output_text from ContentItems
  • ResponseTransformer<T> - Full response transformation function

Example: Normalizing HTTP Response

import type {
  ResponseNormalizerConfig,
  NormalizedResponse,
} from "@wavespec/kit/response";

// Schema defines how to extract content from HTTP response
const httpSchema: ResponseNormalizerConfig<HttpResponse> = {
  contentPath: "data", // Extract from response.data
  errorPath: "error", // Check response.error for errors
  errorExtractor: (data) => data.status >= 400,
  itemNormalizer: (item) => ({
    type: "text",
    text: JSON.stringify(item, null, 2),
  }),
};

// Coming in Phase 2: ResponseNormalizer class will apply this schema

Example: Building Content Items

Coming in Phase 2, these helpers will create properly typed ContentItems:

import { textContent, imageContent } from "@wavespec/kit/response";

const items = [
  textContent("Hello, world!"),
  imageContent(
    "/9j/4AAQSkZJRg...", // base64 encoded PNG
    "image/png"
  ),
];

Error Helpers (@wavespec/kit/error)

Purpose: Map protocol-specific errors to structured CoachingErrors with helpful hints and fixes.

When to use: When catching and transforming errors from external services.

Full Documentation

Available Types

From @wavespec/kit/error:

  • ErrorMatcher - Pattern matching strategies (string, RegExp, or function)
  • ErrorPattern - Complete error definition with matching, code, hints, and fix suggestions
  • ErrorMapperConfig - Configuration for error mapper defaults
  • ErrorContext - Context passed when mapping errors (operation, tool name, etc.)
  • MappedError - The final structured error after mapping

Example: Pattern-Based Error Handling

import {
  ErrorMapper,
  type ErrorPattern,
  type ErrorContext,
} from "@wavespec/kit/error";
import { ERROR_CODES } from "@wavespec/types";

const errorMapper = new ErrorMapper({
  defaultCode: "ADAPTER_ERROR",
  defaultHint: "An unexpected error occurred",
  defaultFix: "Enable debug logging for more details",
});

// String matcher - case-insensitive substring
errorMapper.addPattern({
  match: "ECONNREFUSED",
  code: "ADAPTER_CONNECTION_FAILED",
  hint: "The server refused the connection",
  fix: "Verify the server is running and the hostname is correct",
} as ErrorPattern);

// RegExp matcher - pattern matching
errorMapper.addPattern({
  match: /timeout|timed out/i,
  code: "OPERATION_TIMEOUT",
  messageTemplate: "Operation timed out: {message}",
  hint: "The server took too long to respond",
  fix: "Increase the timeout or check server performance",
} as ErrorPattern);

// Function matcher - custom logic
errorMapper.addPattern({
  match: (error) => (error as any).code === "ERR_MODULE_NOT_FOUND",
  code: "DEPENDENCY_NOT_FOUND",
  hint: "A required dependency is missing",
  fix: "Run npm install to install dependencies",
} as ErrorPattern);

// Usage
try {
  await operation();
} catch (error) {
  const context: ErrorContext = { operation: "list_tools" };
  throw errorMapper.map(error, context);
}

Common Error Codes

Error codes are imported from @wavespec/types:

import { ERROR_CODES } from "@wavespec/types";

// Available codes include:
// - ADAPTER_CONNECTION_FAILED
// - OPERATION_TIMEOUT
// - INVALID_PARAMETERS
// - RESOURCE_NOT_FOUND
// - AUTHENTICATION_FAILED
// - UNKNOWN_ERROR
// ... and 43+ more

Discovery Helpers (@wavespec/kit/discovery)

Purpose: Fuzzy-match tool/resource names to provide helpful suggestions when exact matches aren't found.

When to use: When validating requested tools/resources against available options.

Full Documentation

Available Types

From @wavespec/kit/discovery:

  • FuzzyMatchResult - Result containing matched value, distance, and similarity
  • FuzzyMatchOptions - Configuration for match behavior (maxResults, minSimilarity, caseSensitive)

Example: Suggesting Correct Tool Names

import {
  findClosestMatches,
  type FuzzyMatchResult,
  type FuzzyMatchOptions,
} from "@wavespec/kit/discovery";

// Get available tools from cache
const tools = await ctx.discoveryCache?.getTools(async () => {
  return client.listTools();
});

const toolNames = tools.map((t) => t.name);

// User requested "reead_tool" - find similar names
const input = "reead_tool";
const options: FuzzyMatchOptions = {
  maxResults: 3,
  minSimilarity: 0.4, // 40% similarity threshold
  caseSensitive: false,
};

const suggestions: FuzzyMatchResult[] = findClosestMatches(
  input,
  toolNames,
  options
);

// suggestions[0] might be: { value: "read_tool", distance: 1, similarity: 0.91 }

if (suggestions.length > 0) {
  console.log(`Did you mean: ${suggestions[0].value}?`);
} else {
  console.log("No similar tools found");
}

VCR Helpers (@wavespec/kit/vcr)

Purpose: Extract request data from test specs for cassette-based record/replay functionality.

When to use: When implementing adapters that support cassette recording for faster testing.

Full Documentation

Available Types

From @wavespec/kit/vcr:

  • OperationData - Extracted operation with type and adapter-specific data
  • RequestExtractor - Interface for implementing extraction in your adapter
  • ExtractorConfig - Configuration for extraction behavior
  • Cassette - Full cassette format for stored interactions
  • Interaction - Single recorded request/response pair

Example: Implementing Request Extraction

import type {
  RequestExtractor,
  OperationData,
  ExtractorConfig,
} from "@wavespec/kit/vcr";
import type { TestSpec } from "@wavespec/types";

// Coming in Phase 2: BaseRequestExtractor base class

export class HttpRequestExtractor implements RequestExtractor {
  constructor(private config?: ExtractorConfig) {}

  extract(spec: TestSpec): OperationData {
    const { operation, params } = spec;

    return {
      operation,
      data: {
        path: params.path,
        query: params.query,
        headers: params.headers,
        body: params.body,
        method: params.method,
      },
    };
  }
}

// Usage in adapter
export class HttpAdapter {
  private extractor = new HttpRequestExtractor();

  // When recording: pass extracted data to cassette recorder
  const operationData = this.extractor.extract(spec);
  cassette.record(operationData, response);

  // When replaying: use data to find matching cassette
  const operationData = this.extractor.extract(spec);
  const recorded = cassette.find(operationData);
}

Fixtures (@wavespec/kit/fixtures)

Purpose: Create test data without boilerplate for unit testing adapters.

When to use: When writing unit tests for adapter logic.

Available Utilities

From @wavespec/kit/fixtures:

  • createTestSpec(options?) - Generate TestSpec objects with smart defaults
  • text(text, mimeType?) - Create text ContentItem
  • json(obj) - Create JSON text ContentItem from object
  • image(data, mimeType?) - Create image ContentItem
  • resource(data, mimeType?) - Create resource ContentItem
  • createMockDiscoveryCache(options?) - Create mock discovery cache

Example: Using Fixtures in Tests

import { createTestSpec, text, json } from "@wavespec/kit/fixtures";
import { MyAdapter } from "./my-adapter";

describe("MyAdapter", () => {
  it("should parse JSON responses", () => {
    const adapter = new MyAdapter();
    const spec = createTestSpec({
      operation: "fetch",
      params: { url: "https://api.example.com" }
    });

    const mockResponse = {
      content: [json({ status: "ok", data: [1, 2, 3] })]
    };

    const result = adapter.parseResponse(mockResponse);
    expect(result.data).toEqual([1, 2, 3]);
  });

  it("should handle text content", () => {
    const adapter = new MyAdapter();
    const content = [text("Hello world", "text/plain")];

    const output = adapter.computeOutput(content);
    expect(output).toBe("Hello world");
  });
});

Testing Utilities (@wavespec/kit/testing)

Purpose: Create mock test contexts for adapter unit testing.

When to use: When testing adapter internals that require a TestContext.

Available Utilities

From @wavespec/kit/testing:

  • createMockContext(overrides?) - Create TestContext with smart defaults

Example: Testing with Mock Context

import { createMockContext } from "@wavespec/kit/testing";
import { createTestSpec } from "@wavespec/kit/fixtures";
import { MyAdapter } from "./my-adapter";

describe("MyAdapter with context", () => {
  it("should respect context timeout", async () => {
    const adapter = new MyAdapter();
    const ctx = createMockContext({
      mode: "live",
      timeout: 30000,
      adapterConfig: { apiKey: "test-key" }
    });

    const spec = createTestSpec({
      operation: "fetch",
      params: { url: "https://api.example.com" }
    });

    const result = await adapter.run(spec, ctx);
    expect(result.response.isError).toBe(false);
  });
});

Testing Your Adapter

There are two types of testing for adapters:

1. Unit Tests (Use adapter-kit utilities)

For testing adapter internals, parsing logic, and transformations:

import { describe, it, expect } from "vitest";
import { text, json, createMockContext } from "@wavespec/kit/fixtures";
import { MyAdapter } from "./my-adapter";

describe("MyAdapter unit tests", () => {
  it("should parse responses correctly", () => {
    const adapter = new MyAdapter({ apiKey: "test" });
    const mockResponse = {
      content: [json({ status: "ok", data: [1, 2, 3] })]
    };

    const parsed = adapter.extractData(mockResponse);
    expect(parsed).toEqual([1, 2, 3]);
  });
});

2. Integration Tests (Use harness CLI)

For end-to-end testing with real or recorded API interactions, use the harness CLI:

# tests/my-adapter.yaml
suite_name: "My Adapter Integration Tests"
adapter: "my-adapter"
mode: "record"  # Record once, then replay
isolation: "per_test"

tests:
  - name: "should fetch data"
    operation: "fetch"
    params: { url: "https://api.example.com" }
    assert:
      not_error: true
      has_content: true
# Register your adapter
$ harness register my-adapter ./dist/my-adapter.js

# Run integration tests (first run records, subsequent runs replay)
$ harness run tests/my-adapter.yaml --adapter my-adapter

Why use the harness CLI for integration tests?

  • Full VCR record/replay support (test against real APIs, replay offline)
  • Isolation modes (per_test, per_suite, none)
  • Variable interpolation
  • Matches production behavior exactly
  • No test framework boilerplate needed
  • Cassettes ensure consistent, fast CI/CD runs

Architecture & API Design

Response Subpath

Phase 1 (Current): Type definitions

  • NormalizedResponse interface
  • ResponseNormalizerConfig for schema-based transformation
  • PathExtractor, ContentBuilder, OutputTextComputer types

Phase 2 (Coming Soon): Implementation

  • ResponseNormalizer class - applies config to transform responses
  • computeOutputText() - extracts text from ContentItems
  • Builder functions: textContent(), imageContent(), resourceContent()

Error Subpath

Phase 1 (Current): Type definitions

  • ErrorPattern for pattern-based error matching
  • ErrorMatcher supporting string, RegExp, and function matchers
  • ErrorMapperConfig for defaults
  • ErrorContext for operation context

Phase 2 (Coming Soon): Implementation

  • ErrorMapper class - applies patterns to errors
  • Common error patterns for network, timeout, validation failures

Discovery Subpath

Phase 1 (Current): Type definitions

  • FuzzyMatchResult with distance and similarity scores
  • FuzzyMatchOptions for controlling match behavior

Phase 2 (Coming Soon): Implementation

  • findClosestMatches() - Levenshtein distance-based fuzzy matching
  • Built on top of fastest-levenshtein for performance

VCR Subpath

Phase 1 (Current): Type definitions

  • RequestExtractor interface
  • OperationData format
  • Cassette format with interactions
  • ExtractorConfig for customization

Phase 2 (Coming Soon): Implementation

  • BaseRequestExtractor abstract class
  • Helper utilities for common extraction patterns

Development Roadmap

Phase 1: Foundations (Complete)

  • Package infrastructure
  • Subpath exports configured
  • Type definitions for all 4 helpers
  • Build/test infrastructure

Phase 2: Core Helpers (In Progress)

Week 3:

  • Response normalization implementation
  • Error mapping implementation
  • Tests for response and error helpers
  • 100% coverage

Week 4:

  • Fuzzy matching implementation
  • VCR helper base classes
  • Tests for discovery and VCR helpers
  • HTTP adapter migration (proof-of-concept)

Phase 3: Developer Tools (Planned)

  • @harness/adapter-devkit package
  • Test context builders
  • Assertion helpers
  • Scaffolding CLI (create-adapter)

Example Adapters

Minimal HTTP Adapter

import type { Adapter, TestContext, TestResult } from "@wavespec/types";

export class MinimalHttpAdapter implements Adapter {
  readonly name = "http";
  readonly version = "0.1.0";

  async run(spec: TestSpec, ctx: TestContext): Promise<TestResult> {
    const response = await fetch(spec.params.url);
    const data = await response.text();

    return {
      response: {
        content: [{ type: "text", text: data }],
        output_text: data,
        isError: !response.ok,
        raw: { status: response.status },
      },
      durationMs: 0,
    };
  }
}

Full-Featured HTTP Adapter (With Helpers)

Coming in Phase 2 - will show:

  • Response normalization with schema
  • Error mapping with patterns
  • Fuzzy matching for resource discovery
  • VCR cassette recording

Types and Imports

All Available Types

// Response types
import type {
  NormalizedResponse,
  ContentBuilder,
  PathExtractor,
  ResponseNormalizerConfig,
  OutputTextComputer,
  ResponseTransformer,
} from "@wavespec/kit/response";

// Error types
import type {
  ErrorMatcher,
  ErrorPattern,
  ErrorMapperConfig,
  ErrorContext,
  MappedError,
} from "@wavespec/kit/error";

// Discovery types
import type {
  FuzzyMatchResult,
  FuzzyMatchOptions,
} from "@wavespec/kit/discovery";

// VCR types
import type {
  OperationData,
  RequestExtractor,
  ExtractorConfig,
  Cassette,
  Interaction,
  ResponseData,
} from "@wavespec/kit/vcr";

Importing from adapter-types

Many types come from the base @wavespec/types package:

import type {
  Adapter,
  TestContext,
  TestResult,
  TestSpec,
  ValidationResult,
  ContentItem,
  CoachingError,
  ErrorSeverity,
  RecoveryStrategy,
} from "@wavespec/types";

import { ERROR_CODES } from "@wavespec/types";

Dependencies

Production Dependencies

  • fastest-levenshtein@^1.0.16 - High-performance Levenshtein distance for fuzzy matching

Peer Dependencies

  • @wavespec/types@^0.1.0 - Base types (required)

No Runtime Dependencies

The kit has minimal dependencies:

  • No heavy frameworks
  • No polyfills
  • Pure TypeScript/JavaScript
  • Fast and lightweight

API Stability

This package follows semantic versioning:

  • Major (0.x): Breaking changes to public API
  • Minor (.x): New features, backward compatible
  • Patch (.x.x): Bug fixes only

Current Status: 0.1.0 - Experimental

While in 0.x, minor version bumps may include breaking changes. After 1.0.0, we commit to semantic versioning.


Contributing

Contributions are welcome! See the main repository's CONTRIBUTING.md for details.

For adapter-kit specifically:

  • Add types and implementations to appropriate subpaths
  • Keep related helpers together (don't split response helpers)
  • Update JSDoc comments with examples
  • Add tests for all public APIs (100% coverage target)
  • Update this README if adding new helpers

Testing

The adapter-kit package includes comprehensive tests for all helpers:

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Type checking
npm run type-check

# Build
npm run build

Troubleshooting

"Cannot find module '@wavespec/kit'"

Make sure you've installed both packages:

npm install @wavespec/kit @wavespec/types

"Cannot find module '@wavespec/kit/response'"

The subpath exports might not be set up correctly. Verify your import matches the available subpaths:

  • @wavespec/kit/response
  • @wavespec/kit/error
  • @wavespec/kit/discovery
  • @wavespec/kit/vcr

TypeScript strict mode errors

The kit is designed for TypeScript strict mode. Make sure your tsconfig.json has:

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true
  }
}

Reference Links


License

MIT - See LICENSE file in root repository


Support

For issues, questions, or suggestions:

  1. Check the troubleshooting section above
  2. Open an issue on GitHub
  3. See existing adapter implementations for examples

Last Updated: November 6, 2025 Version: 0.1.0 Status: Phase 1 Complete - Accepting Phase 2 Implementations