Socket
Book a DemoInstallSign in
Socket

@outfitter/contracts

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@outfitter/contracts

Core contracts for building type-safe applications, including Result, AppError, and domain types.

1.0.4
latest
Source
npmnpm
Version published
Weekly downloads
3
200%
Maintainers
1
Weekly downloads
 
Created
Source

@outfitter/contracts

Core contracts for building type-safe applications, including Result, AppError, and domain types.

Installation

npm install @outfitter/contracts
# or
pnpm add @outfitter/contracts

Overview

This package provides essential TypeScript utilities that form the foundation of type-safe development:

  • Result Pattern: Type-safe error handling without exceptions
  • AppError: Structured error representation with error codes and context
  • Type Utilities: Advanced TypeScript utility types
  • Environment Validation: Type-safe environment variable handling (via sub-path)
  • Assertions: Runtime validation with type narrowing

Core Concepts

Result Pattern

Handle errors explicitly without throwing exceptions:

import {
  Result,
  success,
  failure,
  isSuccess,
  isFailure,
} from '@outfitter/contracts';

function divide(a: number, b: number): Result<number, AppError> {
  if (b === 0) {
    return failure(makeError('VALIDATION_ERROR', 'Division by zero'));
  }
  return success(a / b);
}

// Usage
const result = divide(10, 2);

if (isSuccess(result)) {
  console.log(result.data); // 5
} else {
  console.error(result.error.message);
}

AppError

Structured errors with rich context:

import { makeError, AppError } from '@outfitter/contracts';

const error = makeError(
  'VALIDATION_ERROR',
  'Invalid email format',
  { field: 'email', value: 'not-an-email' },
  originalError // Optional: wrap caught errors
);

// Error structure
interface AppError {
  code: string;
  message: string;
  details?: unknown;
  cause?: Error;
  stack?: string;
}

Environment Validation

Type-safe environment variable handling with Zod. This is available via a sub-path import to keep the core package dependency-free.

import { validateEnv } from '@outfitter/contracts-zod';
import { z } from 'zod';

const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']),
  PORT: z.string().regex(/^\d+$/).transform(Number),
  API_KEY: z.string().min(1),
  ENABLE_FEATURE: z
    .string()
    .transform(val => val === 'true')
    .optional(),
});

const envResult = validateEnv(process.env, envSchema);

if (isSuccess(envResult)) {
  const env = envResult.data;
  // env is fully typed: { NODE_ENV: 'development' | 'production' | 'test', PORT: number, ... }
} else {
  console.error('Environment validation failed:', envResult.error);
  process.exit(1);
}

Type Utilities

Advanced TypeScript utility types:

import type {
  DeepReadonly,
  DeepPartial,
  Nullable,
  Brand,
  UnionToIntersection,
} from '@outfitter/contracts';

// Brand types for type safety
type UserId = Brand<string, 'UserId'>;
type Email = Brand<string, 'Email'>;

// Deep type transformations
type Config = {
  server: {
    port: number;
    host: string;
  };
};

type ReadonlyConfig = DeepReadonly<Config>;
type PartialConfig = DeepPartial<Config>;

Assertions

Runtime validation with type narrowing:

import { assert, assertDefined, assertNever } from '@outfitter/contracts';

// assert: Ensures condition is true
function processPositive(value: number) {
  assert(value > 0, 'Value must be positive');
  // TypeScript knows value > 0 here
}

// assertDefined: Ensures value is not null/undefined
function processUser(user: User | null) {
  assertDefined(user, 'User is required');
  // TypeScript knows user is User here
}

// assertNever: Exhaustive checking
type Status = 'pending' | 'approved' | 'rejected';

function handleStatus(status: Status) {
  switch (status) {
    case 'pending':
      return 'waiting';
    case 'approved':
      return 'success';
    case 'rejected':
      return 'failed';
    default:
      assertNever(status); // Compile error if cases missed
  }
}

Branded Types

Create nominal types from primitives to prevent accidental misuse of values like IDs or tokens.

import type { Branded } from '@outfitter/contracts';

type UserId = Branded<string, 'UserId'>;

const createUserId = (id: string): UserId => id as UserId;

const userId = createUserId('user-123');
// const otherString: string = 'abc';
// const otherUserId: UserId = otherString; // Fails to compile

API Reference

Result Functions

  • success<T>(data: T): Success<T> - Create a success result
  • failure<E>(error: E): Failure<E> - Create a failure result
  • isSuccess<T, E>(result: Result<T, E>): result is Success<T> - Type guard for success
  • isFailure<T, E>(result: Result<T, E>): result is Failure<E> - Type guard for failure
  • mapResult<T, U, E>(result: Result<T, E>, fn: (data: T) => U): Result<U, E> - Transform success value
  • flatMapResult<T, U, E>(result: Result<T, E>, fn: (data: T) => Result<U, E>): Result<U, E> - Chain results

Error Functions

  • makeError(code: string, message: string, details?: unknown, cause?: Error): AppError - Create structured error
  • isAppError(error: unknown): error is AppError - Type guard for AppError

Environment Functions (via @outfitter/contracts-zod)

  • validateEnv<T>(env: unknown, schema: ZodSchema<T>): Result<T, AppError> - Validate environment variables
  • fromZod(error: ZodError): AppError - Convert a Zod error to an AppError

Type Guards

  • isObject(value: unknown): value is Record<string, unknown> - Check if value is object
  • isString(value: unknown): value is string - Check if value is string
  • isNumber(value: unknown): value is number - Check if value is number
  • isBoolean(value: unknown): value is boolean - Check if value is boolean

Best Practices

1. Always Use Result Pattern

// ❌ Don't throw in library functions
function parseConfig(json: string): Config {
  return JSON.parse(json); // Throws on invalid JSON
}

// ✅ Return Result instead
function parseConfig(json: string): Result<Config, AppError> {
  try {
    return success(JSON.parse(json));
  } catch (error) {
    return failure(
      makeError('PARSE_ERROR', 'Invalid config format', { json }, error)
    );
  }
}

2. Use Branded Types for Domain Concepts

// ❌ Primitive types allow mixing up parameters
function sendEmail(to: string, from: string, subject: string) {}

// ✅ Branded types prevent errors
type Email = Brand<string, 'Email'>;
type Subject = Brand<string, 'Subject'>;

function sendEmail(to: Email, from: Email, subject: Subject) {}

3. Validate at Boundaries

// Validate external data immediately
const configResult = validateEnv(process.env, configSchema);
if (isFailure(configResult)) {
  console.error('Invalid configuration:', configResult.error);
  process.exit(1);
}

const config = configResult.data; // Fully typed and validated

Dependencies

This package has zero runtime dependencies in its core entry point.

The standalone @outfitter/contracts-zod package has a dependency on zod.

Development

This package is part of the @outfitter/monorepo monorepo.

# Install dependencies
pnpm install

# Run tests
pnpm test

# Build the package
pnpm build

# Type check
pnpm type-check

License

MIT

Keywords

typescript

FAQs

Package last updated on 17 Jun 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.