npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

egoki

v0.9.1

Published

A composable JavaScript schema library for constructing, validating, and merging structured values.

Readme

Egoki

From Basque (/eˈɣ̞o.ki/ → eh-GOH-kee), meaning “suitable, appropriate, fitting”

A composable JavaScript schema library for constructing, validating, and merging structured values.

Features

  • Compose schemas with an immutable fluent API.
  • Extend compatible schemas recursively without mutating either schema.
  • Inspect schema types and navigate configured schema structures.
  • Validate unrestricted defined values, strings, numbers, booleans, null, functions, regular expressions, class and built-in instances, arrays, plain objects, and explicit unions of schemas.
  • Configure required and optional values, defaults, allowed values, and synchronous refinements.
  • Recursively validate, default, and merge nested arrays and objects.
  • Merge values according to the schema that describes them.
  • Collect multiple validation issues in a single ValidationError.
  • Uses a single dependency (Hermēneíā), also maintained by the same author.

Installation

npm install egoki

Usage

Egoki schemas are built with Schema factory methods and configured with chainable methods. Schema operations never modify the supplied schema or runtime values. Value-producing operations return values that satisfy the schema, or throw instead of returning an invalid value.

Creating schemas

import Schema from 'egoki';

const userSchema = Schema.object({
	name: Schema.string(),
	age: Schema.number().optional(),
	active: Schema.boolean().default(true),
	result: Schema.null().optional(),
	onSave: Schema.function().optional(),
	pattern: Schema.regexp().optional(),
	createdAt: Schema.instance(Date),
	identifier: Schema.union([
		Schema.string(),
		Schema.number(),
	]),
});

Schemas are immutable. Builder methods return a new schema instead of changing the schema they are called on.

const requiredName = Schema.string();
const optionalName = requiredName.optional();

requiredName === optionalName;
// ↳ false

Any schemas

Use Schema.any() when any defined JavaScript value is valid. It uses replacement merging by default; .adaptive() recursively merges plain objects while replacing arrays and every other runtime value.

const options = Schema.object()
	.additionalProperties(
		Schema.any().adaptive(),
	);

See Any Values for validation, adaptive merging, additional properties, and inspection.

Null schemas

Use Schema.null() when null is the only valid defined value. undefined remains the distinct omitted value and is rejected unless the schema is optional or a default supplies null.

const resultSchema = Schema.null().optional();

resultSchema.test(null);
// ↳ true

resultSchema.test(undefined);
// ↳ true

resultSchema.test(false);
// ↳ false

See Null Values for defaults, merging, composition, and nested usage.

Function schemas

Use Schema.function() for JavaScript function values. It accepts any value for which typeof value === 'function', including arrow functions, async functions, generator functions, and classes.

const handlerSchema = Schema.function();
const handler = value => value;

handlerSchema.test(handler);
// ↳ true

A function passed to default() is the default value itself; Egoki does not invoke it as a factory.

See Functions for validation, defaults, merging, and scope.

Regular-expression schemas

Use Schema.regexp() for native JavaScript regular-expression values. Validation uses instanceof RegExp and does not execute the expression or modify lastIndex.

const patternSchema = Schema.regexp();
const pattern = /example/v;

patternSchema.test(pattern);
// ↳ true

Regular expressions use replacement merging, and their identity is preserved through validation, defaults, and merging.

See Regular Expressions for defaults, merging, composition, and scope.

Instance schemas

Use Schema.instance(Constructor) for values accepted by JavaScript's instanceof operator:

const dateSchema = Schema.instance(Date);

dateSchema.test(new Date());
// ↳ true

dateSchema.test('2026-08-26');
// ↳ false

Subclass instances and custom Symbol.hasInstance behavior follow native JavaScript semantics. Egoki does not invoke the constructor or inspect instance properties.

See Instances for constructor requirements, defaults, composition, and error behavior.

Union schemas

Use Schema.union() when a value may satisfy any of several explicit schemas.

const identifierSchema = Schema.union([
	Schema.string(),
	Schema.number(),
]);

identifierSchema.test('user-42');
// ↳ true

identifierSchema.test(42);
// ↳ true

Union alternatives are ordered and use inclusive OR semantics. The union itself owns required/optional behavior and root defaults; nested defaults inside a selected alternative still apply. Failed unions retain each alternative's structured issues without promoting them to unrelated top-level failures.

See Unions for validation, defaulting, merging, and extension behavior.

Inspecting schemas

Every schema exposes a read-only type property, and Schema.isSchema() can optionally require a specific schema type.

const schema = Schema.object({
	user: Schema.object({
		name: Schema.string(),
	}),
});

schema.type;
// ↳ 'object'

Schema.isSchema(schema, 'object');
// ↳ true

Use get() to retrieve an explicitly declared schema at a path. Use getSchemas() when a path may resolve through union alternatives or schema-valued additional properties. Array paths are the precise form; simple dot-separated strings are also supported.

const nameSchema = schema.get(['user', 'name']);
// ↳ StringSchema

const possible = Schema.union([
	Schema.object({
		value: Schema.string(),
	}),
	Schema.object({
		value: Schema.number(),
	}),
]).getSchemas('value');
// ↳ [StringSchema, NumberSchema]

See Schema Inspection for path syntax, arrays, unions, and additional properties.

Validating values

Use validate() when the invalid value itself should produce an exception, or test() when a boolean result is enough.

userSchema.validate({
	name: 'Ada',
	age: 36,
	active: true,
	identifier: 'user-42',
});
// ↳ {name: 'Ada', age: 36, active: true, identifier: 'user-42'}
userSchema.test({
	name: 42,
	active: true,
	identifier: 'user-42',
});
// ↳ false

A failed validation throws ValidationError. It contains every issue discovered during the operation, including the path and invalid value. Issues may also expose optional, structurally frozen details metadata describing the failed condition.

import Schema, {ValidationError} from 'egoki';

try {
	userSchema.validate({
		name: 42,
		active: true,
		identifier: 'user-42',
	});
} catch (error) {
	if (error instanceof ValidationError) {
		console.log(error.issues);
	}
}

Use .refine() for synchronous application-specific constraints after built-in schema validation:

const port = Schema.number().refine(
	value => Number.isInteger(value) && value > 0,
	'Expected a positive integer port',
);

Use .message() to replace schema-wide messages or select a stable validation reason and optional application-defined code:

const count = Schema.number()
	.message('Invalid count')
	.message({
		message: 'The argument `count` requires a number',
		reason: 'type',
	});

See Validation for validation behavior and structured issues.

See Refinements for predicate rules, custom issue metadata, and composition.

See Validation Messages for selectors, precedence, and composition.

Defaults

Use default() to provide a value when the runtime value is undefined. Defaults are applied explicitly with applyDefaults() and do not make a required schema optional during direct validation.

const schema = Schema.object({
	name: Schema.string().default('Anonymous'),
});

schema.applyDefaults({});
// ↳ {name: 'Anonymous'}

Defaults can be applied recursively to nested schemas and additional properties configured with a schema.

See Defaults for configuring and applying default values.

Merging values

merge() combines a target and source according to the schema's merge strategy.

const schema = Schema.object({
	name: Schema.string(),
	settings: Schema.object({
		language: Schema.string(),
	}),
});

schema.merge(
	{
		name: 'Ada',
		settings: {language: 'en'},
	},
	{
		settings: {language: 'pt'},
	},
);
// ↳ {
//    name: 'Ada',
//    settings: {language: 'pt'},
//   }

Arrays use replacement by default, but can be configured to append, prepend, or merge items by a key. Keyed arrays require every item to contain the configured key as a defined own property and require keys to be unique within every validated array and merge operand.

const schema = Schema.array(
	Schema.number(),
).append();

schema.merge([1, 2], [3, 4]);
// ↳ [1, 2, 3, 4]

See Merging for the available strategies and recursive behavior.

Resolving values

resolve() combines defaulting, merging, and validation into one operation. It first applies defaults to the target, merges the source, and validates the resulting value.

const schema = Schema.object({
	name: Schema.string().default('Anonymous'),
});

schema.resolve({}, {});
// ↳ {name: 'Anonymous'}

See Resolution for applying defaults, merging values, and validating the result.

Extending schemas

Use extend() to compose two schemas of the same root type. The result is a new schema and neither input is modified.

Compatible nested schemas are extended recursively. When nested schema types are incompatible, the extension schema replaces the existing nested schema. The root schema type itself must match.

const base = Schema.object({
	options: Schema.object({
		enabled: Schema.boolean(),
	}),
});

const extension = Schema.object({
	options: Schema.object({
		timeout: Schema.number(),
	}),
	variants: Schema.array(Schema.object()),
});

const combined = base.extend(extension);

extend() composes schema definitions only. It does not use runtime merge strategies; merge() remains the operation for combining runtime values.

See Composition for recursive behavior and option precedence.

API

Schema

The default and named Schema exports provide the factory and utility methods used to create schemas. ValidationError and RefinementError are also available as named exports.

| Method | Parameters | Returns | Description | | - | - | - | - | | Schema.any() | — | AnySchema | Creates a schema accepting any defined value. | | Schema.string() | — | StringSchema | Creates a string schema. | | Schema.number() | — | NumberSchema | Creates a number schema. | | Schema.boolean() | — | BooleanSchema | Creates a boolean schema. | | Schema.null() | — | NullSchema | Creates a schema accepting only null. | | Schema.function() | — | FunctionSchema | Creates a function schema. | | Schema.regexp() | — | RegExpSchema | Creates a regular-expression schema. | | Schema.instance(Constructor) | Function | InstanceSchema | Creates a schema using a constructor's instanceof semantics. | | Schema.array(items?) | Schema | ArraySchema | Creates an array schema, optionally with an item schema. | | Schema.object(properties?) | Record<string, Schema> | ObjectSchema | Creates an object schema, optionally with declared property schemas. | | Schema.union(schemas) | Schema[] | UnionSchema | Creates a schema accepting at least one alternative. | | Schema.isSchema(value, type?) | unknown, string | boolean | Determines whether a value is a schema instance, optionally of a specific type. |

Common schema properties and methods

All schemas expose the following public API from Schema.

| Property | Type | Description | | - | - | - | | .type | string | The schema's concrete public type. |

| Method | Parameters | Returns | Description | | - | - | - | - | | .required(required?) | boolean | this | Configures whether a value is required. | | .optional() | — | this | Makes a value optional. | | .message(options) | object \| string | this | Overrides validation messages reported by the schema. | | .refine(predicate, options?) | (value) => boolean, object \| string | this | Appends synchronous application-specific validation. | | .default(value) | unknown | this | Configures a default value when none is defined. | | .clone() | — | this | Creates a distinct schema with equivalent behavior. | | .replace() | — | this | Replaces source values with target values during merging. | | .extend(schema) | Schema | this | Extends a schema of the same root type without mutating either schema. | | .get(path?) | string \| Array<string \| number> | Schema \| undefined | Returns the explicitly configured schema at a path. | | .getSchemas(path?) | string \| Array<string \| number> | Schema[] | Returns configured schemas that may apply at a path. | | .validate(value) | unknown | unknown | Validates a value and throws on failure. | | .test(value) | unknown | boolean | Tests whether a value satisfies the schema. | | .applyDefaults(value) | unknown | unknown | Applies configured defaults and returns a value satisfying the schema. | | .merge(target, ...sources) | unknown, ...unknown[] | unknown | Merges sources from left to right and validates only the final result. | | .resolve(target, ...sources) | unknown, ...unknown[] | unknown | Resolves sources transactionally with defaults before and after merging, then validates once. |

Merge strategy builders are available only on the schemas that support the corresponding strategy.

AnySchema

AnySchema accepts every defined JavaScript value. It uses replacement merging by default and exposes .adaptive() for runtime-aware recursive plain-object merging.

| Method | Parameters | Returns | Description | | - | - | - | - | | .adaptive() | — | this | Recursively merges plain objects and replaces all other defined values. |

See Any Values for details.

StringSchema, NumberSchema, and BooleanSchema

These schemas validate JavaScript strings, numbers, and booleans respectively.

| Method | Parameters | Returns | Description | | - | - | - | - | | .enum(values) | unknown[] | this | Restricts string, number, or boolean runtime values to an allowed set. |

enum() is available only on string, number, and boolean schemas. Any, null, function, regular-expression, instance, array, object, and union schemas do not expose it.

Non-empty values

Strings and arrays expose .nonEmpty() (or .nonEmpty(true)) to reject zero-length values. .nonEmpty(false) disables only this constraint; length settings remain independent. Optional schemas still accept omitted values.

The check does not trim strings and counts array holes towards length. Failures use the dedicated non-empty reason. See Non-Empty Values.

Length builders

Strings and arrays expose .minLength(limit) and .maxLength(limit) to configure inclusive bounds. .length(limit) sets both bounds to the same value; .length(minimum, maximum) sets both explicitly. Each supplied limit accepts a non-negative safe integer or false to clear its bound. .length(false) clears both.

Schema.string().minLength(3).maxLength(20);
Schema.array(Schema.number()).length(2);
Schema.string().length(3).maxLength(6); // Lengths 3–6.
Schema.array().length(5, false); // At least 5 items, no maximum.

Checks use JavaScript .length, including UTF-16 code units for strings and holes for arrays. See Length Constraints for metadata, defaults, and composition.

String builders

StringSchema exposes .trimmed(), .trimmedStart(), and .trimmedEnd() to require strings that already satisfy JavaScript's corresponding trimming check. They validate without changing values. Directional builders set one end; .trimmed() sets both. Omitted arguments or true enable the selected checks; false disables them. Explicit undefined is invalid.

Schema.string().trimmed().nonEmpty();
Schema.string().trimmedStart(); // Trailing whitespace remains allowed.
Schema.string().trimmedEnd(); // Leading whitespace remains allowed.

Empty strings and internal whitespace pass trimming checks. Each failure produces one issue: trimmed-start, trimmed-end, or trimmed, based on the failing enabled checks. Its details.trimmed describes both effective flags. See String Constraints for messages, defaults, and composition.

StringSchema supports .pattern(regexp) as a first-class constraint before application refinements. Each call replaces the previous pattern; .pattern(false) clears it.

Schema.string().pattern(/^[a-z]+$/iv);

Patterns use native matching without implicit anchoring. Egoki snapshots source and flags and starts each check at index zero without modifying the supplied expression. See String Constraints for stateful patterns, issue details, and composition.

Numeric builders

NumberSchema additionally exposes these first-class constraints. They run before application refinements, without coercing runtime values.

| Method | Parameters | Returns | Description | | - | - | - | - | | .min(value, options?) | number \| false, {exclusive?: boolean} | this | Sets or clears the lower bound. | | .max(value, options?) | number \| false, {exclusive?: boolean} | this | Sets or clears the upper bound. | | .range(minimum, maximum, options?) | number \| false endpoints, {exclusiveMinimum?: boolean, exclusiveMaximum?: boolean} | this | Appends an accepted interval. | | .ranges(ranges) | Descriptor array or false | this | Replaces all intervals; [] or false clears them. | | .finite(isFinite?) | boolean | this | Requires a finite number unless explicitly disabled. | | .integer(isInteger?) | boolean | this | Requires an integer unless explicitly disabled. | | .safeInteger(isSafeInteger?) | boolean | this | Requires a safe integer unless explicitly disabled. |

Bounds are inclusive by default. Pass {exclusive: true} to exclude the boundary or false to clear it. Numeric flags are enabled by no-argument calls and disabled with false. See Numeric Constraints for infinity handling, issue details, and composition.

Ranges accept values in any configured interval while preserving independent bounds and flags. Replacement descriptors use {minimum, maximum, exclusiveMinimum?, exclusiveMaximum?}. During extension, an explicitly configured range collection replaces the inherited collection in full.

NullSchema

NullSchema accepts only null as a defined runtime value. It extends the common Schema API directly, uses replacement merging, and does not expose enum().

See Null Values for details.

FunctionSchema

FunctionSchema validates JavaScript function values. It extends the common Schema API directly, uses replacement merging, and does not expose enum() or callable-contract builders.

See Functions for details.

RegExpSchema

RegExpSchema validates native JavaScript regular-expression values using instanceof RegExp. It extends the common Schema API directly, uses replacement merging, and does not execute expressions or expose enum().

See Regular Expressions for details.

InstanceSchema

InstanceSchema validates with a configured constructor's native instanceof semantics. It uses replacement merging, preserves instance identity, and does not inspect instance properties or expose enum().

See Instances for details.

UnionSchema

UnionSchema accepts a runtime value when at least one configured alternative schema accepts it. It uses replacement merging and can be nested anywhere another schema is accepted.

See Unions for alternative ordering, defaults, validation errors, and extension behavior.

ArraySchema

ArraySchema validates arrays and can optionally validate each item with another schema.

| Method | Parameters | Returns | Description | | - | - | - | - | | .items(schema) | Schema | this | Configures the item schema. | | .append() | — | this | Appends source items after target items during merging. | | .prepend() | — | this | Prepends source items before target items during merging. | | .keyedBy(key) | string | this | Matches array items by a defined, unique own property and enforces that identity during validation. |

ObjectSchema

ObjectSchema validates plain objects and can define schemas for declared and additional properties.

| Method | Parameters | Returns | Description | | - | - | - | - | | .properties(properties) | Record<string, Schema> | this | Configures declared property schemas. | | .additionalProperties(schema) | Schema \| boolean | this | Configures how undeclared properties are handled. | | .reserve(properties) | string \| string[] | this | Protects exact property definitions and policies from later configuration and extension. | | .forbid(properties, options?) | string \| string[] \| RegExp, string \| object | this | Rejects matching enumerable string properties. | | .disallow(properties, options?) | string \| string[] \| RegExp, string \| object | this | Alias for .forbid(). | | .allow(properties) | string \| string[] \| RegExp | this | Reverses earlier matching property prohibitions. | | .deep() | — | this | Deep merges source values with target values during merging. |

ValidationError

ValidationError extends TypeError and is thrown when validation fails.

| Property | Type | Description | | - | - | - | | summary | string | Concise validation-result summary. | | message | string | Summary followed by formatted issue details. | | issues | object[] | All validation issues, in discovery order. | | issue | object | The first validation issue. |

See Validation for the structure of validation issues.

RefinementError

RefinementError extends TypeError and is thrown when a refinement predicate returns a value other than a primitive boolean. Exceptions thrown by predicate code propagate unchanged rather than being wrapped.

See Refinements for the predicate contract and error boundaries.

Documentation

  • Any Values — unrestricted defined values and adaptive merging.
  • Null Values — validating the exact null value and distinguishing omission.
  • Functions — function validation, defaults, merging, and scope.
  • Regular Expressions — native regular-expression validation, defaults, and merging.
  • Instances — constructor-based validation, identity, and composition.
  • Validation — validation behavior, errors, and structured issues.
  • String Constraints — regular-expression patterns, matching state, and composition.
  • Length Constraints — minimum, maximum, and exact lengths for strings and arrays.
  • Non-Empty Values — independent non-empty validation for strings and arrays.
  • Numeric Constraints — numeric bounds, ranges, finite numbers, integers, and safe integers.
  • Validation Messages — overriding issue messages by schema, reason, and application code.
  • Object Property Policies — forbidding and re-allowing reserved object properties.
  • Reserved Schema Properties — protecting object schema definitions and policies during composition.
  • Refinements — synchronous custom validation, issue paths, and predicate behavior.
  • Defaults — configuring and applying default values.
  • Merging — merge strategies and recursive merging.
  • Resolution — applying defaults, merging values, and validating the result as one operation.
  • Composition — composing and extending schemas.
  • Unions — accepting values through one or more alternative schemas.
  • Inspection — inspecting schema types and retrieving schemas by path.