Skip to content

Releases: SrHenry/type-utils

v0.8.4 (2026-06-04)

05 Jun 02:48
v0.8.4
937fd6a

Choose a tag to compare

What's Changed

New Features

  • isNativeSchema(value) — type guard that checks if a value is a native type-utils TypeGuard<T>, useful for distinguishing from external Standard Schema validators
  • GetSchema<T> — type helper that maps a TypeScript type to the corresponding FluentSchema result type
  • isStandardSchema now accepts functions with ~standard property
  • is() and ensureInterface() now use native-first routing for type-utils guards

Bug Fixes

  • Schema.validator README section now shows actual .validator() usage examples
  • BooleanSchema correctly returns FluentSchema<boolean>
  • GetSchema handles void, never, and readonly array types
  • normalizeSchema early-exits for native schemas, preventing recursion in record()
  • Circular dependency resolved in isNativeSchema

Improvements

  • Schema types refactored into dedicated files (V1/V2/V3 namespaces, individual schema type files)
  • V3 wildcard re-export replaced with explicit allowlist to prevent leaking internal types
  • Schema types, ValidateReturn, and rule types re-exported from types hub

Dependencies

  • Vitest 3.2.1 → 4.1.0
  • @biomejs/biome 2.4.15 → 2.4.16
  • tsx 4.22.3 → 4.22.4
  • webpack-cli 7.0.2 → 7.0.3

Full Changelog: v0.8.3...v0.8.4

v0.8.3

27 May 23:55
v0.8.3
a748220

Choose a tag to compare

What's Changed

  • fix(pipeline): wrap pipeAsync return in Awaited to match runtime 5ce0c0c
  • style(build): format package.json with Prettier 1bb2a56

Full Changelog: v0.8.2...v0.8.3

v0.8.2 (2026-05-27)

27 May 19:51
v0.8.2
51ea39e

Choose a tag to compare

Bug Fixes

  • fix(validator): resolve inline rule overload in createInlineRule (#43)
  • fix(schema): preserve optional schema metadata (#44)
  • fix(schema): preserve array element type inference (#46)
  • refactor(validator): reduce normalize() cognitive complexity (#45)
  • refactor(schema): align _fn/OptionalizedArray overloads with ArraySchema (#49)

Internal

  • Move TypeScript and @types/node to dev/peer deps to improve Socket supply chain score
  • Extract helpers from normalize() to reduce cognitive complexity
  • Add unit tests for copyStructMetadata, createInlineRule overloads, and array inference

Full Changelog: v0.8.1...v0.8.2

v0.8.1 (2026-05-26)

26 May 10:12
v0.8.1
f590726

Choose a tag to compare

What's Changed

Features

  • Schema: Add .toStandardSchema() to 6 missing schemas (Standard Schema interop completeness) (#30)
  • Release: Add --rc, --beta, --alpha prerelease flags
  • Release: Add --bump flag for auto version calculation
  • Release: Add release automation script and pipeline docs
  • Build: Move TypeScript and @types/node to dev/peer deps to improve socket supply chain score

Bug Fixes

  • Schema: primitive() and enum validation now correctly handle null (#32)

Pipeline & Internal

  • Modularize workflows into subdirectories
  • Add AGENTS.md with AI harness session behavior and PR workflow
  • TTY detection, NO_COLOR support, and summary for all test runs
  • Fix ANSI escape and harness adapter abstraction in release script
  • Polish --help text alignment in release script

Dependencies

  • Bump @types/node from 25.9.0 to 25.9.1 (#37)
  • Bump webpack from 5.106.2 to 5.107.2 (#36)

Full Changelog: v0.8.0...v0.8.1

v0.8.0 (2026-05-25)

25 May 21:35
v0.8.0
36035d3

Choose a tag to compare

What's Changed

Three major features land in this release:


🔄 Pipeline Refactor

Pipeline internals replaced decorator-based architecture with PipelineBox/AsyncPipelineBox classes. New helpers: callWith() (reverse-apply), apply() (partial application), tap()/tapAsync() (side-effects). enpipe is now deprecated — use callWith/apply for clearer type-safe transforms. inject() removed. GetPipeline<T> type removed — use Pipe<T>.


🧪 Vitest Migration

Full migration from Jest/ts-jest to Vitest with globals mode. 318 tests passing. Build fix: test files and vitest globals excluded from production tsconfigs (TS 6 compatibility).


🔧 Biome Lint Pipeline

Biome + Prettier enforced across codebase. 525 lint warnings → 0. Precommit hook now runs: build:clean && check:fix && test && circular-dependencies.


⚡ SchemaValidator Refactor

Extracted validate() monolith (760+ lines, complexity 150) into dedicated handlers: validateObject, validateRecord, validateIntersection, validateUnion, validateDefault, validateWithoutMetadata.


🔧 Other Changes

  • feat(pipeline): add callWith reverse-apply helper, remove inject b08ffd6
  • feat(pipeline): add tap/tapAsync, restructure into layers, fix types and casts 9d0f9ce
  • refactor(pipeline): replace decorator-based internals with PipelineBox classes, add apply helper 0d6041f
  • deprecate(pipeline): mark enpipe as deprecated, recommend pipe/callWith/apply 20a4373
  • docs(README): rewrite pipeline section with callWith/apply sub-sections e4f284f
  • feat: migrate test suite from jest/ts-jest to vitest 004ffe4
  • feat: add Biome lint + Prettier format pipeline ed88069
  • chore: reduce biome lint warnings from 525 to 167 55b665a
  • chore: eliminate all remaining biome lint warnings (167 → 0) 4d7a6d9
  • refactor(validator): extract SchemaValidator.validate() cases into dedicated functions a0b6299
  • fix(build): exclude test files from production tsconfigs a1da192
  • style: clean up asTypeGuard.ts formatting, make random.ts param schemas lazy d541cc6
  • chore: remove completed SchemaValidator refactor from TODO f451250
  • docs(README): add Schema.bigint/tuple/record/asUndefined, BigInt rules, Standard Schema interop, Tag type, badges 671ec00
  • chore: bump version to 0.8.0 36035d3

🧪 Testing

318 tests pass, typecheck clean, 0 circular dependencies, 0 lint warnings.

⚠️ Breaking Changes (experimental API)

  • inject() removed — use callWith()
  • GetPipeline<T> type removed — use Pipe<T>
  • PipelineBox is now a standalone class (not decorator-based)
  • AsyncPipelineBox no longer has .catch() — use .depipe().catch()

📋 Migration Notes

  • Replace .pipe(fn => fn(value)) with .pipe(callWith(value))
  • Replace .pipe(enpipe(fn, ...args)) with .pipe(apply(fn, ...args))
  • Replace inject() usage with callWith() or plain transforms

Full Changelog: v0.7.1...v0.8.0

v0.7.1 (2026-05-24)

24 May 16:21
v0.7.1
6095815

Choose a tag to compare

What's Changed

  • fix(rules): eliminate handler(void 0) spec-check via CUSTOM_RULE_BRAND
    isCustomHandler called handler(void 0) at registration time to spec-check rule handlers, breaking the type contract and forcing users to guard against undefined on destructuring/property-access handlers. Replaced with CUSTOM_RULE_BRAND runtime symbol for brand-checking, extended Custom tuple to 4 elements [name, args, handler, formator], and deleted isCustomHandler entirely. Includes regression tests ensuring handler is never called with undefined. c90bb45

  • refactor(helpers): rewrite random.ts param schemas with createInlineRule and match dispatch
    Replace asTypeGuard validators with createInlineRule (nullParam, arrayLike, minMax), replace if/else dispatch with match().with() pattern, convert param schemas to lazy factories, extract pure helper functions. 5f1da5850fd8cb3b7d9cf0ff2e9e97b9e97f4ad3

  • fix(deps): resolve fast-uri ReDoS vulnerability via webpack/ajv transitive dep
    Bump fast-uri resolution to ^3.1.2, patching ReDoS vulnerabilities reported in Dependabot alerts #36 and #37. Also bumps: jest 30.3.0 → 30.4.2, @types/node 25.6.0 → 25.6.2, @sinonjs/fake-timers ^15.0.0 → ^15.4.0. d9b3ac8

  • style: format all src files with prettier e0041c7

🔀 Merged Pull Requests

  • build(deps): bump @types/node from 25.6.2 to 25.9.0 (#24)
  • build(deps-dev): bump ts-jest from 29.4.9 to 29.4.10 (#25)
  • build(deps-dev): bump tsx from 4.21.0 to 4.22.3 (#23)

Full Changelog: v0.7.0...v0.7.1

v0.7.0 (2026-04-27)

27 Apr 10:27
v0.7.0
7c42347

Choose a tag to compare

What's Changed

Two major features land in this release: full Standard Schema V1 interoperability and ESM migration with dual CJS+ESM output.


🔄 Standard Schema V1 Interoperability

Implements the Standard Schema V1 spec — the common interface created by the authors of Zod, Valibot, and ArkType that allows any tool accepting "a schema" to work with any conforming library interchangeably.

Producer direction — all TypeGuards and schemas now expose ~standard automatically:

import { string, object, number } from '@srhenry/type-utils'

const user = object({ name: string(), age: number() })

// Any Standard Schema consumer can use this
const result = user['~standard'].validate({ name: 'Alice', age: 30 })
// → { success: true, value: { name: 'Alice', age: 30 } }

Consumer directionis(), ensureInterface(), and match() accept external Standard Schemas directly:

import { is, ensureInterface } from '@srhenry/type-utils'
import { z } from 'zod'

const zodUser = z.object({ name: z.string(), age: z.number() })

is({ name: 'Alice', age: 30 }, zodUser)           // true
ensureInterface({ name: 'Alice', age: 30 }, zodUser) // passes

Composition widening — all 6 composition schemas now accept StandardSchemaV1<T,T> alongside TypeGuard<T>:

object({ name: zodString, age: number() })   // mix freely
array(zodString)                              // Standard Schema as element
tuple(zodString, zodNumber)                   // Standard Schema in tuples
or(zodString, zodNumber)                      // union with Standard Schema
and(zodNameSchema, zodAgeSchema)              // intersection
record(string(), zodNumber)                   // record values

Adapters — explicit conversion when you need it:

import { toStandardSchema, fromStandardSchema } from '@srhenry/type-utils/standard-schema'

// TypeGuard → StandardSchemaV1
const std = toStandardSchema(string())

// StandardSchemaV1 → TypeGuard
const guard = fromStandardSchema(zodSchema)

Fluent .toStandardSchema() — available on all schema instances:

const std = string().toStandardSchema()
const stdObj = object({ name: string() }).toStandardSchema()

New files

File Purpose
types.ts Full StandardSchemaV1 namespace inlined from spec — no @standard-schema/spec dependency
isStandardSchema.ts Runtime guard; returns false for functions to prevent infinite recursion
attachStandardSchema.ts Attaches ~standard via Object.defineProperty (non-enumerable)
toStandardSchema.ts TypeGuard<T>StandardSchemaV1<T,T> adapter
fromStandardSchema.ts StandardSchemaV1<Input,Output>TypeGuard<Input> adapter; throws on async
normalizeSchema.ts Composition boundary helper — converts StandardSchemaV1TypeGuard with probe-based optional detection, synthetic CustomStruct metadata, and vendor-prefixed error messages
pathConverter.ts Converts internal ValidationError.path strings ↔ Standard Schema Issue.path format

Design decisions

  • ~standard is non-enumerable — keeps Object.keys(), JSON.stringify(), and spread operators clean on TypeGuards
  • isStandardSchema returns false for functions — prevents infinite recursion when TypeGuards (which are functions with ~standard attached) are passed to widened ensureInterface
  • Single attachment pointattachStandardSchema(guard) called inside setStructMetadata, so all schemas automatically get ~standard
  • Sync-onlyfromStandardSchema(), is(), ensureInterface(), and match() throw TypeError for async schemas, per spec guidance
  • Input === Output — producer always sets Input = Output = T; transform support deferred as future work

📦 ESM Migration & Dual CJS+ESM Output

The package is now fully ESM-native with dual output:

  • "type": "module" in package.json
  • Conditional exports map with types/import/require for all subpaths
  • Separate ESM and CJS build pipelines (tsconfig.esm.json, tsconfig.cjs.json)
  • .ts import extensions across all 264 source files (TypeScript 6.0 allowImportingTsExtensions)
  • Jest configured for ESM module resolution

reflect-metadata removed — replaced with a custom WeakMap<object, Map<string|symbol, unknown>> metadata store and Symbol.for() keys. Eliminates the CJS-only runtime dependency and global Reflect side effects.

No breaking changes for downstream consumers — the conditional exports map ensures import resolves to ESM and require resolves to CJS automatically.


🔧 Other Changes

  • Added asEnum().toStandardSchema() method (was the only schema missing it)
  • setStructMetadata gained CustomStruct overload for external schema normalization
  • ValidatorMap<T> widened to accept StandardSchemaV1<T[K],T[K]> alongside TypeGuard<T[K]>
  • New NormalizedValidatorMap<T> internal type for BaseValidator compatibility
  • TypeGuardTupleUnwrap updated to unwrap StandardSchemaV1<infer U, any> in addition to TypeGuard<infer U>
  • Fixed pre-existing bug: ./schemas./schema in package.json exports map
  • Dev dependency bumps: jest 30.3.0, ts-jest 29.4.9, webpack 5.106.2, webpack-cli 7.0.2, @types/node 25.6.0
  • Security: bumped transient dependencies to mitigate CVE-2026-27903 (ReDoS) and CVE-2026-33672

🧪 Testing

259 tests pass (14 new composition widening + 46 Standard Schema + 199 existing), typecheck clean.


⚠️ Known Limitations

  • Async Standard Schema not supportedfromStandardSchema(), is(), ensureInterface(), and match() throw TypeError for async schemas. Async adapters (fromStandardSchemaAsync, isAsync, etc.) are deferred as future work.
  • Transforms not supported — Standard Schema's Input ≠ Output mode is not yet supported. Our producer always sets Input = Output = T.
  • match().with() type-level widening deferred — runtime works, but TypeScript overloads for .with() are not yet widened to accept StandardSchemaV1<Pattern> in the type system.
  • UMD builds have pre-existing type errors in omit.ts and replaceSchemaTree.ts — separate fix needed.

📋 Migration Notes

  • No breaking API changes. Existing code continues to work unchanged.
  • The reflect-metadata polyfill is no longer loaded at runtime. If downstream code was relying on Reflect.defineMetadata/getMetadata/hasMetadata being available globally (internal only), it will need its own reflect-metadata import.
  • Standard Schema interop is opt-in — you must explicitly access ~standard or use the new adapters.

Full Changelog: v0.6.5...v0.7.0

v0.6.5 (2026-02-05)

05 Feb 08:30
v0.6.5
b97661e

Choose a tag to compare

What's Changed

  • hotfix: 🚑 add custom keyMetadata type (kind: 'string') validation resolver to account for record() call with no params. ab7c3b8

Full Changelog: v0.6.4...v0.6.5

v0.6.4 (2026-02-04)

04 Feb 22:36
v0.6.4
0ea1a79

Choose a tag to compare

What's Changed

  • fix: 🐛 add custom rule type annotation in schemas + fix optional schema propagated type param 3061093

Full Changelog: v0.6.3...v0.6.4

v0.6.3 (2026-02-04)

04 Feb 19:55
v0.6.3
ee10bd3

Choose a tag to compare

What's Changed

  • hotfix: 🚑 .exec method of matcher returning wrong type when expression mapper returns another function, resolving to the inner function return type instead of propagating the function returned from the expression mapper 7eae293

Full Changelog: v0.6.2...v0.6.3