Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/composed-valsan.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,25 @@ const strict = new EmailAddressValSan({ minLength: 8, maxLength: 50 });
await standard.run('user@example.com'); // ✅ passes
await strict.run('a@b.c'); // ❌ fails minLength
```

## Rules and Validation Descriptions

`ComposedValSan.rules()` merges rules from its steps. Override `rules()` in a composed validator when you need a different user-facing summary:

```typescript
class EmailAddressValSan extends ComposedValSan<string, string> {
override rules() {
return {
email: {
code: 'email',
user: {
helperText: 'A valid email address',
errorMessage: 'Input must be a valid email address'
}
}
};
}

// Define the composition in the constructor.
}
```
28 changes: 28 additions & 0 deletions docs/using-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,34 @@ You can use options to:

All options are optional and have sensible defaults. See validator docs for details.

## Schema Metadata

Built-in Valsans provide a title and description through `getTitle()` and `getDescription()` for schema documentation and JSON Schema output. Instance options override those defaults:

```typescript
import { MacAddressValSan } from 'valsan';

const macAddress = new MacAddressValSan({
description: 'The MAC address of the Wi-Fi NIC'
});

console.log(macAddress.getTitle()); // "MAC Address"
console.log(macAddress.getDescription()); // "The MAC address of the Wi-Fi NIC"
```

`getValidationDescription()` combines the Valsan description with its rule helper text. Composed Valsans include helper text from each step.

Custom Valsans can provide intrinsic defaults by overriding the `title` and `description` fields:

```typescript
class MyValSan extends ValSan<string, string> {
override title = 'My value';
override description = 'A value with custom validation.';
// Implement the remaining ValSan methods.
}
```


## Optional Values

### isOptional
Expand Down
3 changes: 3 additions & 0 deletions src/primitives/array/array-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export interface ArrayValSanOptions extends ValSanOptions {
*/
export class ArrayValSan extends ValSan<unknown[], unknown[]> {
override type: ValSanTypes = 'array';
override title = 'Array';
override description =
'An array whose items each satisfy the configured schema.';

public get schema(): ArraySchema {
return (this.options as ArrayValSanOptions).schema;
Expand Down
4 changes: 4 additions & 0 deletions src/primitives/auth/bearer-token-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export class BearerTokenValSan extends ValSan<
string
> {
override type: ValSanTypes = 'string';
override title = 'Bearer token';
override description =
'An HTTP authorization value using the Bearer scheme and a ' +
'non-empty token.';
override example = 'Bearer mF_9.B5f-4.1JqM';

override rules() {
Expand Down
4 changes: 4 additions & 0 deletions src/primitives/bool/string-to-boolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export interface StringToBooleanValSanOptions extends ValSanOptions {
*/
export class StringToBooleanValSan extends ValSan<string, boolean> {
override type: ValSanTypes = 'boolean';
override title = 'Boolean string';
override description =
'A string representing true or false, such as "true", "false", "1", ' +
'or "0".';
override example = 'true';

private readonly trueValues: string[];
Expand Down
4 changes: 4 additions & 0 deletions src/primitives/color/hex-color-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const hexColorRegex =

export class HexColorValSan extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Hex color';
override description =
'A hexadecimal color in 3-, 4-, 6-, or 8-digit notation, ' +
'prefixed with "#".';
override format = 'color-hex';
override example = '#FF0000';

Expand Down
3 changes: 3 additions & 0 deletions src/primitives/date-time/iso8601-timestamp-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { parseIsoTimestamp } from './parse-iso-timestamp';
*/
export class Iso8601TimestampValSan extends ValSan<string | Date, Date> {
override type: ValSanTypes = 'string';
override title = 'ISO 8601 timestamp';
override description =
'A valid Date object or ISO 8601 timestamp.';
override format = 'date-time';
override example = '2023-01-01T12:00:00Z';

Expand Down
4 changes: 4 additions & 0 deletions src/primitives/date-time/string-to-date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export class StringToDateValSan extends ValSan<
Date
> {
override type: ValSanTypes = 'string';
override title = 'Date';
override description =
'An ISO calendar date, timestamp, numeric millisecond timestamp, ' +
'or valid Date object.';
override format = 'date';
override example = '2024-01-15';

Expand Down
3 changes: 3 additions & 0 deletions src/primitives/encoding/semver-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const semverRegex =

export class SemverValSan extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Semantic version';
override description =
'A Semantic Versioning 2.0.0 version string.';
override format = 'semver';
override example = '1.2.3';

Expand Down
3 changes: 3 additions & 0 deletions src/primitives/encoding/uuid-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const uuidRegex =

export class UuidValSan extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'UUID';
override description =
'A UUID string in version 1, 3, 4, or 5 format.';
override format = 'uuid';
override example = '550e8400-e29b-41d4-a716-446655440000';

Expand Down
3 changes: 3 additions & 0 deletions src/primitives/json/json-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { stringRule } from '../string/string-rules';

export class JsonValSan extends ValSan<string, unknown> {
override type: ValSanTypes = 'string';
override title = 'JSON';
override description =
'A string containing valid JSON.';
override format = 'json';
override example = '{"key": "value"}';

Expand Down
2 changes: 2 additions & 0 deletions src/primitives/network/fqdn-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { isFqdn } from './is-fqdn';

export class FqdnValSan extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Fully qualified domain name';
override description = 'A fully qualified domain name (FQDN).';
override format = 'hostname';
override example = 'host.example.com';

Expand Down
2 changes: 2 additions & 0 deletions src/primitives/network/ip-address-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ function isIpv6(input: string): boolean {

export class IpAddressValSan extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'IP address';
override description = 'A valid IPv4 or IPv6 address.';
override example = '192.168.0.1';

override rules() {
Expand Down
4 changes: 4 additions & 0 deletions src/primitives/network/mac-address-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ const macRegex =

export class MacAddressValSan extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'MAC Address';
override description =
'A MAC address in colon-separated, hyphen-separated, or dotted ' +
'notation.';
override example = '00:1A:2B:3C:4D:5E';

override rules() {
Expand Down
4 changes: 4 additions & 0 deletions src/primitives/network/port-number-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { normalizeNumber } from '../number/normalize-number';
export class PortNumberValSan extends ValSan<number | string, number> {
// Represent port numbers as integers in OpenAPI
override type: ValSanTypes = 'integer';
override title = 'Port number';
override description =
'An integer from 0 through 65535, supplied as a number or numeric ' +
'string.';
override format = 'int32';
override example = '8080';

Expand Down
2 changes: 2 additions & 0 deletions src/primitives/network/url-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { isString } from '../string/is-string';

export class UrlValSan extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'URL';
override description = 'An absolute URL.';
override format = 'uri';
override example = 'https://example.com';

Expand Down
4 changes: 4 additions & 0 deletions src/primitives/number/decimal-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export class DecimalValidator extends ValSan<
number
> {
override type: ValSanTypes = 'number';
override title = 'Decimal number';
override description =
'A numeric value with decimal places, optionally limited to a ' +
'configured number of decimal places.';
override example = '3.14';

private readonly maxDecimalPlaces?: number;
Expand Down
4 changes: 4 additions & 0 deletions src/primitives/number/integer-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export class IntegerValidator extends ValSan<
number
> {
override type: ValSanTypes = 'integer';
override title = 'Integer';
override description =
'An integer supplied as a number, numeric string, or exactly ' +
'representable bigint.';
override example = '42';

override rules() {
Expand Down
5 changes: 4 additions & 1 deletion src/primitives/number/max-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export class MaxValidator extends ValSan<
number
> {
override type: ValSanTypes = 'number';
override title = 'Maximum value';
override description =
'A number no greater than the configured inclusive maximum.';

private readonly max: number;

Expand All @@ -46,7 +49,7 @@ export class MaxValidator extends ValSan<
max: {
code: 'maximum',
user: {
helperText: `Maximum: ${this.max}`,
helperText: `Maximum value: ${this.max}`,
errorMessage: `Number must be at most ${this.max}`,
},
context: {
Expand Down
5 changes: 4 additions & 1 deletion src/primitives/number/min-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export class MinValidator extends ValSan<
number
> {
override type: ValSanTypes = 'number';
override title = 'Minimum value';
override description =
'A number no less than the configured inclusive minimum.';

private readonly min: number;

Expand All @@ -46,7 +49,7 @@ export class MinValidator extends ValSan<
min: {
code: 'minimum',
user: {
helperText: `Minimum: ${this.min}`,
helperText: `Minimum value: ${this.min}`,
errorMessage: `Number must be at least ${this.min}`,
},
context: {
Expand Down
7 changes: 5 additions & 2 deletions src/primitives/number/range-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export class RangeValidator extends ValSan<
number
> {
override type: ValSanTypes = 'number';
override title = 'Number range';
override description =
'A number within the configured inclusive minimum and maximum.';

private readonly min: number;
private readonly max: number;
Expand All @@ -52,13 +55,13 @@ export class RangeValidator extends ValSan<
range: {
code: 'number_range',
user: {
helperText: 'Range',
helperText: `Value from ${this.min} through ${this.max}.`,
errorMessage:
`Number must be between ${this.min} ` +
`and ${this.max}`,
},
dev: {
helperText: 'Range',
helperText: `Number from ${this.min} through ${this.max}.`,
errorMessage:
`Number must be between ${this.min} and ` +
`${this.max}`,
Expand Down
2 changes: 2 additions & 0 deletions src/primitives/number/string-to-number.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { normalizeNumber } from './normalize-number';
*/
export class StringToNumberValSan extends ValSan<string, number> {
override type: ValSanTypes = 'number';
override title = 'Number';
override description = 'A string representing a valid number.';

override rules() {
return {
Expand Down
3 changes: 3 additions & 0 deletions src/primitives/object/object-valsan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export class ObjectValSan extends ValSan<
Record<string, unknown>
> {
override type: ValSanTypes = 'object';
override title = 'Object';
override description =
'An object whose configured properties each satisfy their schema.';

public get schema(): ObjectSchema {
return (this.options as ObjectValSanOptions).schema;
Expand Down
4 changes: 4 additions & 0 deletions src/primitives/person/email-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export interface EmailValidatorOptions extends ValSanOptions {
*/
export class EmailValidator extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Email address';
override description =
'An email address, optionally restricted to configured domains or ' +
'excluding plus addressing.';
override format = 'email';
override example = 'test@example.com';

Expand Down
3 changes: 3 additions & 0 deletions src/primitives/string/alpha-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export interface AlphaValidatorOptions extends ValSanOptions {
*/
export class AlphaValidator extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Alphabetic string';
override description =
'A string containing only letters, optionally including spaces.';
override example = 'hello';

private readonly allowSpaces: boolean;
Expand Down
3 changes: 3 additions & 0 deletions src/primitives/string/alphanumeric-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import { stringRule } from './string-rules';
*/
export class AlphanumericValidator extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Alphanumeric string';
override description =
'A string containing only letters and numbers.';
override example = 'abc123';

override rules() {
Expand Down
3 changes: 3 additions & 0 deletions src/primitives/string/length-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export interface LengthValidatorOptions
*/
export class LengthValidator extends ComposedValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'String length';
override description =
'A string whose length falls within the configured inclusive range.';

constructor(options: LengthValidatorOptions = {}) {
const steps = [
Expand Down
2 changes: 2 additions & 0 deletions src/primitives/string/lowercase-sanitizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { stringRule } from './string-rules';
*/
export class LowercaseSanitizer extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Lowercase string';
override description = 'A string with all letters in lowercase.';

public override rules() {
return {
Expand Down
5 changes: 4 additions & 1 deletion src/primitives/string/max-length-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export interface MaxLengthValidatorOptions extends ValSanOptions {
*/
export class MaxLengthValidator extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Maximum string length';
override description =
'A string no longer than the configured maximum length.';

private readonly maxLength: number;

Expand All @@ -46,7 +49,7 @@ export class MaxLengthValidator extends ValSan<string, string> {
maxLength: {
code: 'string_max_len',
user: {
helperText: `Max length: ${this.maxLength}`,
helperText: `Maximum length: ${this.maxLength}`,
errorMessage:
`Input must be at most ${this.maxLength} ` +
'character(s)',
Expand Down
5 changes: 4 additions & 1 deletion src/primitives/string/min-length-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export interface MinLengthValidatorOptions extends ValSanOptions {
*/
export class MinLengthValidator extends ValSan<string, string> {
override type: ValSanTypes = 'string';
override title = 'Minimum string length';
override description =
'A string at least as long as the configured minimum length.';
private readonly minLength: number;

constructor(options: MinLengthValidatorOptions = {}) {
Expand All @@ -46,7 +49,7 @@ export class MinLengthValidator extends ValSan<string, string> {
minLength: {
code: 'string_min_len',
user: {
helperText: `Min length: ${this.minLength}`,
helperText: `Minimum length: ${this.minLength}`,
errorMessage:
`Input must be at least ${this.minLength} ` +
'character(s)',
Expand Down
Loading
Loading