inu-light 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # inu-light
2
+
3
+ A lightweight, zero-dependency schema validation library for TypeScript and JavaScript.
4
+
5
+ ## Design philosophy
6
+
7
+ inu-light focuses on predictable schemas and strict typing, avoiding magic behavior and unnecessary runtime dependencies.
8
+
9
+ ## Features
10
+
11
+ - **Zero Dependencies**: Minimum footprint for your project.
12
+ - **Type Safe**: Automatically infers TypeScript types from your schemas without using explicit any.
13
+ - **ESM Native**: Built for modern environments.
14
+ - **Lightweight**: Focused on core validation primitives.
15
+
16
+ ### Complex Types & Modifiers
17
+
18
+ - `inu.object(shape)`: Validates an object based on the provided shape.
19
+ - `inu.array(schema)`: Validates an array of elements matching the schema.
20
+ - `inu.union([schema1, schema2])`: Validates that the input matches at least one of the provided schemas.
21
+ - `inu.literal(value)`: Validates an exact value (string, number, or boolean).
22
+ - `.optional()`: Modifier to allow `undefined`.
23
+ - `.nullable()`: Modifier to allow `null`.
24
+
25
+ ## Installation
26
+
27
+ ```
28
+ npm install inu-light
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Basic Example
34
+
35
+ Define a schema and validate data against it. The `parse` method returns a result object containing either the validated data or an error message.
36
+
37
+ ```typescript
38
+ import { inu } from 'inu-light';
39
+
40
+ const userSchema = inu.object({
41
+ name: inu.string(),
42
+ age: inu.number(),
43
+ isActive: inu.boolean(),
44
+ });
45
+
46
+ const result = userSchema.parse({
47
+ name: 'John Doe',
48
+ age: 30,
49
+ isActive: true,
50
+ });
51
+
52
+ if (result.success) {
53
+ console.log(result.value); // Type-safe data
54
+ } else {
55
+ console.error(result.error);
56
+ }
57
+ ```
58
+
59
+ ### Type Inference
60
+
61
+ You can extract the TypeScript type directly from a schema using `ShapeToType`.
62
+
63
+ ```typescript
64
+ import { inu, ShapeToType } from 'inu-light';
65
+
66
+ const schema = inu.object({
67
+ id: inu.number(),
68
+ });
69
+
70
+ type User = ShapeToType<typeof schema>;
71
+ ```
72
+
73
+ ## API Reference
74
+
75
+ ### Primitives
76
+
77
+ - `inu.string()`: Validates that the input is a string.
78
+ - `inu.number()`: Validates that the input is a number.
79
+ - `inu.boolean()`: Validates that the input is a boolean.
80
+
81
+ ### Complex Types
82
+
83
+ - `inu.object(shape)`: Validates an object based on the provided shape.
84
+
85
+ ## Development
86
+
87
+ ### Build
88
+
89
+ To compile the library from source:
90
+
91
+ ```
92
+ npm run build
93
+ ```
94
+
95
+ ### Testing
96
+
97
+ To run local tests during development:
98
+
99
+ ```
100
+ npx tsx test-local.ts
101
+ ```
102
+
103
+ ## License
104
+
105
+ MIT
@@ -0,0 +1,7 @@
1
+ import { Schema, ParseResult } from '../types/schema.js';
2
+ export declare class InuArray<T> extends Schema<T[]> {
3
+ private itemSchema;
4
+ constructor(itemSchema: Schema<T>);
5
+ parse(value: unknown): ParseResult<T[]>;
6
+ }
7
+ export declare function array<T>(input: Schema<T> | [Schema<T>]): Schema<T[]>;
@@ -0,0 +1,36 @@
1
+ import { Schema } from '../types/schema.js';
2
+ import { isArray } from '../utils/guards.js';
3
+ export class InuArray extends Schema {
4
+ constructor(itemSchema) {
5
+ super();
6
+ this.itemSchema = itemSchema;
7
+ }
8
+ parse(value) {
9
+ if (!isArray(value)) {
10
+ return { success: false, error: 'Array Expected' };
11
+ }
12
+ const validatedArray = [];
13
+ for (let i = 0; i < value.length; i++) {
14
+ const result = this.itemSchema.parse(value[i]);
15
+ if (result.success === false) {
16
+ return {
17
+ success: false,
18
+ error: `Invalid item at index ${i}: ${result.error}`,
19
+ };
20
+ }
21
+ validatedArray.push(result.value);
22
+ }
23
+ return { success: true, value: validatedArray };
24
+ }
25
+ }
26
+ export function array(input) {
27
+ if (Array.isArray(input)) {
28
+ if (input.length !== 1) {
29
+ throw new Error(`[InuError] inu.array([...]) must contain exactly one schema.
30
+ - For multiple types in any order, use inu.union().
31
+ - For a fixed sequence, use inu.tuple().`);
32
+ }
33
+ return new InuArray(input[0]);
34
+ }
35
+ return new InuArray(input);
36
+ }
@@ -0,0 +1,6 @@
1
+ import { Schema, ParseResult } from '../types/schema.js';
2
+ export declare class InuBoolean extends Schema<boolean> {
3
+ constructor();
4
+ parse(value: unknown): ParseResult<boolean>;
5
+ }
6
+ export declare function boolean(): Schema<boolean>;
@@ -0,0 +1,24 @@
1
+ import { Schema } from '../types/schema.js';
2
+ import { isBoolean } from '../utils/guards.js';
3
+ export class InuBoolean extends Schema {
4
+ constructor() {
5
+ super();
6
+ }
7
+ parse(value) {
8
+ if (isBoolean(value)) {
9
+ return {
10
+ success: true,
11
+ value: value,
12
+ };
13
+ }
14
+ else {
15
+ return {
16
+ success: false,
17
+ error: 'Boolean Expected',
18
+ };
19
+ }
20
+ }
21
+ }
22
+ export function boolean() {
23
+ return new InuBoolean();
24
+ }
@@ -0,0 +1,7 @@
1
+ import { Schema, ParseResult } from '../types/schema.js';
2
+ export declare class InuLiteral<T> extends Schema<T> {
3
+ private readonly expectedValue;
4
+ constructor(expectedValue: T);
5
+ parse(value: unknown): ParseResult<T>;
6
+ }
7
+ export declare function literal<T>(value: T): Schema<T>;
@@ -0,0 +1,22 @@
1
+ import { Schema } from '../types/schema.js';
2
+ export class InuLiteral extends Schema {
3
+ constructor(expectedValue) {
4
+ super();
5
+ this.expectedValue = expectedValue;
6
+ }
7
+ parse(value) {
8
+ if (value === this.expectedValue) {
9
+ return {
10
+ success: true,
11
+ value: value,
12
+ };
13
+ }
14
+ return {
15
+ success: false,
16
+ error: `Expected literal ${JSON.stringify(this.expectedValue)}, but got ${JSON.stringify(value)}`,
17
+ };
18
+ }
19
+ }
20
+ export function literal(value) {
21
+ return new InuLiteral(value);
22
+ }
@@ -0,0 +1,5 @@
1
+ import { Schema, ParseResult } from '../types/schema.js';
2
+ export declare class InuNumber extends Schema<number> {
3
+ parse(value: unknown): ParseResult<number>;
4
+ }
5
+ export declare function number(): Schema<number>;
@@ -0,0 +1,18 @@
1
+ import { Schema } from '../types/schema.js';
2
+ export class InuNumber extends Schema {
3
+ parse(value) {
4
+ if (typeof value === 'number' && !isNaN(value)) {
5
+ return {
6
+ success: true,
7
+ value,
8
+ };
9
+ }
10
+ return {
11
+ success: false,
12
+ error: 'Number Expected',
13
+ };
14
+ }
15
+ }
16
+ export function number() {
17
+ return new InuNumber();
18
+ }
@@ -0,0 +1,14 @@
1
+ import { Schema, ParseResult, NestedShape } from '../types/schema.js';
2
+ type IsOptional<T> = undefined extends T ? true : false;
3
+ export type ShapeToType<S> = S extends Schema<infer T> ? T : {
4
+ [K in keyof S as IsOptional<S[K] extends Schema<infer T> ? T : never> extends true ? never : K]: S[K] extends Schema<infer T> ? T : S[K] extends Record<string, unknown> ? ShapeToType<S[K]> : never;
5
+ } & {
6
+ [K in keyof S as IsOptional<S[K] extends Schema<infer T> ? T : never> extends true ? K : never]?: S[K] extends Schema<infer T> ? T : S[K] extends Record<string, unknown> ? ShapeToType<S[K]> : never;
7
+ };
8
+ export declare class InuObject<S extends NestedShape> extends Schema<ShapeToType<S>> {
9
+ private shape;
10
+ constructor(shape: S);
11
+ parse(value: unknown): ParseResult<ShapeToType<S>>;
12
+ }
13
+ export declare function object<S extends NestedShape>(shape: S): Schema<ShapeToType<S>>;
14
+ export {};
@@ -0,0 +1,54 @@
1
+ import { Schema } from '../types/schema.js';
2
+ import { isRecord, isSchema } from '../utils/guards.js';
3
+ export class InuObject extends Schema {
4
+ constructor(shape) {
5
+ super();
6
+ this.shape = shape;
7
+ }
8
+ parse(value) {
9
+ if (!isRecord(value)) {
10
+ return { success: false, error: 'Object Expected' };
11
+ }
12
+ const parsedFields = [];
13
+ for (const key in this.shape) {
14
+ const field = this.shape[key];
15
+ if (isSchema(field)) {
16
+ const parsed = field.parse(value[key]);
17
+ if (!parsed.success) {
18
+ parsedFields.push({ key, success: false, error: parsed.error });
19
+ continue;
20
+ }
21
+ parsedFields.push({ key, success: true, value: parsed.value });
22
+ }
23
+ else {
24
+ const nestedResult = new InuObject(field).parse(value[key]);
25
+ if (!nestedResult.success) {
26
+ parsedFields.push({
27
+ key,
28
+ success: false,
29
+ error: nestedResult.error,
30
+ });
31
+ continue;
32
+ }
33
+ parsedFields.push({ key, success: true, value: nestedResult.value });
34
+ }
35
+ }
36
+ const failed = parsedFields.find((f) => f.success === false);
37
+ if (failed && !failed?.success) {
38
+ return {
39
+ success: false,
40
+ error: `Invalid field "${failed.key}": ${failed.error}`,
41
+ };
42
+ }
43
+ const finalObject = parsedFields.reduce((acc, field) => {
44
+ if (field.success) {
45
+ acc[field.key] = field.value;
46
+ }
47
+ return acc;
48
+ }, {});
49
+ return { success: true, value: finalObject };
50
+ }
51
+ }
52
+ export function object(shape) {
53
+ return new InuObject(shape);
54
+ }
@@ -0,0 +1,6 @@
1
+ import { Schema, ParseResult } from '../types/schema.js';
2
+ export declare class InuString extends Schema<string> {
3
+ constructor();
4
+ parse(value: unknown): ParseResult<string>;
5
+ }
6
+ export declare const string: () => Schema<string>;
@@ -0,0 +1,16 @@
1
+ import { Schema } from '../types/schema.js';
2
+ import { isString } from '../utils/guards.js';
3
+ export class InuString extends Schema {
4
+ constructor() {
5
+ super();
6
+ }
7
+ parse(value) {
8
+ if (isString(value)) {
9
+ return { success: true, value };
10
+ }
11
+ return { success: false, error: 'String Expected' };
12
+ }
13
+ }
14
+ export const string = () => {
15
+ return new InuString();
16
+ };
@@ -0,0 +1,11 @@
1
+ import { Schema, ParseResult } from '../types/schema.js';
2
+ export declare class InuTuple<T extends unknown[]> extends Schema<T> {
3
+ private schemas;
4
+ constructor(schemas: {
5
+ [K in keyof T]: Schema<T[K]>;
6
+ });
7
+ parse(value: unknown): ParseResult<T>;
8
+ }
9
+ export declare function tuple<T extends unknown[]>(schemas: {
10
+ [K in keyof T]: Schema<T[K]>;
11
+ }): Schema<T>;
@@ -0,0 +1,37 @@
1
+ import { Schema } from '../types/schema.js';
2
+ import { isArray } from '../utils/guards.js';
3
+ export class InuTuple extends Schema {
4
+ constructor(schemas) {
5
+ super();
6
+ this.schemas = schemas;
7
+ }
8
+ parse(value) {
9
+ if (!isArray(value)) {
10
+ return {
11
+ success: false,
12
+ error: 'Expected an array for tuple validation',
13
+ };
14
+ }
15
+ if (value.length !== this.schemas.length) {
16
+ return {
17
+ success: false,
18
+ error: `Tuple length mismatch: expected ${this.schemas.length} items, but got ${value.length}`,
19
+ };
20
+ }
21
+ const validatedTuple = [];
22
+ for (let i = 0; i < this.schemas.length; i++) {
23
+ const result = this.schemas[i].parse(value[i]);
24
+ if (!result.success) {
25
+ return {
26
+ success: false,
27
+ error: `Invalid tuple item at index ${i}: ${result.error}`,
28
+ };
29
+ }
30
+ validatedTuple[i] = result.value;
31
+ }
32
+ return { success: true, value: validatedTuple };
33
+ }
34
+ }
35
+ export function tuple(schemas) {
36
+ return new InuTuple(schemas);
37
+ }
@@ -0,0 +1,9 @@
1
+ import { Schema, ParseResult } from '../types/schema.js';
2
+ export declare class InuUnion<T> extends Schema<T> {
3
+ private schemas;
4
+ constructor(schemas: Schema<T>[]);
5
+ parse(value: unknown): ParseResult<T>;
6
+ }
7
+ export declare function union<T extends unknown[]>(schemas: [...{
8
+ [K in keyof T]: Schema<T[K]>;
9
+ }]): Schema<T[number]>;
@@ -0,0 +1,25 @@
1
+ import { Schema } from '../types/schema.js';
2
+ export class InuUnion extends Schema {
3
+ constructor(schemas) {
4
+ super();
5
+ this.schemas = schemas;
6
+ }
7
+ parse(value) {
8
+ const errors = [];
9
+ for (const schema of this.schemas) {
10
+ const result = schema.parse(value);
11
+ if (result.success) {
12
+ return result;
13
+ }
14
+ errors.push(result.error || 'Invalid input');
15
+ }
16
+ return {
17
+ success: false,
18
+ error: `Union error: Data does not match any of the allowed types.
19
+ Sub-errors: [${errors.join(' | ')}]`,
20
+ };
21
+ }
22
+ }
23
+ export function union(schemas) {
24
+ return new InuUnion(schemas);
25
+ }
@@ -0,0 +1,19 @@
1
+ import { number } from './core/number.js';
2
+ import { boolean } from './core/boolean.js';
3
+ import { object } from './core/object.js';
4
+ import { array } from './core/array.js';
5
+ import { tuple } from './core/tuple.js';
6
+ import { union } from './core/union.js';
7
+ import { literal } from './core/literal.js';
8
+ export declare const inu: {
9
+ string: () => import("./types/schema.js").Schema<string>;
10
+ number: typeof number;
11
+ boolean: typeof boolean;
12
+ object: typeof object;
13
+ array: typeof array;
14
+ tuple: typeof tuple;
15
+ union: typeof union;
16
+ literal: typeof literal;
17
+ };
18
+ export type { Schema, ParseResult } from './types/schema.js';
19
+ export type { ShapeToType } from './core/object.js';
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ import { string } from './core/string.js';
2
+ import { number } from './core/number.js';
3
+ import { boolean } from './core/boolean.js';
4
+ import { object } from './core/object.js';
5
+ import { array } from './core/array.js';
6
+ import { tuple } from './core/tuple.js';
7
+ import { union } from './core/union.js';
8
+ import { literal } from './core/literal.js';
9
+ export const inu = {
10
+ string,
11
+ number,
12
+ boolean,
13
+ object,
14
+ array,
15
+ tuple,
16
+ union,
17
+ literal,
18
+ };
@@ -0,0 +1,15 @@
1
+ export type ParseResult<T> = {
2
+ success: true;
3
+ value: T;
4
+ } | {
5
+ success: false;
6
+ error: string;
7
+ };
8
+ export declare abstract class Schema<T> {
9
+ abstract parse(value: unknown): ParseResult<T>;
10
+ optional(): Schema<T | undefined>;
11
+ nullable(): Schema<T | null>;
12
+ }
13
+ export type NestedShape = {
14
+ [k: string]: Schema<unknown> | NestedShape;
15
+ };
@@ -0,0 +1,37 @@
1
+ export class Schema {
2
+ optional() {
3
+ if (this instanceof InuOptional) {
4
+ return this;
5
+ }
6
+ return new InuOptional(this);
7
+ }
8
+ nullable() {
9
+ if (this instanceof InuNullable)
10
+ return this;
11
+ return new InuNullable(this);
12
+ }
13
+ }
14
+ class InuOptional extends Schema {
15
+ constructor(innerSchema) {
16
+ super();
17
+ this.innerSchema = innerSchema;
18
+ }
19
+ parse(value) {
20
+ if (value === undefined) {
21
+ return { success: true, value: undefined };
22
+ }
23
+ return this.innerSchema.parse(value);
24
+ }
25
+ }
26
+ class InuNullable extends Schema {
27
+ constructor(innerSchema) {
28
+ super();
29
+ this.innerSchema = innerSchema;
30
+ }
31
+ parse(value) {
32
+ if (value === null) {
33
+ return { success: true, value: null };
34
+ }
35
+ return this.innerSchema.parse(value);
36
+ }
37
+ }
@@ -0,0 +1,7 @@
1
+ import { Schema } from '../types/schema.js';
2
+ export declare function isSchema(value: unknown): value is Schema<unknown>;
3
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
4
+ export declare function isString(value: unknown): value is string;
5
+ export declare function isNumber(value: unknown): value is number;
6
+ export declare function isBoolean(value: unknown): value is boolean;
7
+ export declare function isArray(value: unknown): value is unknown[];
@@ -0,0 +1,21 @@
1
+ export function isSchema(value) {
2
+ return (typeof value === 'object' &&
3
+ value !== null &&
4
+ 'parse' in value &&
5
+ typeof value.parse === 'function');
6
+ }
7
+ export function isRecord(value) {
8
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
9
+ }
10
+ export function isString(value) {
11
+ return typeof value === 'string';
12
+ }
13
+ export function isNumber(value) {
14
+ return typeof value === 'number' && !isNaN(value);
15
+ }
16
+ export function isBoolean(value) {
17
+ return typeof value === 'boolean';
18
+ }
19
+ export function isArray(value) {
20
+ return Array.isArray(value);
21
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "inu-light",
3
+ "version": "1.0.0",
4
+ "description": "A ultra-lightweight TypeScript validation library with zero dependencies.",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "type": "module",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "keywords": [
16
+ "validation",
17
+ "typescript",
18
+ "schema",
19
+ "lightweight",
20
+ "zod-like"
21
+ ],
22
+ "author": "Seu Nome",
23
+ "license": "MIT"
24
+ }