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

@git-stunts/trailer-codec

v2.1.1

Published

Robust encoder/decoder for structured metadata in Git commit messages

Readme

@git-stunts/trailer-codec

npm version CI license

A robust encoder/decoder for structured metadata within Git commit messages.

Key Features

  • Standard Compliant: Follows the Git "trailer" convention (RFC 822 / Email headers)
  • DoS Protection: Built-in 5MB message size limit to prevent attacks
  • Structured Domain: Formalized entities and value objects for type safety
  • Zod Validation: Schema-driven validation with helpful error messages
  • Case Normalization: Trailer keys normalized to lowercase for consistency
  • Pure Domain Logic: No I/O, no Git subprocess execution

Design Principles

  1. Domain Purity: Core logic independent of infrastructure
  2. Type Safety: Value Objects ensure data validity at instantiation
  3. Immutability: All entities are immutable
  4. Separation of Concerns: Encoding/decoding in dedicated service

Prerequisites

  • Node.js: >= 20.0.0

Installation

npm install @git-stunts/trailer-codec

Developer & Testing

  • Node.js ≥ 20 matches the engines field in package.json and is required for Vitest/ESM support.
  • npm test runs the Vitest suite, npm run lint validates the code with ESLint, and npm run format formats files with Prettier; all scripts target the entire repo root.
  • Consult TESTING.md for run modes, test filters, and tips for extending the suite before submitting contributions.

Usage

Basic Encoding/Decoding

import { createDefaultTrailerCodec } from '@git-stunts/trailer-codec';

const codec = createDefaultTrailerCodec();
const message = codec.encode({
  title: 'feat: add user authentication',
  body: 'Implemented OAuth2 flow with JWT tokens.',
  trailers: [
    { key: 'Signed-off-by', value: 'James Ross' },
    { key: 'Reviewed-by', value: 'Alice Smith' },
  ],
});

console.log(message);
// feat: add user authentication
//
// Implemented OAuth2 flow with JWT tokens.
//
// signed-off-by: James Ross
// reviewed-by: Alice Smith

const decoded = codec.decode(message);
console.log(decoded.title);      // "feat: add user authentication"
console.log(decoded.trailers);   // { 'signed-off-by': 'James Ross', 'reviewed-by': 'Alice Smith' }

API Patterns

  • Primary entry point: createDefaultTrailerCodec() returns a TrailerCodec wired with a fresh TrailerCodecService; use .encode()/.decode() (or .encodeMessage()/.decodeMessage()) to keep configuration in one place.
  • Facade: TrailerCodec keeps configuration near instantiation while still leveraging createMessageHelpers() under the hood (pass your own service when you need control).
  • Advanced: createConfiguredCodec() and direct TrailerCodecService usage let you swap schema bundles, parsers, formatters, or helper overrides when you need custom validation or formatting behavior. The standalone helpers encodeMessage()/decodeMessage() remain available as deprecated convenience wrappers.

Breaking Changes

  • decodeMessage() now trims trailing newlines in the version v0.2.0+ runtime, so plain string inputs will no longer include a final \n unless you opt into it.
  • To preserve the trailing newline you rely on (e.g., when round-tripping commit templates), either instantiate TrailerCodec with bodyFormatOptions: { keepTrailingNewline: true }, call formatBodySegment(body, { keepTrailingNewline: true }) yourself, or pass the same option through createConfiguredCodec.
  • See docs/MIGRATION.md#v020 for the full migration checklist and decoding behavior rationale.

Body Formatting & Facade

decodeMessage now trims the decoded body by default, returning the content exactly as stored; no extra newline is appended automatically. If you still need the trailing newline (for example when writing the decoded body back into a commit template), instantiate the helpers or facade with bodyFormatOptions: { keepTrailingNewline: true }:

import TrailerCodec from '@git-stunts/trailer-codec';

const codec = new TrailerCodec({ bodyFormatOptions: { keepTrailingNewline: true } });
const payload = codec.decode('Title\n\nBody\n');
console.log(payload.body); // 'Body\n'

You can also call the exported formatBodySegment(body, { keepTrailingNewline: true }) helper directly when you need the formatting logic elsewhere.

import { formatBodySegment } from '@git-stunts/trailer-codec';

const trimmed = formatBodySegment('Body\n', { keepTrailingNewline: true });
console.log(trimmed); // 'Body\n'

Advanced

Configured Codec Builder

When you need a prewired codec (custom key patterns, parser tweaks, formatter hooks), use createConfiguredCodec({ keyPattern, keyMaxLength, parserOptions }). It builds a schema bundle, parser, and service for you, and returns helpers so you can immediately call decodeMessage/encodeMessage:

import { createConfiguredCodec } from '@git-stunts/trailer-codec';

const { decodeMessage, encodeMessage } = createConfiguredCodec({
  keyPattern: '[A-Za-z._-]+',
  keyMaxLength: 120,
  parserOptions: {},
});

const payload = { title: 'feat: cli docs', trailers: { 'Custom.Key': 'value' } };
const encoded = encodeMessage(payload);
const decoded = decodeMessage(encoded);
console.log(decoded.title); // 'feat: cli docs'

Domain Entities

import { GitCommitMessage } from '@git-stunts/trailer-codec';

const msg = new GitCommitMessage({
  title: 'fix: resolve memory leak',
  body: 'Fixed WeakMap reference cycle.',
  trailers: [
    { key: 'Issue', value: 'GH-123' },
    { key: 'Signed-off-by', value: 'James Ross' }
  ]
});

console.log(msg.toString());

Public API Helpers & Configuration

  • formatBodySegment(body, { keepTrailingNewline = false }) mirrors the helper powering decodeMessage, trimming whitespace while optionally preserving the trailing newline when you plan to write the body back into a template.
  • createMessageHelpers({ service, bodyFormatOptions }) returns { decodeMessage, encodeMessage } bound to the provided TrailerCodecService; pass bodyFormatOptions to control whether decoded bodies keep their trailing newline.
  • TrailerCodec wraps createMessageHelpers() so you can instantiate a codec class with custom service or bodyFormatOptions and still leverage the helper contract via encode()/decode().
  • createConfiguredCodec({ keyPattern, keyMaxLength, parserOptions, formatters, bodyFormatOptions }) wires together createGitTrailerSchemaBundle, TrailerParser, TrailerCodecService, and the helper pair, letting you configure key validation, parser heuristics, formatting hooks, and body formatting in a single call.
  • TrailerCodecService exposes the schema bundle, parser, trailer factory, formatter hooks, and helper utilities (MessageNormalizer, extractTitle, composeBody); see docs/SERVICE.md for a deeper explanation of how to customize each stage without touching the core service.

✅ Validation Rules

Trailer codec enforces strict validation via the concrete subclasses of TrailerCodecError:

| Rule | Constraint | Thrown Error | |------|------------|--------------| | Message Size | ≤ 5MB | TrailerTooLargeError | | Title | Must be a non-empty string | CommitMessageInvalidError (during entity construction) | | Trailer Key | Alphanumeric, hyphens, underscores only (/^[A-Za-z0-9_-]+$/) and ≤ 100 characters (prevents ReDoS) | TrailerInvalidError | | Trailer Value | Cannot contain carriage returns or line feeds and must not be empty | TrailerValueInvalidError |

Key Normalization: All trailer keys are automatically normalized to lowercase (e.g., Signed-Off-Bysigned-off-by).

Blank-Line Guard: Trailers must be separated from the body by a blank line; omitting the separator throws TrailerNoSeparatorError.

Validation Errors

When TrailerCodecService or the exported helpers throw, they surface one of the following classes so you can recover with instanceof checks:

| Error | Trigger | Suggested Fix | | --- | --- | --- | | TrailerTooLargeError | Message exceeds 5MB while MessageNormalizer.guardMessageSize() runs | Split the commit or remove content until the payload fits. | | TrailerNoSeparatorError | Missing blank line before trailers when TrailerParser.split() runs | Insert the required empty line between body and trailers. | | TrailerValueInvalidError | Trailer value includes newline characters or fails the schema value rules | Remove or escape newline characters before encoding. | | TrailerInvalidError | Trailer key/value pair fails the schema validation (GitTrailerSchema) | Adjust the key/value or supply a custom schema bundle via TrailerCodecService. | | CommitMessageInvalidError | GitCommitMessageSchema rejects the full payload (title/body/trailers) | Fix the invalid field or pass a conforming payload; use formatters if needed. |

All of the above inherit from TrailerCodecError (src/domain/errors/TrailerCodecError.js) and expose meta for diagnostics; prefer checking the specific class instead of inspecting code.

🛡️ Security

  • No Code Execution: Pure string manipulation, no eval() or dynamic execution
  • DoS Protection: Rejects messages > 5MB
  • ReDoS Prevention: Max key length limits regex execution time
  • No Git Subprocess: Library performs no I/O operations
  • Line Injection Guard: Trailer values omit newline characters so no unexpected trailers can be injected

See SECURITY.md for details.

📚 Additional Documentation

  • docs/ADVANCED.md — Custom schema injection, validation overrides, and advanced integration patterns.
  • docs/PARSER.md — Step-by-step explanation of the backward-walk parser.
  • docs/INTEGRATION.md — Git log scripting, streaming decoder, and Git-CMS filtering recipes.
  • docs/SERVICE.md — How TrailerCodecService wires schema, parser, and formatter helpers for customization.
  • API_REFERENCE.md — Complete catalog of the public exports, their inputs/outputs, and notable knobs.
  • TESTING.md — How to run/extend the Vitest, lint, and format scripts plus contributor tips.
  • Git hooks: Run npm run setuphooks once per clone to point core.hooksPath at scripts/. The hook now runs just npm run lint and npm run format before each commit.

License

Apache-2.0 Copyright © 2026 James Ross