Catalog npm
Back to packageRELEASE DETAILS

0.7.0

npm

3xp

A typescript library for validating object schemas

Published20 Jan 2025
Direct dependencies19
Transitive dependenciesNot calculated

README

Current package documentation

3xp

A TypeScript library for runtime object validation with compile-time type inference.

Overview

3xp provides a simple, type-safe way to validate JavaScript objects against schemas at runtime while automatically inferring TypeScript types at compile time. Define your schema once, and get both runtime validation and static type checking.

Key Features:

  • Runtime Validation: Validate objects against schemas with clear error messages
  • Type Inference: Automatically derive TypeScript types from your schemas using SchemaType<T>
  • Type Guards: Use isValid for type-safe conditional checks
  • Flexible Validation: Choose strict mode (exact match) or permissive mode (allow extra properties)
  • Schema Definition: Support for primitives, objects, arrays, optional fields, and enum values
  • Lightweight: Extremely minimal dependencies with low runtime overhead

Installation

npm install 3xp

Quick Start

import exp from '3xp';

const obj = {
  foo: 'a',
  boo: 1
};

const schema: exp.Schema = {
  primitive: 'object',
  properties: {
    foo: 'string', // Shorthand for primitive type
    boo: {
      primitive: 'number', // Extended format with options
      optional: true
    }
  }
} as const;

exp.ensure(obj, schema);
// If the object doesn't have that schema, the method `ensure` throws an error.

if(!exp.isValid(obj, schema)){
  console.error('Object is not valid');
}

// Get all validation errors
const result = exp.parse({ name: 123 }, schema);
if (!result.success) {
  console.error(result.errors);
  // [
  //   { path: 'name', message: "Attribute 'name' has an invalid type. Type should be 'string'. Type given 'number'" },
  //   { path: 'boo', message: "Missing required attribute 'boo'" }
  // ]
}

Schema

Example of a schema:

const schema: exp.Schema = {
  primitive: 'object',
  properties: {
    foo: 'string', // Shorthand: primitive type directly
    boo: {
      primitive: 'number', // Extended format with additional options
      optional: true,
    },
    moo: {
      primitive: 'array',
      item: {
        primitive: 'object',
        properties: {
          pippo: 'boolean', // Shorthand
          pluto: 'any' // Shorthand
        }
      }
    },
    poo: {
      primitive: 'string',
      values: ['A', 'B', 'C'], // Enum values create union types
    }
  }
} as const;

The following is the type of the schema:

export type Schema = Primitive | ExpandedSchema;

export type ExpandedSchema = {
  primitive: Primitive;
  item?: Schema;
  values?: Values;
  properties?: Properties;
  optional?: boolean;
  minLength?: number;
  maxLength?: number;
};

export type Properties = {
  [k: string]: Schema;
};

export type ExpandedProperties = {
  [k: string]: ExpandedSchema;
};

export type Values = (string | number)[];

export const PRIMITIVE = {
  ANY: 'any',
  ARRAY: 'array',
  BOOLEAN: 'boolean',
  ENUM: 'enum',
  NULL: 'null',
  NUMBER: 'number',
  OBJECT: 'object',
  STRING: 'string',
  UNDEFINED: 'undefined',
  UNKNOWN: 'unknown',
};

export type Primitive = ObjectValue<typeof PRIMITIVE>;

type ObjectValue<T> = T[keyof T];

Length Validation

You can enforce length constraints on strings and arrays using minLength and maxLength:

const schema: exp.Schema = {
  primitive: 'object',
  properties: {
    username: {
      primitive: 'string',
      minLength: 3,
      maxLength: 20
    },
    tags: {
      primitive: 'array',
      minLength: 1,
      maxLength: 5
    },
    zipCode: {
      primitive: 'string',
      minLength: 5,
      maxLength: 5  // Exact length validation
    }
  }
} as const;

Exact Mode

By default, validation is strict and does not allow additional properties not defined in the schema. You can disable this behavior by setting the exact parameter to false:

import exp from '3xp';

const schema: exp.Schema = {
  primitive: 'object',
  properties: {
    foo: 'string', // Shorthand for primitive type
    boo: 'number'
  }
} as const;

// This will throw an error (extra property 'bar')
const obj1 = { foo: 'a', boo: 1, bar: 'extra' };
exp.ensure(obj1, schema); // Error: No additional attributes are permitted

// This will pass (exact mode disabled)
const obj2 = { foo: 'a', boo: 1, bar: 'extra' };
exp.ensure(obj2, schema, false); // OK - extra properties are ignored

// Works with isValid too
if(exp.isValid(obj2, schema, false)){
  console.log('Valid (with extra properties allowed)');
}

TypeScript Type Inference

You can use SchemaType to infer TypeScript types from your schemas. This provides full type safety at compile time based on your runtime schema definitions.

import exp from '3xp';

// Define your schema with 'as const' to preserve literal types
const schema = {
  primitive: 'object',
  properties: {
    name: 'string',
    age: 'number',
    email: {
      primitive: 'string',
      optional: true
    }
  }
} as const;

// Infer the TypeScript type from the schema
type User = exp.SchemaType<typeof schema>;
// User is: { name: string; age: number; email?: string }

// Now you have full type safety
const user: User = {
  name: 'John',
  age: 30
  // email is optional
};

// TypeScript will catch errors at compile time
const invalidUser: User = {
  name: 'Jane',
  age: 'not a number' // Error: Type 'string' is not assignable to type 'number'
};

Important Notes

  • Use as const: You must use as const assertion on your schema to preserve literal types
  • Works with all schema features: Supports primitives, objects, arrays, optional fields, and enum values
  • Type-safe enums: The values property creates proper union types

Example with enum values:

const statusSchema = {
  primitive: 'string',
  values: ['active', 'inactive', 'pending'] as const
} as const;

type Status = exp.SchemaType<typeof statusSchema>;
// Status is: 'active' | 'inactive' | 'pending'

Unix philosophy

This repo try to follow the Unix philosophy.

Name

3xp stands for experiment, like in Human radiation experiments.

i0n A typescript library for console logging.

r4y A typescript library for managing child processes.

w3i A typescript library for handling configurations.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 19

Native requirements
19 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
i0n
Environment
{
  "local_name": "i0n",
  "alias": false,
  "peer_metadata": null
}
^0.9.0runtime
w3i
Environment
{
  "local_name": "w3i",
  "alias": false,
  "peer_metadata": null
}
^0.3.0runtime
husky
Environment
{
  "local_name": "husky",
  "alias": false,
  "peer_metadata": null
}
^8.0.3development
eslint
Environment
{
  "local_name": "eslint",
  "alias": false,
  "peer_metadata": null
}
^8.39.0development
ts-node
Environment
{
  "local_name": "ts-node",
  "alias": false,
  "peer_metadata": null
}
^10.9.1development
prettier
Environment
{
  "local_name": "prettier",
  "alias": false,
  "peer_metadata": null
}
2.8.7development
tsc-watch
Environment
{
  "local_name": "tsc-watch",
  "alias": false,
  "peer_metadata": null
}
^6.0.0development
typescript
Environment
{
  "local_name": "typescript",
  "alias": false,
  "peer_metadata": null
}
^4.9.4development
@types/node
Environment
{
  "local_name": "@types/node",
  "alias": false,
  "peer_metadata": null
}
^18.11.18development
lint-staged
Environment
{
  "local_name": "lint-staged",
  "alias": false,
  "peer_metadata": null
}
^13.2.1development
eslint-plugin-jest
Environment
{
  "local_name": "eslint-plugin-jest",
  "alias": false,
  "peer_metadata": null
}
^27.2.1development
eslint-plugin-json
Environment
{
  "local_name": "eslint-plugin-json",
  "alias": false,
  "peer_metadata": null
}
^3.1.0development
eslint-plugin-node
Environment
{
  "local_name": "eslint-plugin-node",
  "alias": false,
  "peer_metadata": null
}
^11.1.0development
source-map-support
Environment
{
  "local_name": "source-map-support",
  "alias": false,
  "peer_metadata": null
}
^0.5.21development
eslint-plugin-import
Environment
{
  "local_name": "eslint-plugin-import",
  "alias": false,
  "peer_metadata": null
}
^2.27.5development
eslint-plugin-promise
Environment
{
  "local_name": "eslint-plugin-promise",
  "alias": false,
  "peer_metadata": null
}
^6.1.1development
eslint-config-prettier
Environment
{
  "local_name": "eslint-config-prettier",
  "alias": false,
  "peer_metadata": null
}
^8.8.0development
@typescript-eslint/parser
Environment
{
  "local_name": "@typescript-eslint/parser",
  "alias": false,
  "peer_metadata": null
}
^5.59.1development
@typescript-eslint/eslint-plugin
Environment
{
  "local_name": "@typescript-eslint/eslint-plugin",
  "alias": false,
  "peer_metadata": null
}
^5.59.1development

Distribution files

1 collected
Distribution 1

Source distribution

Source file
Checksums from source
{
  "shasum": "d847dde6be9848f9c6033a32decead76628a4e96",
  "tarball": "https://registry.npmjs.org/3xp/-/3xp-0.7.0.tgz",
  "fileCount": 84,
  "integrity": "sha512-XIOgrpOzXvrVStjYM3YS5tY4RJAVmPJtMWoZxy8fVzKlAIcZy93+zAypyoI/uxttnQUTIMR1hAVzNn7ipeTE6A==",
  "signatures": [
    {
      "sig": "MEUCIHCuhnZViBIWaLa2JSuhMb8O0sdQqdluIxHTvLSVMn3wAiEAhikV0TwSuqCcfUyeOLwOD4EjpFqesRKmtGBVRjKHwMc=",
      "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"
    }
  ],
  "unpackedSize": 55320
}

Repository

PENDING
x71c9/3xp

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 19:55 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:d9b40814ee8cd0a7d27f85b2cb54a0ce65d635ab3aa2dbc94c1a58fc937d8074

Other collected versions

1.4.07 May1.3.116 Mar1.3.010 Mar1.2.42 Mar1.2.32 Mar1.2.224 FebView all collected versions →