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

tyforge

v0.2.22

Published

Type-safe schema validation, Result pattern, and DDD building blocks for TypeScript

Readme

TyForge

Português

npm version license TypeScript Node.js

Type-safe schema validation, Result pattern and DDD building blocks for TypeScript.

Zero boilerplate — install, import, use. Every validation, every naming convention, every architectural pattern already exists in TyForge. You write domain logic, not infrastructure.

Why TyForge?

For humans: standardized code, zero debate

TyForge eliminates one of the biggest hidden costs in software teams: deciding how to do things.

Every project that adopts TyForge speaks the same language — same prefixes, same conventions, same validation flow and error handling. A developer familiar with TyForge can open any project based on it and quickly understand the entire structure.

This goes beyond aesthetics. When validation, naming, domain rules, and error handling are already built into the framework, developers stop writing repetitive code and focus exclusively on business logic.

The gain is cumulative:

  • Faster onboarding
  • More objective code reviews
  • Fewer inconsistencies
  • Safer refactoring

Because every project follows the exact same mental model.

For AI: less ambiguity, better results

AI coding assistants work best when the environment is predictable. TyForge provides exactly that: a closed set of patterns, rules, and structures that eliminate ambiguity.

Instead of inferring conventions, the AI follows a system already defined — and validated by the code itself.

In practice, this results in:

  • High first-try accuracy — generated code tends to compile and pass validations without adjustments, thanks to the type system and linter constraints
  • Consistent output — regardless of model or session, the structure generated for Entities, UseCases, and TypeFields follows the same pattern
  • Automatic self-correction — errors are quickly caught by tsc and tyforge-lint, reducing manual iteration
  • Safe composition — AI can build complex models by combining TypeFields, schemas, and domain events without relying on global context

The result: less trial and error, more usable code from the first generation.

TyForge provides the structure. AI provides the speed.

Full-stack: one single source of truth

TyForge runs anywhere TypeScript runs — Node.js, Deno, Bun, Angular, React, Vue, Next.js, React Native, Electron. The same TypeFields that validate input on your API validate forms on your UI.

This means:

  • Validations don't need to be rewritten
  • Formatting doesn't diverge between client and server
  • Business rules aren't scattered

A single schema definition becomes the source of truth for the entire stack.

TyForge centralizes the rules. Both sides just consume.

Installation

npm install tyforge

Quick Start

Schema validation with automatic type inference

import { SchemaBuilder, FString, FEmail, FInt, isSuccess } from "tyforge";
import type { ISchema, InferProps, InferJson } from "tyforge";

const userSchema = {
  name:  { type: FString },
  email: { type: FEmail },
  age:   { type: FInt, required: false },
} satisfies ISchema;

// Types inferred automatically — zero manual annotation
type TUserJson = InferJson<typeof userSchema>;
// => { name: string; email: string; age?: number }

type TUserProps = InferProps<typeof userSchema>;
// => { name: FString; email: FEmail; age?: FInt }

const validator = SchemaBuilder.compile(userSchema);
const result = validator.create({ name: "Maria Silva", email: "[email protected]" });

if (isSuccess(result)) {
  result.value.name.getValue();  // "Maria Silva"
  result.value.email.getValue(); // "[email protected]"
}

Result pattern — functional error handling

import { ok, err, isSuccess, isFailure } from "tyforge";
import type { Result } from "tyforge";

function divide(a: number, b: number): Result<number, Error> {
  if (b === 0) return err(new Error("Division by zero"));
  return ok(a / b);
}

const result = divide(10, 2)
  .map(v => v * 100)           // 500
  .flatMap(v => divide(v, 5))  // 100
  .fold(
    value => `Result: ${value}`,
    error => `Error: ${error.message}`,
  );
// "Result: 100"

TypeFields — validated Value Objects

import { FEmail, FDocumentCpf, FCurrency, FPassword, TypeField } from "tyforge";

// Every TypeField validates on creation
const email = FEmail.createOrThrow("[email protected]");
email.getValue();  // "[email protected]"
email.formatted(); // "[email protected]"

// CPF with mod 11 check digit validation
const cpf = FDocumentCpf.createOrThrow("52998224725");
cpf.formatted(); // "529.982.247-25" (progressive mask)

// Password with complexity rules (uppercase, lowercase, digit, special, 8+ chars)
const pwd = FPassword.createOrThrow("S3cur3!Pass");

// Currency with locale-aware formatting
TypeField.configure({ localeDisplay: "br" });
const price = FCurrency.createOrThrow(1234.50);
price.getValue();       // 123450 (stored as integer cents)
price.toDecimalValue(); // 1234.5
price.formatted();      // "1.234,50" (Brazilian format)

Money arithmetic — zero floating point issues

import { FMoney, FCurrency } from "tyforge";

const a = FCurrency.createOrThrow(0.1);
const b = FCurrency.createOrThrow(0.2);
const sum = a.add(b);
// 0.1 + 0.2 = 0.30 (not 0.30000000000000004)
// Internally: 10 + 20 = 30 cents

const price = FMoney.createOrThrow(1050);  // 1050 cents
price.toDecimal();    // 10.50
price.isPositive();   // true
price.isZero();       // false

const discount = FMoney.createOrThrow(200);
const final = price.subtract(discount); // 850 cents

Domain Models — Entity, Aggregate, Domain Events

import { SchemaBuilder, FId, FString, FEmail, ok, isFailure } from "tyforge";
import { Aggregate, DomainEvent } from "tyforge";
import type { ISchema, InferProps, InferJson } from "tyforge";

const userSchema = {
  id:    { type: FId, required: false },
  name:  { type: FString },
  email: { type: FEmail },
} satisfies ISchema;

type TUserProps = InferProps<typeof userSchema>;
type TUserJson = InferJson<typeof userSchema>;

class EventUserCreated extends DomainEvent<{ userId: string; email: string }> {
  readonly queueName = "user-events";
  static create(userId: string, email: string): EventUserCreated {
    return new EventUserCreated("user.created", { userId, email });
  }
}

class User extends Aggregate<TUserProps, TUserJson> implements TUserProps {
  readonly id: FId | undefined;
  readonly name: FString;
  readonly email: FEmail;

  protected readonly _classInfo = { name: "User", version: "1.0.0", description: "User aggregate" };

  private constructor(props: TUserProps) {
    super();
    this.id = props.id;
    this.name = props.name;
    this.email = props.email;
  }

  static create(data: TUserJson) {
    const validator = SchemaBuilder.compile(userSchema);
    const result = validator.create(data);
    if (isFailure(result)) return result;

    const user = new User(result.value);
    const id = user.id?.getValue() ?? FId.generateId();
    user.addDomainEvent(EventUserCreated.create(id, user.email.getValue()));
    return ok(user);
  }
}

Expose / Redaction — control JSON output

const schema = {
  name:     { type: FString },
  email:    { type: FEmail, expose: "private" },
  password: { type: FPassword, expose: "redacted" },
} satisfies ISchema;

// toJSON() or toJSON(config, "public") — hides private + redacted
// => { name: "Maria" }

// toJSON(config, "private") — shows private, hides redacted
// => { name: "Maria", email: "[email protected]" }

// toJSON(config, "redacted") — shows everything
// => { name: "Maria", email: "[email protected]", password: "S3cur3!Pass" }

Locale system — display + region + data, independently configured

import { TypeField, FBankCode, FCurrency, FDocumentCpf } from "tyforge";

// Configure three axes separately: display, region rules, and data formatting
TypeField.configure({ localeDisplay: "br", localeRegion: "br", localeData: "br" });

// localeRegion: "br" → enforces ISPB 8-digit format
FBankCode.createOrThrow("60701190"); // OK (Itau ISPB)

// localeDisplay: "br" → formats with Brazilian separators
FCurrency.createOrThrow(1234.50).formatted(); // "1.234,50"

// localeData: "br" → formatted("data") for API/persistence formatting
FCurrency.createOrThrow(1234.50).formatted("data"); // API-ready format

// Strict types (TLocaleDisplay, TLocaleRegion, TLocaleData) with exhaustive switch:
// adding a new locale causes compile errors wherever handling is missing

Form input normalization

import { FCurrency, FInt } from "tyforge";

// formCreate/formAssign normalize string inputs from HTML forms
FCurrency.formCreate("1234,50");  // "1234,50" → 1234.50 → 123450 cents
FInt.formCreate("42");            // "42" → 42
FInt.formCreate("0x1A");          // rejected (hex not allowed)
FInt.formCreate("1e5");           // rejected (scientific notation not allowed)

Batch validation with parallel workers

import { SchemaBuilder, FString, FEmail, batchCreate } from "tyforge";
import type { ISchema } from "tyforge";

const schema = {
  name:  { type: FString },
  email: { type: FEmail },
} satisfies ISchema;

const items = Array.from({ length: 10000 }, (_, i) => ({
  name: `User ${i}`,
  email: `user${i}@company.com`,
}));

// Sequential
const result = await batchCreate(schema, items);

// Parallel with 4 worker threads
const result = await batchCreate(schema, items, { concurrency: 4 });
// result.successes: validated items
// result.errors: items that failed with index + error detail

Exceptions — RFC 7807 with lazy stack trace

import { ExceptionValidation, ExceptionBusiness, ExceptionNotFound, OHttpStatus } from "tyforge";

const err = ExceptionValidation.create("email", "Invalid email format");
err.status; // 400
err.toJSON();
// {
//   type: "validation",
//   title: "Validation Error",
//   status: 400,
//   detail: "Invalid email format",
//   code: "email"
// }

// Stack trace captured only when accessed (lazy) — zero cost in batch validation

Nested schemas and arrays

const orderSchema = {
  id:      { type: FId },
  address: {
    street: { type: FString },
    city:   { type: FString },
    state:  { type: FStateCode },
  },
  items: [{
    type: {
      productId: { type: FId },
      quantity:  { type: FInt },
      price:    { type: FMoney },
    },
  }],
} satisfies ISchema;

// Deeply nested validation with indexed error paths:
// "items[2].price" → "Value must be an integer (cents)"

Linter — enforces conventions automatically

npx tyforge-lint              # check all .ts files
npx tyforge-lint --staged     # check only staged files
npx tyforge-lint --fix        # auto-fix what's possible
npx tyforge-lint --init       # setup pre-commit hooks (Husky/Lefthook/native)

10 rules: no-any, no-cast, no-non-null, no-ts-ignore, no-export-default, no-to-json-lowercase, no-new-type-field, no-magic-http-status, no-declare, no-satisfies-without-prefix.

Modules

| Module | Description | |--------|-------------| | Result Pattern | ok(), err(), map, flatMap, fold, match, all, allSettled, toPromise — functional error handling with zero exceptions | | Schema Builder | Compiled schema validation with InferProps/InferJson type inference, nested objects, arrays, batchCreate, composeSchema | | Type Fields | Validator Value Objects — strings, emails, currency, banking, documents, PIX, security, enums, dates, identifiers | | Domain Models | Entity, ValueObject, Aggregate (with domain events), Dto, DtoReq, DtoRes — full DDD building blocks | | Exceptions | RFC 7807 exception types with lazy stack trace and OHttpStatus constants | | Application | UseCase, IMapper, Saga, DomainEventDispatcher, CQRS interfaces | | Infrastructure | Repository, RepositoryRead, RepositoryWrite, RepositoryCrud, Paginated, ServiceBase, IUnitOfWork, IOutbox | | HTTP Client | ServiceHttp abstract base class — secure HTTP client with Result pattern, SSRF/CRLF/path traversal protection, timeout, ExceptionHttp | | GraphQL Client | ServiceGraphQL abstract base class — secure GraphQL client with Result pattern, introspection blocking, prototype pollution protection, UNAUTHENTICATED detection, ExceptionGraphQL | | WebSocket Client | ServiceWebSocket abstract base class — WebSocket client with connect/disconnect/send/subscribe, reconnect with jitter and delay cap, DNS rebinding protection, ExceptionWebSocket | | Tools | TypeGuard, ToolObjectTransform (flatten/unflatten), ToolCliParser, ToolFileDiscovery, ToolGit, ToolNetworkSecurity | | Linter | npx tyforge-lint — 10 rules with --init / --fix / --format json for CI | | Config | tyforge.config.json — global validation levels (full, type, none) and linter settings |

Type Fields by Category

| Category | Fields | |----------|--------| | Strings | FString, FText, FDescription, FFullName, FBusinessName, FEmail | | Numeric | FInt, FFloat | | Pagination | FPageNumber, FPageSize, FSortOrder | | Currency | FMoney (integer cents), FCurrency (decimal convenience) — arithmetic, comparisons, fromDecimal() | | Dates | FDate, FDateISODate, FDateTimeISOZ, FDateTimeISOZMillis, FDateISOCompact, FDateTimeISOCompact, FDateTimeISOFullCompact | | Identifiers | FId, FIdReq, FTransactionId, FDeviceId, FCorrelationId, FReconciliationId, FIdempotencyKey, FCertificateThumbprint | | Documents | FDocumentCpf, FDocumentCnpj, FDocumentCpfOrCnpj, FDocumentRg, FDocumentId, FDocumentType, FDocumentStateRegistration, FDocumentMunicipalRegistration | | Banking | FBankCode, FBankBranch, FBankAccountNumber, FBankNsu, FBankE2eId, FEmvQrCodePayload | | PIX | FPixKey, FPixKeyType | | Security | FApiKey, FBearer, FPassword, FSignature, FPublicKeyPem, FTotpCode, FTotpSecret, FHashAlgorithm | | HTTP | FHttpMethod, FHttpFormat, FHttpStatus | | GraphQL | FGraphQLDocument, FGraphQLOperationName, FFetchPolicy | | URL | FUrlOrigin, FUrlFull, FUrlPath, FUrlDns, FUrlQuery | | Enums | FAppStatus, FBoolInt, FPersonType, FGender, FMaritalStatus, FTransactionStatus, FStateCode | | Other | FBoolean, FJson, FTraceId |

Subpath Exports

import { ok, err } from "tyforge/result";
import { FString, FEmail } from "tyforge/type-fields";
import { ExceptionValidation } from "tyforge/exceptions";
import { SchemaBuilder } from "tyforge/schema";
import { TypeGuard } from "tyforge/tools";
import { ServiceHttp, ExceptionHttp } from "tyforge/http";
import { ServiceGraphQL, ExceptionGraphQL } from "tyforge/graphql";

Documentation

Full documentation at tyforge.navegarsistemas.com.br.

License

MIT - Navegar Sistemas