Skip to content
Merged

Dev #44

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
## Changelog

### [v6.0.3](https://github.com/panates/valgen/compare/v6.0.2...v6.0.3) -
### [v6.1.0](https://github.com/panates/valgen/compare/v6.0.3...v6.1.0) -

#### 🚀 New Features

- feat: Added isInstanceOf validator with tests and export @Eray Hanoğlu

#### 🛠 Refactoring and Updates

- refactor: Updated imports for consistency and maintenance, upgraded dependencies in package-lock.json @Eray Hanoğlu

### [v6.0.3](https://github.com/panates/valgen/compare/v6.0.2...v6.0.3) - 6 April 2026

#### 🛠 Refactoring and Updates

Expand Down
498 changes: 263 additions & 235 deletions package-lock.json

Large diffs are not rendered by default.

23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
{
"name": "valgen",
"description": "Fast runtime type validator, converter and io (encoding/decoding) library",
"version": "6.0.3",
"version": "6.1.0",
"author": "Panates",
"license": "MIT",
"private": true,
"dependencies": {
"@browsery/validator": "^13.15.35",
"@jsopen/objects": "^2.2.1",
"date-fns": "^4.1.0",
"reflect-metadata": "^0.2.2",
"ts-gems": "^3.11.6",
"ts-gems": "^3.12.0",
"tslib": "^2.8.1"
},
"devDependencies": {
"@panates/eslint-config": "^2.1.3",
"@panates/eslint-config-ts": "^2.1.3",
"@panates/tsconfig": "^2.1.3",
"@panates/eslint-config": "^2.1.6",
"@panates/eslint-config-ts": "^2.1.6",
"@panates/tsconfig": "^2.1.6",
"@swc-node/register": "^1.11.1",
"@swc/core": "^1.15.24",
"@swc/core": "^1.15.32",
"@swc/helpers": "^0.5.21",
"@types/mocha": "^10.0.10",
"@types/node": "^25.5.2",
"@types/node": "^25.6.0",
"auto-changelog": "^2.5.0",
"c8": "^11.0.0",
"expect": "^30.3.0",
"globals": "^17.4.0",
"globals": "^17.5.0",
"husky": "^9.1.7",
"dpdm": "^4.0.1",
"mocha": "^11.7.5",
"npm-check-updates": "^20.0.0",
"prettier": "^3.8.1",
"npm-check-updates": "^22.0.1",
"prettier": "^3.8.3",
"rimraf": "^6.1.3",
"ts-cleanup": "^1.3.0",
"typescript": "^6.0.2"
"typescript": "^6.0.3"
},
"scripts": {
"compile": "tsc --noEmit",
Expand Down
21 changes: 20 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Context, ExecutionOptions } from './core/index.js';
import type { Type } from 'ts-gems';
import {
Context,
type ExecutionOptions,
type Validator,
} from './core/index.js';
import * as vg from './rules/index.js';

export * from './constants.js';
export * from './core/index.js';
const isInstanceOfCache = new WeakMap<Type, Validator>();

const isAlpha = vg.isAlpha();
const isAlphanumeric = vg.isAlphanumeric();
Expand All @@ -27,6 +33,18 @@ const isHex = vg.isHex();
const isHexColor = vg.isHexColor();
const isIBAN = vg.isIBAN();
const isInteger = vg.isInteger();
const isInstanceOf = <T extends object>(
clazz: Type<T>,
input: any,
options?: vg.isInstanceOf.Options,
) => {
let fn = isInstanceOfCache.get(clazz);
if (!fn) {
fn = vg.isInstanceOf(clazz);
isInstanceOfCache.set(clazz, fn);
}
return fn(input, options);
};
const isIP = vg.isIP();
const isIPRange = vg.isIPRange();
const isISSN = vg.isISSN();
Expand Down Expand Up @@ -108,6 +126,7 @@ export {
isHex,
isHexColor,
isIBAN,
isInstanceOf,
isInteger,
isIP,
isIPRange,
Expand Down
1 change: 1 addition & 0 deletions src/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export * from './type-rules/is-bigint.js';
export * from './type-rules/is-boolean.js';
export * from './type-rules/is-date.js';
export * from './type-rules/is-enum.js';
export * from './type-rules/is-instanceof.js';
export * from './type-rules/is-integer.js';
export * from './type-rules/is-null.js';
export * from './type-rules/is-number.js';
Expand Down
12 changes: 6 additions & 6 deletions src/rules/type-rules/is-array.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
type Context,
import type {
Context,
ErrorIssue,
type Nullish,
type ValidationOptions,
type Validator,
validator,
Nullish,
ValidationOptions,
Validator,
} from '../../core/index.js';
import { validator } from '../../core/index.js';

/**
* Validates if the value is "array" and applies validation for each item.
Expand Down
39 changes: 39 additions & 0 deletions src/rules/type-rules/is-instanceof.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { isPlainObject } from '@jsopen/objects';
import type { Type } from 'ts-gems';
import {
type Context,
type Nullish,
type ValidationOptions,
validator,
} from '../../core/index.js';

/**
* Validates if the value instance of given class or classes
* @validator isUndefined
*/
export function isInstanceOf<T extends object>(
clazz: Type<T>,
options?: isInstanceOf.Options,
) {
return validator<T, unknown>(
'isInstanceOf',
(input: unknown, context: Context, _this): Nullish<any> => {
if ((options?.coerce ?? context.coerce) && isPlainObject(input)) {
Object.setPrototypeOf(input, clazz.prototype);
}
if (!(input instanceof clazz)) {
context.fail(
_this,
`Value must be an instance of "${clazz.name}"`,
input,
);
}
return input;
},
options,
);
}

export namespace isInstanceOf {
export interface Options extends ValidationOptions {}
}
12 changes: 6 additions & 6 deletions src/rules/type-rules/is-record.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
type Context,
import type {
Context,
ErrorIssue,
type Nullish,
type ValidationOptions,
type Validator,
validator,
Nullish,
ValidationOptions,
Validator,
} from '../../core/index.js';
import { validator } from '../../core/index.js';

/**
* Validates the record object according to given "key" and "value" rules
Expand Down
8 changes: 4 additions & 4 deletions src/rules/utility-rules/all-of.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
type Context,
import type {
Context,
ValidationOptions,
type Validator,
validator,
Validator,
} from '../../core/index.js';
import { validator } from '../../core/index.js';

/**
* Test given value against to all codecs and returns original input
Expand Down
8 changes: 4 additions & 4 deletions src/rules/utility-rules/one-of.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
type Context,
import type {
Context,
ValidationOptions,
type Validator,
validator,
Validator,
} from '../../core/index.js';
import { validator } from '../../core/index.js';

type DiscriminatorRecord = Record<string, Validator>;

Expand Down
39 changes: 39 additions & 0 deletions test/type-rules/is-instanceof.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { expect } from 'expect';
import { isInstanceOf } from 'valgen';

describe('isInstanceOf', () => {
class Class1 {}
class Class2 {}

it('should validate value is instance of given class', () => {
const c1 = new Class1();
expect(isInstanceOf(Class1, c1)).toStrictEqual(c1);
expect(isInstanceOf(Class1, c1)).toBeInstanceOf(Class1);

expect(() => isInstanceOf(Class1, undefined)).toThrow(
'Value must be an instance of "Class1"',
);
expect(() => isInstanceOf(Class1, {})).toThrow(
'Value must be an instance of "Class1"',
);
expect(() => isInstanceOf(Class1, null)).toThrow(
'Value must be an instance of "Class1"',
);
expect(() => isInstanceOf(Class1, 5)).toThrow(
'Value must be an instance of "Class1"',
);
expect(() => isInstanceOf(Class1, 'x')).toThrow(
'Value must be an instance of "Class1"',
);
});

it('should coerce plain object to given class', () => {
expect(isInstanceOf(Class1, {}, { coerce: true })).toBeInstanceOf(Class1);
});

it('should not coerce the object if is not plain', () => {
expect(() => isInstanceOf(Class1, new Class2(), { coerce: true })).toThrow(
'Value must be an instance of "Class1"',
);
});
});