npm package: @pfeiferio/checks
Description: Deterministic validation primitives for common formats and constraints
Author: Pascal Pfeifer
Latest version: 1.0.0

# @pfeiferio/checks

> A deterministic validation library for common formats and constraints.

[![npm version](https://badge.fury.io/js/%40pfeiferio%2Fchecks.svg)](https://www.npmjs.com/package/@pfeiferio/checks)
![ESM Only](https://img.shields.io/badge/module-ESM-orange)
[![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![codecov](https://codecov.io/gh/pfeiferio/checks/branch/main/graph/badge.svg)](https://codecov.io/gh/pfeiferio/checks)

All functions either:

- return a validated (and normalized) value, or
- throw a `ValidationError`.

No schema builders. No runtime type systems. Just strict validation primitives.

Ideal for backend services that require strict, predictable validation without introducing a schema DSL.

## Installation

```bash
npm install @pfeiferio/checks
````

## Usage

```ts
import {checkEmail, checkUuidV4, checkUrl} from '@pfeiferio/checks'

const email = checkEmail('user@example.com')     // 'user@example.com'
const uuid = checkUuidV4('550e8400-...')         // normalized lowercase
const url = checkUrl('https://example.com')    // 'https://example.com/'
```

Or use assert functions for TypeScript type narrowing:

```ts
import {assertEmail} from '@pfeiferio/checks'

function process(value: unknown) {
  assertEmail(value)
  // value is now typed as string
  console.log(value.toUpperCase())
}
```

---

## Design Principles

* Deterministic output (normalized values)
* Explicit failure via exceptions
* No implicit coercion
* Zero schema abstraction layer
* TypeScript-first with `asserts`

---

## API

### Format Checks

#### `checkDate(value, templates?): string`

Validates a date string. Returns normalized `YYYY-MM-DD HH:mm:ss`.

Supported templates (default: all three):

- `TEMPLATE_DATE` → `YYYY-MM-DD`
- `TEMPLATE_DATE_TIME_SHORT` → `YYYY-MM-DD hh:mm`
- `TEMPLATE_DATE_TIME` → `YYYY-MM-DD hh:mm:ss`

```ts
checkDate('2024-01-15')            // '2024-01-15 00:00:00'
checkDate('2024-01-15 08:30')      // '2024-01-15 08:30:00'
checkDate('2024-01-15 08:30:45')   // '2024-01-15 08:30:45'
```

---

#### `checkTime(value, templates?): string`

Validates a time string. Returns normalized `hh:mm:ss`.

```ts
checkTime('08:30')      // '08:30:00'
checkTime('08:30:45')   // '08:30:45'
```

---

#### `checkDuration(value, templates?): number`

Validates a duration string. Returns total seconds.

```ts
checkDuration('01:30')      // 5400
checkDuration('01:30:45')   // 5445
checkDuration('-01:00')     // -3600
```

---

#### `checkEmail(value): string`

Validates an ASCII email address (not RFC 6531).

```ts
checkEmail('user@example.com')   // 'user@example.com'
```

---

#### `checkUrl(value): string`

Validates a URL. Only `http` and `https` are allowed.

```ts
checkUrl('https://example.com')   // 'https://example.com/'
```

---

#### `checkUrlRaw(value, allowedProtocols?): string`

Like `checkUrl` but with configurable protocol whitelist. If `allowedProtocols` is omitted, any protocol is accepted.

```ts
checkUrlRaw('ftp://files.example.com', ['ftp'])   // 'ftp://files.example.com/'
checkUrlRaw('ws://example.com')                   // any protocol allowed
```

---

#### `checkUuidV4(value): string`

Validates a UUID v4. Returns normalized lowercase.

```ts
checkUuidV4('550E8400-E29B-41D4-A716-446655440000')
// '550e8400-e29b-41d4-a716-446655440000'
```

---

#### `checkHash(value, length?): string`

Validates a hex string. Returns lowercase.

```ts
checkHash('DEADBEEF')       // 'deadbeef'
checkHash('deadbeef', 8)    // 'deadbeef'
```

Convenience wrappers:

- `checkHashMd5(value)` – expects 32 hex chars
- `checkHashSha1(value)` – expects 40 hex chars
- `checkHashSha256(value)` – expects 64 hex chars

---

#### `checkIpV4(value): string`

Validates an IPv4 address.

```ts
checkIpV4('192.168.1.1')   // '192.168.1.1'
```

---

### Constraint Checks

#### `checkMinValue(value, min): number`

#### `checkMaxValue(value, max): number`

```ts
checkMinValue(10, 5)    // 10
checkMaxValue(3, 5)     // 3
```

---

#### `checkMinLength(value, min): string`

#### `checkMaxLength(value, max): string`

```ts
checkMinLength('hello', 3)   // 'hello'
checkMaxLength('hi', 5)      // 'hi'
```

---

#### `checkMinCount<T>(array, min): T[]`

#### `checkMaxCount<T>(array, max): T[]`

```ts
checkMinCount([1, 2, 3], 2)   // [1, 2, 3]
checkMaxCount([1, 2], 5)      // [1, 2]
```

---

#### `checkInArray<T>(value, pool): T`

Validates that a value is included in the given pool.

```ts
checkInArray('admin', ['admin', 'editor', 'viewer'])   // 'admin'
```

---

#### `checkTimeBetween(value, min, max, templates?, inclusive?): string`

#### `checkTimeGt(value, compareTime, templates?): string`

#### `checkTimeGte(value, compareTime, templates?): string`

#### `checkTimeLt(value, compareTime, templates?): string`

#### `checkTimeLte(value, compareTime, templates?): string`

```ts
checkTimeBetween('10:00', '08:00', '12:00')   // '10:00:00'
checkTimeGt('10:00', '08:00')                 // '10:00:00'
checkTimeLte('08:00', '08:00')                // '08:00:00'
```

---

### Assert Functions

Every check function has a corresponding assert function that returns `void` and uses TypeScript's `asserts` keyword for
type narrowing.

```ts
import {assertUrl, assertMinValue, assertInArray} from '@pfeiferio/checks'

function handle(value: unknown) {
  assertUrl(value)
  // value is string
}

function handleNumber(value: unknown) {
  assertMinValue(value, 0)
  // value is number
}

function handleRole(value: unknown) {
  assertInArray(value, ['admin', 'editor'] as const)
  // value is 'admin' | 'editor'
}
```

Available assert functions:
`assertDate`, `assertTime`, `assertDuration`, `assertEmail`, `assertUrl`, `assertUrlRaw`, `assertUuidV4`, `assertHash`,
`assertHashMd5`, `assertHashSha1`, `assertHashSha256`, `assertIpV4`, `assertMinValue`, `assertMaxValue`,
`assertMinLength`, `assertMaxLength`, `assertMinCount`, `assertMaxCount`, `assertInArray`

---

## Error Handling

All functions throw a `ValidationError` from `@pfeiferio/check-primitives` on invalid input.

```ts
import {ValidationError} from '@pfeiferio/check-primitives'
import {checkEmail} from '@pfeiferio/checks'

try {
  checkEmail('not-valid')
} catch (err) {
  if (err instanceof ValidationError) {
    console.log(err.code)            // 'email.invalidFormat'
    console.log(err.context)         // { ... }
  }
}
```