@janus-validator/dsl
v0.6.0
Published
Concise DSL for Janus Validator - short aliases for all combinators
Downloads
395
Maintainers
Readme
@janus-validator/dsl
Concise DSL for Janus Validator - short aliases for all validators and combinators.
The DSL is “just syntax”: it builds validators from @janus-validator/core, so you still get:
- ✅ Forward validation:
validate(unknown) - 🎲 Backwards generation:
new Generator(rng).generate(validator)
Installation
npm install @janus-validator/dsl @janus-validator/coreQuick Start
import { B, U, S, I, N, L, R, O, Bytes, Or, Seq, optional, oneOrMore } from '@janus-validator/dsl';
import { Generator } from '@janus-validator/core';
// Define validators with concise syntax
const userValidator = O({
name: U(1, 100),
age: I(0, 150),
email: R(/^[\w.]+@[\w.]+\.\w+$/),
active: B(),
role: Or('admin', 'user', 'guest'), // Primitives auto-wrapped
version: 1, // Also auto-wrapped
});
// Validate data
const result = userValidator.validate({
name: 'Alice',
age: 30,
email: '[email protected]',
active: true,
role: 'admin',
version: 1,
});
// Generate test data
const generator = new Generator({ random: Math.random });
const testUser = generator.generate(userValidator);Why this is powerful
The same validator definition can be used:
- In production (validate API requests / config / events)
- In tests (generate fixtures that must satisfy the same constraints)
import { Generator } from '@janus-validator/core';
import { O, U, I } from '@janus-validator/dsl';
const User = O({ name: U(1, 50), age: I(0, 150) });
const generator = new Generator({ random: Math.random });
const fixture = generator.generate(User);
const roundTrip = User.validate(fixture);
// roundTrip.valid === trueDSL Reference
Primitive Validators
| Function | Description | Example |
|----------|-------------|---------|
| B() | Boolean | B() |
| U(min?, max?) | Unicode string with length range | U(1, 100) |
| S(...parts) | Compound string from parts | S(digits(4), '-', digits(2)) |
| I(min?, max?) | Integer in range | I(0, 150) |
| N(min?, max?) | Float in range | N(-273.15, 1000) |
| L(min?, max?) | 64-bit BigInt | L(0n, 1000n) |
| R(pattern) | Regex pattern | R(/^\d{3}-\d{4}$/) |
| Bytes(min?, max?) | Binary data (Uint8Array) | Bytes(16, 32) |
Object & Special Values
| Function | Description | Example |
|----------|-------------|---------|
| O(schema, strict?) | Object/struct validator | O({ name: U(), age: I() }) |
| C(value) | Constant value | C(42), C('hello') |
| Null() | Matches null | Null() |
| Undefined() | Matches undefined | Undefined() |
| Inf() | Positive infinity | Inf() |
| NInf() | Negative infinity | NInf() |
| NaN() | Not-a-Number | NaN() |
| Enum(enumObj) | TypeScript enum | Enum(Status) |
Combinators
| Function | Description | Example |
|----------|-------------|---------|
| Or(...validators) | Union/alternation | Or(U(), I(), Null()) |
| Seq(...validators) | Tuple/sequence | Seq(U(), I(), B()) |
Character Sets (for S())
| Function | Short | Description |
|----------|-------|-------------|
| digits(n) | D(n) | Digits (0-9) |
| lower(n) | | Lowercase (a-z) |
| upper(n) | | Uppercase (A-Z) |
| letters(n) | | Letters (a-zA-Z) |
| alphanumeric(n) | A(n) | Alphanumeric |
| hex(n) | H(n) | Hex (0-9a-f) |
| hexUpper(n) | | Hex (0-9A-F) |
| chars(set, n) | | Custom char set |
Quantifiers
| Function | Description | Example |
|----------|-------------|---------|
| zeroOrMore(v) | 0+ elements | zeroOrMore(I()) |
| oneOrMore(v) | 1+ elements | oneOrMore(U()) |
| optional(v) | 0 or 1 element | optional(U()) |
| exactly(v, n) | Exactly n elements | exactly(I(), 3) |
| atLeast(v, n) | n+ elements | atLeast(U(), 2) |
| between(v, min, max) | min-max elements | between(I(), 1, 5) |
Type Assertion
interface User { name: string; age: number; }
const userValidator = Typed<User>()(O({
name: U(1, 100),
age: I(0, 150),
}));
// Or use As<User>() as an aliasCapture & Reference
const { capture, ref, context } = createCaptureGroup();
const form = O({
password: capture('pwd', U(8, 100)),
confirmPassword: ref('pwd'),
});String Modifiers
// Case-insensitive matching
const hexColor = caseInsensitive(S('#', hex(6)));
hexColor.validate('#AABBCC'); // valid
hexColor.validate('#aabbcc'); // validRecipes
1) Nested objects (strict vs non-strict)
import { O, U, I } from '@janus-validator/dsl';
const User = O({
name: U(1, 100),
age: I(0, 150),
});
const StrictUser = O({ name: U(1, 100), age: I(0, 150) }, true);2) “Enum-like” values (auto-wrapping)
import { Or } from '@janus-validator/dsl';
const Status = Or('pending', 'active', 'complete');
// Type is: Validator<'pending' | 'active' | 'complete'>3) Formatted strings without regex
import { S, D, H } from '@janus-validator/dsl';
const ISODate = S(D(4), '-', D(2), '-', D(2)); // YYYY-MM-DD
const UUID = S(H(8), '-', H(4), '-', H(4), '-', H(4), '-', H(12));4) Capture & reference (e.g. password confirmation)
import { O, U, createCaptureGroup } from '@janus-validator/dsl';
const { capture, ref, context } = createCaptureGroup();
const Signup = O({
password: capture('pwd', U(8, 100)),
confirmPassword: ref('pwd'),
});
// If reusing between validations, clear captures
context.clear();Auto-Wrapping
The Or, Seq, and O combinators automatically wrap primitive values and enums:
// Primitives auto-wrapped in Constant
const protocol = Or('http', 'https', 'ws'); // No need for C()
const config = O({ version: 1, env: 'prod' });
// Enums auto-wrapped
enum Status { Active = 'active', Inactive = 'inactive' }
const task = O({
status: Status, // Automatically creates Or('active', 'inactive')
});License
MIT
