SubAtom subatom-infer
Node.js >= 18 LTS TypeScript >= 5.0 Pure ESM / CJS

Subatom Infer Reference Manual

High-performance runtime validation, strict schema transformation, and compile-time type inference engine powered by the infer namespace.

Architecture
Dual Pipeline

Sync throws on async; Async evaluates non-blocking.

Typing
Schema<Out, In>

Separates input preconditions from validated output.

State
Immutable AST

Zero side-effects, prototype poisoning protection.

Diagnostics
Discriminated AST

Exact JSON paths, key-level issue grouping.

1

Core Execution & Parsing Engine

Validation is invoked directly via synchronous and asynchronous methods attached to every schema instance.

Method Return Signature Behavior & Fail Mode
.parse(input) TOutput Synchronous. Throws ValidationError on invalid input or async pipelines.
.safeParse(input) SafeParseResult<TOutput> Returns { success: true, data } or { success: false, error }.
.parseAsync(input) Promise<TOutput> Asynchronous execution. Awaits all async transformations, refinements, and sub-schemas.
.safeParseAsync(input) Promise<SafeParseResult> Non-throwing Promise resolving to a strongly typed discriminated union. Alias: .spa().
// Importing the unified namespace
import { infer, type Infer } from "subatom-infer";

const User = infer.object({
  id: infer.uuid(),
  username: infer.string().min(3),
});

// Safe synchronous parsing
const result = User.safeParse({ id: "123e4567-e89b-12d3-a456-426614174000", username: "alex" });
if (result.success) {
  console.log(result.data.username); // Typed as string
}
2

Primitives, Unit Types & String Validation API

Core Primitives & Singletons

infer.string()
infer.number()
infer.boolean()
infer.bigint()
infer.date()
infer.symbol()
infer.undefined()
infer.null()
infer.void()
infer.any()
infer.unknown()
infer.never()
infer.nan()
infer.literal("ACTIVE")

String Formatting & Constraints

infer.string()
  .min(5).max(100).length(20)
  .email().url().httpUrl()
  .uuid().guid().cuid().cuid2()
  .ulid().nanoid()
  .regex(/^[a-z]+$/i)
  .startsWith("sub_").endsWith("_node")
  .includes("@")
  .datetime().date().time().duration()
  .ipv4().ipv6().hostname()
  .trim().toLowerCase().toUpperCase()
3

Number & BigInt Validation Constraints

Number Schema Constraints

infer.number()
  .int() // Integer only
  .safe() // Safe IEEE-754 range
  .finite() // Rejects Infinity
  .positive() // > 0
  .nonnegative() // >= 0
  .negative() // < 0
  .nonpositive() // <= 0
  .min(1).max(100)
  .gte(1).lte(100)
  .gt(0).lt(101)
  .multipleOf(5)

BigInt Schema Constraints

infer.bigint()
  .positive() // > 0n
  .nonnegative() // >= 0n
  .negative() // < 0n
  .nonpositive() // <= 0n
  .min(100n)
  .max(1000000n)
  .multipleOf(10n)
4

Objects & Structural Policies

Objects offer manipulation primitives, structural modifiers, key policies, and recursive transformations.

const BaseUser = infer.object({
  id: infer.uuid(),
  name: infer.string(),
  role: infer.enum(["admin", "user"]),
});

// Object Policy Modifiers
const StrictUser = BaseUser.strict(); // Rejects unrecognized keys
const LooseUser = BaseUser.passthrough(); // Retains unknown keys
const StrippedUser = BaseUser.strip(); // Default: strips extra properties
const CatchallUser = BaseUser.catchall(infer.boolean()); // Validates unknown keys

// Structural Composition & Transformations
const ExtendedUser = BaseUser.extend({ email: infer.email() });
const MergedSchema = BaseUser.merge(infer.object({ traceId: infer.string() }));
const PickedName = BaseUser.pick({ name: true });
const OmittedId = BaseUser.omit({ id: true });
const PartialUser = BaseUser.partial(); // All fields optional
const RequiredUser = PartialUser.required(); // All fields required
const DeepOptional = BaseUser.deepPartial(); // Recursively optional
const UserKeysEnum = BaseUser.keyof(); // Returns EnumSchema of keys
5

Collections: Arrays, Tuples, Records, Sets & Maps

Arrays & Fixed Tuples

// Array with length bounds
const Tags = infer.array(infer.string())
  .min(1).max(10).nonempty();

// Tuples with positional schemas
const Coord = infer.tuple([
  infer.number(),
  infer.number(),
  infer.number().optional()
]);

Records, Sets & Maps

// Dynamic Key-Value Map
const Config = infer.record(
  infer.string().min(2),
  infer.number()
);

// Native JS Set & Map instances
const Roles = infer.set(infer.string()).min(1);
const Lookup = infer.map(infer.uuid(), infer.boolean());
6

Combinators, Special Schemas & Recursive Types

Tagged & Untagged Unions

// Discriminated Union (O(1) matching)
const Event = infer.discriminatedUnion("type", [
  infer.object({ type: infer.literal("click"), x: infer.number() }),
  infer.object({ type: infer.literal("hover"), element: infer.string() })
]);

// Union & Intersection
const StrOrNum = infer.union([infer.string(), infer.number()]);
const Combined = infer.intersection(SchemaA, SchemaB);

Function, Promise, File & Lazy

// Function Schema
const AddFn = infer.function(
  infer.tuple([infer.number(), infer.number()]),
  infer.number()
);

// Promise, File & Recursive Lazy
const AsyncStr = infer.promise(infer.string());
const Upload = infer.file().max(5_000_000).mime("image/png");
const Node = infer.lazy(() => infer.object({
  next: Node.optional()
}));
7

Modifiers, Pipelines, Codecs & Refinements

// 1. Modifiers & Defaults
const OptStr = infer.string().optional(); // string | undefined
const NullableNum = infer.number().nullable(); // number | null
const NullishDate = infer.date().nullish(); // Date | null | undefined
const DefaultPort = infer.number().default(3000);
const PrefaultVal = infer.string().prefault("anonymous");
const SafeValue = infer.number().catch(0);

// 2. Transformation Pipeline & Pipe
const StrToDate = infer.string().transform((val) => new Date(val));
const PipedValidation = infer.pipe(infer.string().min(2), infer.string().email());

// 3. Refinements & SuperRefine
const PasswordCheck = infer.object({
  password: infer.string().min(8),
  confirm: infer.string()
}).superRefine((data, ctx) => {
  if (data.password !== data.confirm) {
    ctx.addIssue({ code: "custom", message: "Passwords must match" });
  }
});

// 4. Nominal Branding & Bidirectional Codecs
const UserIdSchema = infer.brand(infer.uuid(), "UserId");
const Base64Codec = infer.codec(
  infer.string().transform((str) => Buffer.from(str, "base64")),
  (buf: Buffer) => buf.toString("base64")
);
8

Coercion Engine & Error Formatting API

Primitive Coercion

infer.coerce.string()
infer.coerce.number().int()
infer.coerce.boolean()
infer.coerce.bigint()
infer.coerce.date()

// Automatically parses strings
infer.coerce.number().parse("42"); // 42 (number)

Error Tree Formatting

try {
  UserSchema.parse(badInput);
} catch (err) {
  // Form & field error record
  err.flatten();
  // Deeply nested issue tree
  err.format();
  // Pretty CLI string format
  console.log(err.prettifyError());
}