From e372c6fdd424506437df432fadf14f2bbc898d93 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Wed, 21 Dec 2022 10:05:28 +0330 Subject: [PATCH 01/23] feat:validator --- demo/validator/type.ts | 3 + demo/validator/validator.ts | 110 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 demo/validator/type.ts create mode 100644 demo/validator/validator.ts diff --git a/demo/validator/type.ts b/demo/validator/type.ts new file mode 100644 index 000000000..c0ae3aac1 --- /dev/null +++ b/demo/validator/type.ts @@ -0,0 +1,3 @@ +export interface valid { + [key: string]: valid | 'string' | 'number' | 'boolean'; +} diff --git a/demo/validator/validator.ts b/demo/validator/validator.ts new file mode 100644 index 000000000..3db69883d --- /dev/null +++ b/demo/validator/validator.ts @@ -0,0 +1,110 @@ +import {isNumber} from '@alwatr/math'; + +import type {valid} from './type.js'; + +function validator<T extends Record<string, unknown>>(valueObj: Record<string, unknown>, params: valid): T { + const validObj: Record<string, unknown> = {}; + for (const paramName in params) { + if (!Object.prototype.hasOwnProperty.call(params, paramName)) continue; + const type = params[paramName]; + if (typeof params[paramName] === 'object') { + validObj[paramName] = validator<T>(valueObj[paramName] as Record<string, unknown>, params[paramName] as valid); + } + + let value = valueObj[paramName] as string | number | boolean; + + if (type === 'boolean') { + if (value === true) { + validObj[paramName] = true; + } + else if (value === false) { + validObj[paramName] = false; + } + else if (value === 1) { + validObj[paramName] = true; + } + else if (value === 0) { + validObj[paramName] = false; + } + else if (typeof value === 'string') { + value = value.trim(); + if (value === 'true' || value === '1') { + validObj[paramName] = true; + } + else if (value === 'false' || value === '0') { + value = value.trim(); + validObj[paramName] = false; + } + } + else { + throw new Error(undefined, { + cause: { + name: 'boolean_validator', + message: `'${value}' not valid`, + }, + }); + } + } + else if (type === 'number') { + if (isNumber(value)) { + validObj[paramName] = +value; + } + else { + throw new Error('invalid_type', { + cause: { + name: 'number_validator', + message: `'${value}' not valid`, + }, + }); + } + } + else if (type === 'string') { + validObj[paramName] = value.toString(); + } + } + + return validObj as T; +} + +// number +console.log(validator<{a: number}>({a: 2}, {a: 'number'})); +console.log(validator<{a: number}>({a: '2'}, {a: 'number'})); + +// boolean +console.log(validator<{a: boolean}>({a: '1'}, {a: 'boolean'})); +console.log(validator<{a: boolean}>({a: 'true'}, {a: 'boolean'})); +console.log(validator<{a: boolean}>({a: '0'}, {a: 'boolean'})); +console.log(validator<{a: boolean}>({a: 'false'}, {a: 'boolean'})); + +// string +console.log(validator<{a: string}>({a: false}, {a: 'string'})); +console.log(validator<{a: string}>({a: 'false'}, {a: 'string'})); +console.log(validator<{a: string}>({a: 1}, {a: 'string'})); + +// nested object +console.log( + validator<{a: number; b: {c: boolean}}>( + {a: '2', b: {c: 'true', d: {e: 1}}}, + {a: 'number', b: {c: 'boolean', d: {e: 'number'}}}, + ), +); + +// not valid +try { + console.log( + validator<{a: number; b: {c: boolean}}>( + {a: '2', b: {c: 'true', d: {e: true}}}, + {a: 'number', b: {c: 'boolean', d: {e: 'number'}}}, + ), + ); +} +catch (error) { + console.log((error as Error)); +} + +try { + console.log(validator<{a: boolean}>({a: 'trus'}, {a: 'boolean'})); +} +catch (error) { + console.log((error as Error)); +} From c0cfdc21a99cf02bb4d56c00ba186429e72d3aa2 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Wed, 21 Dec 2022 10:26:05 +0330 Subject: [PATCH 02/23] fix(validator): validate boolean --- demo/validator/validator.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/demo/validator/validator.ts b/demo/validator/validator.ts index 3db69883d..8f939fccf 100644 --- a/demo/validator/validator.ts +++ b/demo/validator/validator.ts @@ -35,9 +35,17 @@ function validator<T extends Record<string, unknown>>(valueObj: Record<string, u value = value.trim(); validObj[paramName] = false; } + else { + throw new Error('invalid_type', { + cause: { + name: 'boolean_validator', + message: `'${value}' not valid`, + }, + }); + } } else { - throw new Error(undefined, { + throw new Error('invalid_type', { cause: { name: 'boolean_validator', message: `'${value}' not valid`, @@ -99,12 +107,12 @@ try { ); } catch (error) { - console.log((error as Error)); + console.log(error as Error); } try { - console.log(validator<{a: boolean}>({a: 'trus'}, {a: 'boolean'})); + console.log(validator<{a: boolean}>({a: 'tru'}, {a: 'boolean'})); } catch (error) { - console.log((error as Error)); + console.log(error as Error); } From ee9b601198ea8dded43497df6824e09f65be86b3 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Thu, 22 Dec 2022 11:20:18 +0330 Subject: [PATCH 03/23] fix(validator): remove extra trim --- demo/validator/validator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/demo/validator/validator.ts b/demo/validator/validator.ts index 8f939fccf..11a8bac8e 100644 --- a/demo/validator/validator.ts +++ b/demo/validator/validator.ts @@ -32,7 +32,6 @@ function validator<T extends Record<string, unknown>>(valueObj: Record<string, u validObj[paramName] = true; } else if (value === 'false' || value === '0') { - value = value.trim(); validObj[paramName] = false; } else { From 5aa48c354c116655e7824366d4efcd5965172fe3 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Sat, 24 Dec 2022 14:59:06 +0330 Subject: [PATCH 04/23] feat(validator): new package --- core/validator/README.md | 210 ++++++++++++++++++++++++++++++++ core/validator/package.json | 38 ++++++ core/validator/src/type.ts | 3 + core/validator/src/validator.ts | 51 ++++++++ core/validator/tsconfig.json | 15 +++ 5 files changed, 317 insertions(+) create mode 100644 core/validator/README.md create mode 100644 core/validator/package.json create mode 100644 core/validator/src/type.ts create mode 100644 core/validator/src/validator.ts create mode 100644 core/validator/tsconfig.json diff --git a/core/validator/README.md b/core/validator/README.md new file mode 100644 index 000000000..9f8034124 --- /dev/null +++ b/core/validator/README.md @@ -0,0 +1,210 @@ +# Alwatr Math - `@alwatr/math` + +Simple useful Math library written in tiny TypeScript module. + +## API + +### `UnicodeDigits(fromLanguages: Array<UnicodeLangKeys> | 'all' | 'common', toLanguage: UnicodeLangKeys)` + +Translate number. + +- **fromLanguages** The source language to be translated. +- **toLanguages** The dest language to be translated. + +Example: + +```ts +const unicodeDigits = new UnicodeDigits('common', 'en'); + +const list = [ + '0123456789', + '٠١٢٣٤٥٦٧٨٩', + '߀߁߂߃߄߅߆߇߈߉', + '०१२३४५६७८९', + '০১২৩৪৫৬৭৮৯', + '੦੧੨੩੪੫੬੭੮੯', + '૦૧૨૩૪૫૬૭૮૯', + '୦୧୨୩୪୫୬୭୮୯', + '௦௧௨௩௪௫௬௭௮௯', +].join('\n'); + +console.log(unicodeDigits.translate(list)); +``` + +### `unicodeDigits.translate(str: string): string` + +Convert the String of number of the source language to the destination language. + +- **str** is String of number of the source language. + +@TODO: update from ts files docs + +### `isNumber(value: unknown): boolean` + +Check the value is number or can convert to a number, for example string ' 123 ' can be converted to 123. + +#### Why is this needed? + +```ts +console.log(typeof '123'); //=> 'string' +console.log(+[]); //=> 0 +console.log(+''); //=> 0 +console.log(+' '); //=> 0 +console.log(typeof NaN); //=> 'number' +console.log(typeof Infinity); //=> 'number' +``` + +#### True + +<!-- prettier-ignore --> +```ts +import {isNumber} from 'https://esm.run/@alwatr/math'; + +isNumber(5e3); // true +isNumber(0xff); // true +isNumber(-1.1); // true +isNumber(0); // true +isNumber(1); // true +isNumber(1.1); // true +isNumber('-1.1'); // true +isNumber('0'); // true +isNumber('0xff'); // true +isNumber('1'); // true +isNumber('1.1'); // true +isNumber('5e3'); // true +isNumber('012'); // true +isNumber(parseInt('012')); // true +isNumber(parseFloat('012')); // true +``` + +#### False + +<!-- prettier-ignore --> +```ts +import {isNumber} from 'https://esm.run/@alwatr/math'; + +isNumber(Infinity); // false +isNumber(NaN); // false +isNumber(null); // false +isNumber(undefined); // false +isNumber(''); // false +isNumber(' '); // false +isNumber('foo'); // false +isNumber([1]); // false +isNumber([]); // false +isNumber(function () {}); // false +isNumber({}); // false +``` + +### `transformToRange(x: number, options}): number` + +Transform a number from one range to another. + +Options: + +```ts +{ + /** + * The input range [min, max]. + * + */ + in: [number, number]; + + /** + * The output (request) range [min, max]. + */ + out: [number, number]; + + /** + * If true, the output will be bounded to the output range (between min and max). + * + * In default behavior when x (input number) does not between input min~max range, + * the output value will be out of output min~max range. + * + */ + bound?: boolean; +} +``` + +#### Example + +```ts +transformToRange(5, {in: [0, 10], out: [0, 100]}); // => 50 +``` + +Make percentage of any value + +```ts +transformToRange(2000, {in: [0, 5000], out: [0, 100]}); // => 40 +``` + +Calculate progress-bar with + +```ts +const progressOuterWith = 400; //px +const gap = 5; //px (the visual gap between progressBar and component outer). +const currentProgress = 30; //% + +const progressBarWith = transformToRange(currentProgress, { + in: [0, 100], + out: [componentPadding, progressOuterWith - componentPadding], + bound: true, +}); + +this.progressBar.style.width = `${progressBarWith}px`; +``` + +### Generate Random + +### `value` + +Returns a float random number between 0 and 1 (1 Not included). + +```ts +console.log(random.value); // 0.7124123 +``` + +### `random.integer(min: number, max: number): number` + +Generate a random integer between min and max. + +```ts +console.log(random.integer(1, 10)); // somewhere between 1 and 10 +``` + +### `random.float(min: number, max: number): number` + +Generate a random float between min and max. + +```ts +console.log(random.float(1, 10)); // somewhere between 1 and 10 +``` + +### `string: (min: number, max?: number): string` + +Generate a random string with random length. +The string will contain only characters from the characters list. +The length of the string will be between min and max (max included). +If max not specified, the length will be set to min. + +```ts +console.log(random.string(6)); // something like 'Aab1V2' +``` + +### `step(min: number, max: number, step: number): number` + +Generate a random integer between min and max with a step. + +```ts +console.log(random.step(6, 10, 2)); // 6 or 8 or 10 +``` + +### `shuffle(array: any[]): any[]` + +Shuffle an array. + +```ts +const array = [1, 2, 3, 4, 5]; +random.shuffle(array); +console.log(array); // [2, 4, 3, 1, 5] +``` diff --git a/core/validator/package.json b/core/validator/package.json new file mode 100644 index 000000000..bba05d334 --- /dev/null +++ b/core/validator/package.json @@ -0,0 +1,38 @@ +{ + "name": "@alwatr/validator", + "version": "0.0.0", + "description": "Simple useful validator library written in tiny TypeScript module.", + "keywords": [ + "validator", + "typescript", + "esm", + "alwatr" + ], + "main": "validator.js", + "type": "module", + "types": "validator.d.ts", + "author": "S. Ali Mihandoost <ali.mihandoost@gmail.com>", + "contributors": [ + "S. Amir Mohammad Najafi <njfamirm@gmail.com> (njfamirm.ir)" + ], + "license": "MIT", + "files": [ + "**/*.{d.ts.map,d.ts,js.map,js,html,md}" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/AliMD/alwatr", + "directory": "core/validator" + }, + "homepage": "https://github.com/AliMD/alwatr/tree/main/core/validator#readme", + "bugs": { + "url": "https://github.com/AliMD/alwatr/issues" + }, + "dependencies": { + "tslib": "~2.4.1", + "@alwatr/validator": "~0.26.0" + } +} diff --git a/core/validator/src/type.ts b/core/validator/src/type.ts new file mode 100644 index 000000000..3431136f0 --- /dev/null +++ b/core/validator/src/type.ts @@ -0,0 +1,3 @@ +export type schema = { + [key: string]: schema | 'string' | 'number' | 'boolean'; +} diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts new file mode 100644 index 000000000..c6021e6d0 --- /dev/null +++ b/core/validator/src/validator.ts @@ -0,0 +1,51 @@ +import {isNumber} from '@alwatr/math'; + +import type {schema} from './type.js'; + +export {validator}; + +function validator<DataType extends Record<string, unknown>>(value: Record<string, unknown>, schema: schema): DataType { + for (const paramName in schema) { + if (!Object.prototype.hasOwnProperty.call(schema, paramName)) continue; + + const valueType = schema[paramName]; + if (typeof schema[paramName] === 'object') { + value[paramName] = + validator<DataType>(value[paramName] as Record<string, unknown>, schema[paramName] as schema); + } + + const validValue = value[paramName] as string | number | boolean; + + if (valueType === 'boolean') { + if (validValue === true || validValue === false) { + value[paramName] = true; + } + else { + throw new Error('invalid_type', { + cause: { + name: 'boolean_validator', + message: `'${validValue}' not valid`, + }, + }); + } + } + else if (valueType === 'number') { + if (isNumber(validValue)) { + value[paramName] = +validValue; + } + else { + throw new Error('invalid_type', { + cause: { + name: 'number_validator', + message: `'${validValue}' not valid`, + }, + }); + } + } + else if (valueType === 'string') { + value[paramName] = validValue.toString(); + } + } + + return value as DataType; +} diff --git a/core/validator/tsconfig.json b/core/validator/tsconfig.json new file mode 100644 index 000000000..cb6662a18 --- /dev/null +++ b/core/validator/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": ".tsbuildinfo", + "rootDir": "src", + "outDir": "." + }, + + "include": ["src/**/*.ts"], + "exclude": [], + "references": [ + {"path": "../math"} + ] +} From b48a30bac9f02f0b7edb3b4069c324f835f6d49b Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Sat, 24 Dec 2022 14:59:43 +0330 Subject: [PATCH 05/23] feat(validator): demo --- demo/package.json | 3 +- demo/tsconfig.json | 1 + demo/validator/type.ts | 3 -- demo/validator/validator.ts | 75 +------------------------------------ yarn.lock | 7 ++++ 5 files changed, 11 insertions(+), 78 deletions(-) delete mode 100644 demo/validator/type.ts diff --git a/demo/package.json b/demo/package.json index f4163e8a4..0be43a2a8 100644 --- a/demo/package.json +++ b/demo/package.json @@ -6,7 +6,8 @@ "type": "module", "private": "true", "contributors": [ - "MohammadMahdi Zamanian <mm25zamanian@gmail.com>" + "MohammadMahdi Zamanian <mm25zamanian@gmail.com>", + "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://njfamirm.ir)" ], "dependencies": { "@alwatr/logger": "~0.27.0", diff --git a/demo/tsconfig.json b/demo/tsconfig.json index a7edd5a62..f5c072b88 100644 --- a/demo/tsconfig.json +++ b/demo/tsconfig.json @@ -22,5 +22,6 @@ {"path": "../ui/element"}, {"path": "../ui/icon"}, {"path": "../core/storage-engine"}, + {"path": "../core/validator"} ], } diff --git a/demo/validator/type.ts b/demo/validator/type.ts deleted file mode 100644 index c0ae3aac1..000000000 --- a/demo/validator/type.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface valid { - [key: string]: valid | 'string' | 'number' | 'boolean'; -} diff --git a/demo/validator/validator.ts b/demo/validator/validator.ts index 11a8bac8e..36912759f 100644 --- a/demo/validator/validator.ts +++ b/demo/validator/validator.ts @@ -1,77 +1,4 @@ -import {isNumber} from '@alwatr/math'; - -import type {valid} from './type.js'; - -function validator<T extends Record<string, unknown>>(valueObj: Record<string, unknown>, params: valid): T { - const validObj: Record<string, unknown> = {}; - for (const paramName in params) { - if (!Object.prototype.hasOwnProperty.call(params, paramName)) continue; - const type = params[paramName]; - if (typeof params[paramName] === 'object') { - validObj[paramName] = validator<T>(valueObj[paramName] as Record<string, unknown>, params[paramName] as valid); - } - - let value = valueObj[paramName] as string | number | boolean; - - if (type === 'boolean') { - if (value === true) { - validObj[paramName] = true; - } - else if (value === false) { - validObj[paramName] = false; - } - else if (value === 1) { - validObj[paramName] = true; - } - else if (value === 0) { - validObj[paramName] = false; - } - else if (typeof value === 'string') { - value = value.trim(); - if (value === 'true' || value === '1') { - validObj[paramName] = true; - } - else if (value === 'false' || value === '0') { - validObj[paramName] = false; - } - else { - throw new Error('invalid_type', { - cause: { - name: 'boolean_validator', - message: `'${value}' not valid`, - }, - }); - } - } - else { - throw new Error('invalid_type', { - cause: { - name: 'boolean_validator', - message: `'${value}' not valid`, - }, - }); - } - } - else if (type === 'number') { - if (isNumber(value)) { - validObj[paramName] = +value; - } - else { - throw new Error('invalid_type', { - cause: { - name: 'number_validator', - message: `'${value}' not valid`, - }, - }); - } - } - else if (type === 'string') { - validObj[paramName] = value.toString(); - } - } - - return validObj as T; -} +import {validator} from '@alwatr/validator'; // number console.log(validator<{a: number}>({a: 2}, {a: 'number'})); diff --git a/yarn.lock b/yarn.lock index a50785ccc..1887dbc1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@alwatr/math@~0.26.0": + version "0.26.0" + resolved "https://registry.yarnpkg.com/@alwatr/math/-/math-0.26.0.tgz#924cb6c0f0ada5929b96cf718d4b6238ceab6446" + integrity sha512-iP8GJErkWw4Yh/GAX1yUOM5jB+Jw6aSAVzGRgsurdwqIfeZVSkvgB5VCSU2ZR0t2OWju2OEUvPfEwV2cIbdLsw== + dependencies: + tslib "~2.4.1" + "@ampproject/remapping@^2.1.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" From 4e99b8078455443e70aab5ebdbe30c21152b48ae Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Sat, 24 Dec 2022 15:00:36 +0330 Subject: [PATCH 06/23] feat(validator): throw value in error --- core/validator/src/validator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index c6021e6d0..7ecbbf650 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -24,7 +24,7 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin throw new Error('invalid_type', { cause: { name: 'boolean_validator', - message: `'${validValue}' not valid`, + message: JSON.stringify(value), }, }); } @@ -37,7 +37,7 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin throw new Error('invalid_type', { cause: { name: 'number_validator', - message: `'${validValue}' not valid`, + message: JSON.stringify(value), }, }); } From 635866d81134df0db46fc44b7ed7575e0486f9cd Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Sat, 24 Dec 2022 15:02:13 +0330 Subject: [PATCH 07/23] fix(validator): deps --- core/validator/package.json | 2 +- yarn.lock | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/core/validator/package.json b/core/validator/package.json index bba05d334..ec1f23000 100644 --- a/core/validator/package.json +++ b/core/validator/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "tslib": "~2.4.1", - "@alwatr/validator": "~0.26.0" + "@alwatr/math": "~0.26.0" } } diff --git a/yarn.lock b/yarn.lock index 1887dbc1e..a50785ccc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,13 +2,6 @@ # yarn lockfile v1 -"@alwatr/math@~0.26.0": - version "0.26.0" - resolved "https://registry.yarnpkg.com/@alwatr/math/-/math-0.26.0.tgz#924cb6c0f0ada5929b96cf718d4b6238ceab6446" - integrity sha512-iP8GJErkWw4Yh/GAX1yUOM5jB+Jw6aSAVzGRgsurdwqIfeZVSkvgB5VCSU2ZR0t2OWju2OEUvPfEwV2cIbdLsw== - dependencies: - tslib "~2.4.1" - "@ampproject/remapping@^2.1.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" From af237db8e0c2f5cd37d487304f2097bed95e3a90 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Mon, 26 Dec 2022 07:41:29 +0330 Subject: [PATCH 08/23] fix(validator): readme --- core/validator/README.md | 211 +-------------------------------------- 1 file changed, 2 insertions(+), 209 deletions(-) diff --git a/core/validator/README.md b/core/validator/README.md index 9f8034124..d9e0003ab 100644 --- a/core/validator/README.md +++ b/core/validator/README.md @@ -1,210 +1,3 @@ -# Alwatr Math - `@alwatr/math` +# Alwatr Math - `@alwatr/validator` -Simple useful Math library written in tiny TypeScript module. - -## API - -### `UnicodeDigits(fromLanguages: Array<UnicodeLangKeys> | 'all' | 'common', toLanguage: UnicodeLangKeys)` - -Translate number. - -- **fromLanguages** The source language to be translated. -- **toLanguages** The dest language to be translated. - -Example: - -```ts -const unicodeDigits = new UnicodeDigits('common', 'en'); - -const list = [ - '0123456789', - '٠١٢٣٤٥٦٧٨٩', - '߀߁߂߃߄߅߆߇߈߉', - '०१२३४५६७८९', - '০১২৩৪৫৬৭৮৯', - '੦੧੨੩੪੫੬੭੮੯', - '૦૧૨૩૪૫૬૭૮૯', - '୦୧୨୩୪୫୬୭୮୯', - '௦௧௨௩௪௫௬௭௮௯', -].join('\n'); - -console.log(unicodeDigits.translate(list)); -``` - -### `unicodeDigits.translate(str: string): string` - -Convert the String of number of the source language to the destination language. - -- **str** is String of number of the source language. - -@TODO: update from ts files docs - -### `isNumber(value: unknown): boolean` - -Check the value is number or can convert to a number, for example string ' 123 ' can be converted to 123. - -#### Why is this needed? - -```ts -console.log(typeof '123'); //=> 'string' -console.log(+[]); //=> 0 -console.log(+''); //=> 0 -console.log(+' '); //=> 0 -console.log(typeof NaN); //=> 'number' -console.log(typeof Infinity); //=> 'number' -``` - -#### True - -<!-- prettier-ignore --> -```ts -import {isNumber} from 'https://esm.run/@alwatr/math'; - -isNumber(5e3); // true -isNumber(0xff); // true -isNumber(-1.1); // true -isNumber(0); // true -isNumber(1); // true -isNumber(1.1); // true -isNumber('-1.1'); // true -isNumber('0'); // true -isNumber('0xff'); // true -isNumber('1'); // true -isNumber('1.1'); // true -isNumber('5e3'); // true -isNumber('012'); // true -isNumber(parseInt('012')); // true -isNumber(parseFloat('012')); // true -``` - -#### False - -<!-- prettier-ignore --> -```ts -import {isNumber} from 'https://esm.run/@alwatr/math'; - -isNumber(Infinity); // false -isNumber(NaN); // false -isNumber(null); // false -isNumber(undefined); // false -isNumber(''); // false -isNumber(' '); // false -isNumber('foo'); // false -isNumber([1]); // false -isNumber([]); // false -isNumber(function () {}); // false -isNumber({}); // false -``` - -### `transformToRange(x: number, options}): number` - -Transform a number from one range to another. - -Options: - -```ts -{ - /** - * The input range [min, max]. - * - */ - in: [number, number]; - - /** - * The output (request) range [min, max]. - */ - out: [number, number]; - - /** - * If true, the output will be bounded to the output range (between min and max). - * - * In default behavior when x (input number) does not between input min~max range, - * the output value will be out of output min~max range. - * - */ - bound?: boolean; -} -``` - -#### Example - -```ts -transformToRange(5, {in: [0, 10], out: [0, 100]}); // => 50 -``` - -Make percentage of any value - -```ts -transformToRange(2000, {in: [0, 5000], out: [0, 100]}); // => 40 -``` - -Calculate progress-bar with - -```ts -const progressOuterWith = 400; //px -const gap = 5; //px (the visual gap between progressBar and component outer). -const currentProgress = 30; //% - -const progressBarWith = transformToRange(currentProgress, { - in: [0, 100], - out: [componentPadding, progressOuterWith - componentPadding], - bound: true, -}); - -this.progressBar.style.width = `${progressBarWith}px`; -``` - -### Generate Random - -### `value` - -Returns a float random number between 0 and 1 (1 Not included). - -```ts -console.log(random.value); // 0.7124123 -``` - -### `random.integer(min: number, max: number): number` - -Generate a random integer between min and max. - -```ts -console.log(random.integer(1, 10)); // somewhere between 1 and 10 -``` - -### `random.float(min: number, max: number): number` - -Generate a random float between min and max. - -```ts -console.log(random.float(1, 10)); // somewhere between 1 and 10 -``` - -### `string: (min: number, max?: number): string` - -Generate a random string with random length. -The string will contain only characters from the characters list. -The length of the string will be between min and max (max included). -If max not specified, the length will be set to min. - -```ts -console.log(random.string(6)); // something like 'Aab1V2' -``` - -### `step(min: number, max: number, step: number): number` - -Generate a random integer between min and max with a step. - -```ts -console.log(random.step(6, 10, 2)); // 6 or 8 or 10 -``` - -### `shuffle(array: any[]): any[]` - -Shuffle an array. - -```ts -const array = [1, 2, 3, 4, 5]; -random.shuffle(array); -console.log(array); // [2, 4, 3, 1, 5] -``` +Simple useful validator library written in tiny TypeScript module. From 593b4a499529d995836c7a91abc979c4b3a5543b Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Mon, 26 Dec 2022 08:06:27 +0330 Subject: [PATCH 09/23] fix(validator): boolean validator --- core/validator/src/validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index 7ecbbf650..7b47940d1 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -18,7 +18,7 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin if (valueType === 'boolean') { if (validValue === true || validValue === false) { - value[paramName] = true; + value[paramName] = validValue; } else { throw new Error('invalid_type', { From bd7391cd74ac7717b1a253604fa304b8279ddb71 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Mon, 26 Dec 2022 08:06:49 +0330 Subject: [PATCH 10/23] chore(validator): improve logging --- core/validator/src/validator.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index 7b47940d1..f262ecb63 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -9,6 +9,7 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin if (!Object.prototype.hasOwnProperty.call(schema, paramName)) continue; const valueType = schema[paramName]; + // nested object if (typeof schema[paramName] === 'object') { value[paramName] = validator<DataType>(value[paramName] as Record<string, unknown>, schema[paramName] as schema); @@ -23,7 +24,7 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin else { throw new Error('invalid_type', { cause: { - name: 'boolean_validator', + name: 'boolean', message: JSON.stringify(value), }, }); @@ -36,7 +37,7 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin else { throw new Error('invalid_type', { cause: { - name: 'number_validator', + name: 'number', message: JSON.stringify(value), }, }); From da915461583440d655f26c89c0c29b5c9cb74c6e Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Mon, 26 Dec 2022 08:07:45 +0330 Subject: [PATCH 11/23] feat(validator): export schema type --- core/validator/src/type.ts | 4 ++-- core/validator/src/validator.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/validator/src/type.ts b/core/validator/src/type.ts index 3431136f0..966edd2cf 100644 --- a/core/validator/src/type.ts +++ b/core/validator/src/type.ts @@ -1,3 +1,3 @@ -export type schema = { - [key: string]: schema | 'string' | 'number' | 'boolean'; +export type Schema = { + [key: string]: Schema | 'string' | 'number' | 'boolean'; } diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index f262ecb63..e0160875e 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -1,10 +1,10 @@ import {isNumber} from '@alwatr/math'; -import type {schema} from './type.js'; +import type {Schema} from './type.js'; -export {validator}; +export {validator, Schema}; -function validator<DataType extends Record<string, unknown>>(value: Record<string, unknown>, schema: schema): DataType { +function validator<DataType extends Record<string, unknown>>(value: Record<string, unknown>, schema: Schema): DataType { for (const paramName in schema) { if (!Object.prototype.hasOwnProperty.call(schema, paramName)) continue; @@ -12,7 +12,7 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin // nested object if (typeof schema[paramName] === 'object') { value[paramName] = - validator<DataType>(value[paramName] as Record<string, unknown>, schema[paramName] as schema); + validator<DataType>(value[paramName] as Record<string, unknown>, schema[paramName] as Schema); } const validValue = value[paramName] as string | number | boolean; From 4f11b61af433f348cc4a4bd5f837848c6c1298a4 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Mon, 26 Dec 2022 08:19:49 +0330 Subject: [PATCH 12/23] feat(validator): update demo --- demo/validator/validator.ts | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/demo/validator/validator.ts b/demo/validator/validator.ts index 36912759f..3015e9c6d 100644 --- a/demo/validator/validator.ts +++ b/demo/validator/validator.ts @@ -5,20 +5,16 @@ console.log(validator<{a: number}>({a: 2}, {a: 'number'})); console.log(validator<{a: number}>({a: '2'}, {a: 'number'})); // boolean -console.log(validator<{a: boolean}>({a: '1'}, {a: 'boolean'})); -console.log(validator<{a: boolean}>({a: 'true'}, {a: 'boolean'})); -console.log(validator<{a: boolean}>({a: '0'}, {a: 'boolean'})); -console.log(validator<{a: boolean}>({a: 'false'}, {a: 'boolean'})); +console.log(validator<{a: boolean}>({a: true}, {a: 'boolean'})); +console.log(validator<{a: boolean}>({a: false}, {a: 'boolean'})); // string -console.log(validator<{a: string}>({a: false}, {a: 'string'})); -console.log(validator<{a: string}>({a: 'false'}, {a: 'string'})); -console.log(validator<{a: string}>({a: 1}, {a: 'string'})); +console.log(validator<{a: string}>({a: 'salam'}, {a: 'string'})); // nested object console.log( - validator<{a: number; b: {c: boolean}}>( - {a: '2', b: {c: 'true', d: {e: 1}}}, + validator<{a: number; b: {c: boolean, d: {e: number}}}>( + {a: '2', b: {c: true, d: {e: 1}}}, {a: 'number', b: {c: 'boolean', d: {e: 'number'}}}, ), ); @@ -26,19 +22,19 @@ console.log( // not valid try { console.log( - validator<{a: number; b: {c: boolean}}>( - {a: '2', b: {c: 'true', d: {e: true}}}, + validator<{a: number; b: {c: boolean, d: {e: number}}}>( + {a: '2', b: {c: true, d: {e: true}}}, {a: 'number', b: {c: 'boolean', d: {e: 'number'}}}, ), ); } catch (error) { - console.log(error as Error); + console.log(error); } try { - console.log(validator<{a: boolean}>({a: 'tru'}, {a: 'boolean'})); + console.log(validator<{a: boolean}>({a: 'test'}, {a: 'boolean'})); } catch (error) { - console.log(error as Error); + console.log(error); } From b68b2e4671ff30efcc817213e4bf9bee9c322d90 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Mon, 26 Dec 2022 08:20:03 +0330 Subject: [PATCH 13/23] feat(validator): rename var --- core/validator/src/validator.ts | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index e0160875e..e6b436b7c 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -5,21 +5,20 @@ import type {Schema} from './type.js'; export {validator, Schema}; function validator<DataType extends Record<string, unknown>>(value: Record<string, unknown>, schema: Schema): DataType { - for (const paramName in schema) { - if (!Object.prototype.hasOwnProperty.call(schema, paramName)) continue; + for (const instance in schema) { + if (!Object.prototype.hasOwnProperty.call(schema, instance)) continue; - const valueType = schema[paramName]; + const valueType = schema[instance]; // nested object - if (typeof schema[paramName] === 'object') { - value[paramName] = - validator<DataType>(value[paramName] as Record<string, unknown>, schema[paramName] as Schema); + if (typeof schema[instance] === 'object') { + value[instance] = validator<DataType>(value[instance] as Record<string, unknown>, schema[instance] as Schema); } - const validValue = value[paramName] as string | number | boolean; + const valueKey = value[instance] as string | number | boolean; if (valueType === 'boolean') { - if (validValue === true || validValue === false) { - value[paramName] = validValue; + if (valueKey === true || valueKey === false) { + value[instance] = valueKey; } else { throw new Error('invalid_type', { @@ -31,8 +30,8 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin } } else if (valueType === 'number') { - if (isNumber(validValue)) { - value[paramName] = +validValue; + if (isNumber(valueKey)) { + value[instance] = +valueKey; } else { throw new Error('invalid_type', { @@ -44,7 +43,17 @@ function validator<DataType extends Record<string, unknown>>(value: Record<strin } } else if (valueType === 'string') { - value[paramName] = validValue.toString(); + if (typeof valueKey === 'string') { + value[instance] = valueKey; + } + else { + throw new Error('invalid_type', { + cause: { + name: 'string', + message: JSON.stringify(value), + }, + }); + } } } From 720dbb7e9a1eb0fca53b70303e1b824348ae3709 Mon Sep 17 00:00:00 2001 From: Ali Mihandoost <ali.mihandoost@gmail.com> Date: Sun, 1 Jan 2023 17:46:47 +0330 Subject: [PATCH 14/23] refactor(validator): review --- core/validator/src/type.ts | 8 ++- core/validator/src/validator.ts | 92 +++++++++++++++++++++++---------- 2 files changed, 72 insertions(+), 28 deletions(-) diff --git a/core/validator/src/type.ts b/core/validator/src/type.ts index 966edd2cf..d5aebb766 100644 --- a/core/validator/src/type.ts +++ b/core/validator/src/type.ts @@ -1,3 +1,7 @@ -export type Schema = { - [key: string]: Schema | 'string' | 'number' | 'boolean'; +export type JsonSchema = { + [key: string]: JsonSchema | StringConstructor | NumberConstructor | BooleanConstructor; +} + +export type ValidType = { + [key: string]: ValidType | string | number | boolean; } diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index e6b436b7c..739dfe125 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -1,61 +1,101 @@ import {isNumber} from '@alwatr/math'; -import type {Schema} from './type.js'; +import type {JsonSchema, ValidType} from './type.js'; -export {validator, Schema}; +export {JsonSchema}; -function validator<DataType extends Record<string, unknown>>(value: Record<string, unknown>, schema: Schema): DataType { - for (const instance in schema) { - if (!Object.prototype.hasOwnProperty.call(schema, instance)) continue; +export function validator<T extends ValidType>( + validSchema: JsonSchema, + targetObject: Record<string, unknown>, + path = '.', +): T { + const validObject: ValidType = {}; - const valueType = schema[instance]; - // nested object - if (typeof schema[instance] === 'object') { - value[instance] = validator<DataType>(value[instance] as Record<string, unknown>, schema[instance] as Schema); + if (typeof targetObject !== 'object' || targetObject == null) { + throw new Error('invalid_type', { + cause: { + itemPath: path, + itemSchema: 'JsonSchema', + itemValue: String(targetObject), + }, + }); + } + + for (const itemName in validSchema) { + if (!Object.prototype.hasOwnProperty.call(validSchema, itemName)) continue; + + const itemPath = `${path}/${itemName}`; + const itemSchema = validSchema[itemName]; + + if (typeof itemSchema === 'object') { + // nested object + const itemValue = targetObject[itemName] as Record<string, unknown>; + validObject[itemName] = validator<ValidType>(itemSchema, itemValue, itemPath); + continue; } + // else - const valueKey = value[instance] as string | number | boolean; + const itemValue = targetObject[itemName] as string | number | boolean; - if (valueType === 'boolean') { - if (valueKey === true || valueKey === false) { - value[instance] = valueKey; + if (itemSchema === Boolean) { + const strValue = String(itemValue).toLowerCase(); + if (strValue === 'true') { + validObject[itemName] = true; + } + else if (strValue === 'false') { + validObject[itemName] = false; } else { throw new Error('invalid_type', { cause: { - name: 'boolean', - message: JSON.stringify(value), + itemPath, + itemSchema: 'Boolean', + itemValue: String(itemValue), }, }); } } - else if (valueType === 'number') { - if (isNumber(valueKey)) { - value[instance] = +valueKey; + + else if (itemSchema === Number) { + if (isNumber(itemValue)) { + validObject[itemName] = +itemValue; } else { throw new Error('invalid_type', { cause: { - name: 'number', - message: JSON.stringify(value), + itemPath, + itemSchema: 'Number', + itemValue: String(itemValue), }, }); } } - else if (valueType === 'string') { - if (typeof valueKey === 'string') { - value[instance] = valueKey; + + else if (itemSchema === String) { + if (typeof itemValue === 'string') { + validObject[itemName] = itemValue; } else { throw new Error('invalid_type', { cause: { - name: 'string', - message: JSON.stringify(value), + itemPath, + itemSchema: 'String', + itemValue: String(itemValue), }, }); } } + + else { + throw new Error('invalid_schema', { + cause: { + itemPath, + itemSchema: String(itemSchema), + itemValue: String(itemValue), + }, + }); + } } - return value as DataType; + return targetObject as T; } From cc21a906b371f51696c3619fc0df0f392be99dee Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Sun, 1 Jan 2023 20:24:46 +0330 Subject: [PATCH 15/23] feat(validator): update demo --- core/validator/src/validator.ts | 6 +++--- demo/validator/validator.ts | 16 ++++++++-------- yarn.lock | 7 +++++++ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index 739dfe125..be918cb69 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -48,7 +48,7 @@ export function validator<T extends ValidType>( else { throw new Error('invalid_type', { cause: { - itemPath, + itemPath: itemPath, itemSchema: 'Boolean', itemValue: String(itemValue), }, @@ -63,7 +63,7 @@ export function validator<T extends ValidType>( else { throw new Error('invalid_type', { cause: { - itemPath, + itemPath: itemPath, itemSchema: 'Number', itemValue: String(itemValue), }, @@ -78,7 +78,7 @@ export function validator<T extends ValidType>( else { throw new Error('invalid_type', { cause: { - itemPath, + itemPath: itemPath, itemSchema: 'String', itemValue: String(itemValue), }, diff --git a/demo/validator/validator.ts b/demo/validator/validator.ts index 3015e9c6d..3e51cb605 100644 --- a/demo/validator/validator.ts +++ b/demo/validator/validator.ts @@ -1,21 +1,21 @@ import {validator} from '@alwatr/validator'; // number -console.log(validator<{a: number}>({a: 2}, {a: 'number'})); -console.log(validator<{a: number}>({a: '2'}, {a: 'number'})); +console.log(validator<{a: number}>({a: Number}, {a: 2})); +console.log(validator<{a: number}>({a: Number}, {a: '2'})); // boolean -console.log(validator<{a: boolean}>({a: true}, {a: 'boolean'})); -console.log(validator<{a: boolean}>({a: false}, {a: 'boolean'})); +console.log(validator<{a: boolean}>({a: Boolean}, {a: 'false'})); +console.log(validator<{a: boolean}>({a: Boolean}, {a: 'true'})); // string -console.log(validator<{a: string}>({a: 'salam'}, {a: 'string'})); +console.log(validator<{a: string}>({a: String}, {a: 'salam'})); // nested object console.log( validator<{a: number; b: {c: boolean, d: {e: number}}}>( + {a: Number, b: {c: Boolean, d: {e: Number}}}, {a: '2', b: {c: true, d: {e: 1}}}, - {a: 'number', b: {c: 'boolean', d: {e: 'number'}}}, ), ); @@ -23,8 +23,8 @@ console.log( try { console.log( validator<{a: number; b: {c: boolean, d: {e: number}}}>( + {a: Number, b: {c: Boolean, d: {e: Number}}}, {a: '2', b: {c: true, d: {e: true}}}, - {a: 'number', b: {c: 'boolean', d: {e: 'number'}}}, ), ); } @@ -33,7 +33,7 @@ catch (error) { } try { - console.log(validator<{a: boolean}>({a: 'test'}, {a: 'boolean'})); + console.log(validator<{a: boolean}>({a: Boolean}, {a: 'test'})); } catch (error) { console.log(error); diff --git a/yarn.lock b/yarn.lock index a50785ccc..a4f304fd8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@alwatr/math@~0.26.0": + version "0.26.1" + resolved "https://registry.yarnpkg.com/@alwatr/math/-/math-0.26.1.tgz#9165b9212fdf5a7c0576d8fa6aa2b58dc9a3d750" + integrity sha512-BPxV+Kl6Z3ErQVEH2JqLMEdhRNr70u0vXma/dPx81J+YbXiM5RP6ftxZto2wSuTvIcB++Wajg3KhIUke/wmJ7g== + dependencies: + tslib "~2.4.1" + "@ampproject/remapping@^2.1.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" From 48779b1617e319cc3411aae3aaef2e94d303374c Mon Sep 17 00:00:00 2001 From: Ali Mihandoost <ali.mihandoost@gmail.com> Date: Tue, 3 Jan 2023 15:16:36 +0330 Subject: [PATCH 16/23] fix(tsconfig): add missing core/validator --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index c5c50bc56..47100cfb1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,7 @@ {"path": "./core/storage-engine"}, {"path": "./core/storage-client"}, {"path": "./core/token"}, + {"path": "./core/validator"}, {"path": "./core/type"}, // ui From 88bcfff8a144315884690367e738bd3249b3623c Mon Sep 17 00:00:00 2001 From: Ali Mihandoost <ali.mihandoost@gmail.com> Date: Tue, 3 Jan 2023 15:16:56 +0330 Subject: [PATCH 17/23] refactor(validator): rollback cause.itemPath --- core/validator/src/validator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index be918cb69..739dfe125 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -48,7 +48,7 @@ export function validator<T extends ValidType>( else { throw new Error('invalid_type', { cause: { - itemPath: itemPath, + itemPath, itemSchema: 'Boolean', itemValue: String(itemValue), }, @@ -63,7 +63,7 @@ export function validator<T extends ValidType>( else { throw new Error('invalid_type', { cause: { - itemPath: itemPath, + itemPath, itemSchema: 'Number', itemValue: String(itemValue), }, @@ -78,7 +78,7 @@ export function validator<T extends ValidType>( else { throw new Error('invalid_type', { cause: { - itemPath: itemPath, + itemPath, itemSchema: 'String', itemValue: String(itemValue), }, From aa44ed0b18cca15a0c689f9bdb9ce584b0a55eb0 Mon Sep 17 00:00:00 2001 From: Ali Mihandoost <ali.mihandoost@gmail.com> Date: Tue, 3 Jan 2023 15:20:52 +0330 Subject: [PATCH 18/23] fix(validator): return validObject --- core/validator/src/validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index 739dfe125..72477749e 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -97,5 +97,5 @@ export function validator<T extends ValidType>( } } - return targetObject as T; + return validObject as T; } From c063f18b68d2b718bfbfd37abb95f8289eae62e0 Mon Sep 17 00:00:00 2001 From: Ali Mihandoost <ali.mihandoost@gmail.com> Date: Tue, 3 Jan 2023 15:36:00 +0330 Subject: [PATCH 19/23] refactor(validator): change original object --- core/validator/src/validator.ts | 42 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index 72477749e..f4bdf8f93 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -9,8 +9,6 @@ export function validator<T extends ValidType>( targetObject: Record<string, unknown>, path = '.', ): T { - const validObject: ValidType = {}; - if (typeof targetObject !== 'object' || targetObject == null) { throw new Error('invalid_type', { cause: { @@ -21,29 +19,39 @@ export function validator<T extends ValidType>( }); } - for (const itemName in validSchema) { - if (!Object.prototype.hasOwnProperty.call(validSchema, itemName)) continue; + for (const itemName in targetObject) { + if (!Object.prototype.hasOwnProperty.call(targetObject, itemName)) continue; const itemPath = `${path}/${itemName}`; const itemSchema = validSchema[itemName]; + const itemValue = targetObject[itemName] as string | number | boolean | Record<string, unknown>; - if (typeof itemSchema === 'object') { - // nested object - const itemValue = targetObject[itemName] as Record<string, unknown>; - validObject[itemName] = validator<ValidType>(itemSchema, itemValue, itemPath); - continue; + if (itemSchema == null) { + throw new Error('invalid_type', { + cause: { + itemPath, + itemSchema: 'undefined', + itemValue: String(itemValue), + }, + }); } - // else - const itemValue = targetObject[itemName] as string | number | boolean; + else if (typeof itemSchema === 'object') { + // nested object + targetObject[itemName] = validator<ValidType>( + itemSchema, + itemValue as Record<string, unknown>, + itemPath, + ); + } - if (itemSchema === Boolean) { + else if (itemSchema === Boolean) { const strValue = String(itemValue).toLowerCase(); if (strValue === 'true') { - validObject[itemName] = true; + targetObject[itemName] = true; } else if (strValue === 'false') { - validObject[itemName] = false; + targetObject[itemName] = false; } else { throw new Error('invalid_type', { @@ -58,7 +66,7 @@ export function validator<T extends ValidType>( else if (itemSchema === Number) { if (isNumber(itemValue)) { - validObject[itemName] = +itemValue; + targetObject[itemName] = +itemValue; } else { throw new Error('invalid_type', { @@ -73,7 +81,7 @@ export function validator<T extends ValidType>( else if (itemSchema === String) { if (typeof itemValue === 'string') { - validObject[itemName] = itemValue; + targetObject[itemName] = itemValue; } else { throw new Error('invalid_type', { @@ -97,5 +105,5 @@ export function validator<T extends ValidType>( } } - return validObject as T; + return targetObject as T; } From 7c02422e9caf9ab5dd8e352714db346518229d59 Mon Sep 17 00:00:00 2001 From: Ali Mihandoost <ali.mihandoost@gmail.com> Date: Tue, 3 Jan 2023 16:07:37 +0330 Subject: [PATCH 20/23] feat(validator): enum values --- core/validator/src/type.ts | 17 +++++++++++++---- core/validator/src/validator.ts | 18 ++++++++++-------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/core/validator/src/type.ts b/core/validator/src/type.ts index d5aebb766..28a563b01 100644 --- a/core/validator/src/type.ts +++ b/core/validator/src/type.ts @@ -1,7 +1,16 @@ export type JsonSchema = { - [key: string]: JsonSchema | StringConstructor | NumberConstructor | BooleanConstructor; -} + [key: string]: + | JsonSchema + | StringConstructor + | NumberConstructor + | BooleanConstructor + | string + | number + | boolean + | null + | undefined; +}; export type ValidType = { - [key: string]: ValidType | string | number | boolean; -} + [key: string]: ValidType | string | number | boolean | null | undefined; +}; diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index f4bdf8f93..389c6a890 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -93,15 +93,17 @@ export function validator<T extends ValidType>( }); } } - else { - throw new Error('invalid_schema', { - cause: { - itemPath, - itemSchema: String(itemSchema), - itemValue: String(itemValue), - }, - }); + if (itemValue !== itemSchema) { + throw new Error('invalid_type', { + cause: { + message: 'invalid enum value', + itemPath, + itemSchema: String(itemSchema), + itemValue: String(itemValue), + }, + }); + } } } From 60829a6280fdc3138f8702cee776e0dae11f548f Mon Sep 17 00:00:00 2001 From: Ali Mihandoost <ali.mihandoost@gmail.com> Date: Tue, 3 Jan 2023 16:08:09 +0330 Subject: [PATCH 21/23] feat(validator): additionalProperties --- core/validator/src/validator.ts | 48 ++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/core/validator/src/validator.ts b/core/validator/src/validator.ts index 389c6a890..3424afdf3 100644 --- a/core/validator/src/validator.ts +++ b/core/validator/src/validator.ts @@ -7,11 +7,13 @@ export {JsonSchema}; export function validator<T extends ValidType>( validSchema: JsonSchema, targetObject: Record<string, unknown>, + additionalProperties = false, path = '.', ): T { if (typeof targetObject !== 'object' || targetObject == null) { throw new Error('invalid_type', { cause: { + message: 'targetObject is not a function or null', itemPath: path, itemSchema: 'JsonSchema', itemValue: String(targetObject), @@ -19,32 +21,36 @@ export function validator<T extends ValidType>( }); } - for (const itemName in targetObject) { - if (!Object.prototype.hasOwnProperty.call(targetObject, itemName)) continue; + if ( + additionalProperties === false && + Object.keys(validSchema).sort().join() !== Object.keys(targetObject).sort().join() + ) { + throw new Error('invalid_type', { + cause: { + message: 'Object.keys(validSchema) !== Object.keys(targetObject)', + itemPath: path, + itemSchema: String(validSchema), + itemValue: String(targetObject), + }, + }); + } + + for (const itemName in validSchema) { + if (!Object.prototype.hasOwnProperty.call(validSchema, itemName)) continue; const itemPath = `${path}/${itemName}`; const itemSchema = validSchema[itemName]; const itemValue = targetObject[itemName] as string | number | boolean | Record<string, unknown>; - if (itemSchema == null) { - throw new Error('invalid_type', { - cause: { - itemPath, - itemSchema: 'undefined', - itemValue: String(itemValue), - }, - }); - } - - else if (typeof itemSchema === 'object') { + if (typeof itemSchema === 'object' && itemSchema != null) { // nested object targetObject[itemName] = validator<ValidType>( itemSchema, - itemValue as Record<string, unknown>, - itemPath, + itemValue as Record<string, unknown>, + additionalProperties, + itemPath, ); } - else if (itemSchema === Boolean) { const strValue = String(itemValue).toLowerCase(); if (strValue === 'true') { @@ -56,6 +62,7 @@ export function validator<T extends ValidType>( else { throw new Error('invalid_type', { cause: { + message: 'invalid type', itemPath, itemSchema: 'Boolean', itemValue: String(itemValue), @@ -63,7 +70,6 @@ export function validator<T extends ValidType>( }); } } - else if (itemSchema === Number) { if (isNumber(itemValue)) { targetObject[itemName] = +itemValue; @@ -71,6 +77,7 @@ export function validator<T extends ValidType>( else { throw new Error('invalid_type', { cause: { + message: 'invalid type', itemPath, itemSchema: 'Number', itemValue: String(itemValue), @@ -78,14 +85,11 @@ export function validator<T extends ValidType>( }); } } - else if (itemSchema === String) { - if (typeof itemValue === 'string') { - targetObject[itemName] = itemValue; - } - else { + if (typeof itemValue !== 'string') { throw new Error('invalid_type', { cause: { + message: 'invalid type', itemPath, itemSchema: 'String', itemValue: String(itemValue), From 9e577cad18be45942d36d633932ef0aa2c2ec512 Mon Sep 17 00:00:00 2001 From: Ali Mihandoost <ali.mihandoost@gmail.com> Date: Tue, 3 Jan 2023 16:22:48 +0330 Subject: [PATCH 22/23] feat(validator): demo --- core/validator/src/type.ts | 3 +- demo/validator/validator.ts | 59 +++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/core/validator/src/type.ts b/core/validator/src/type.ts index 28a563b01..b03c3331b 100644 --- a/core/validator/src/type.ts +++ b/core/validator/src/type.ts @@ -7,8 +7,7 @@ export type JsonSchema = { | string | number | boolean - | null - | undefined; + | null; }; export type ValidType = { diff --git a/demo/validator/validator.ts b/demo/validator/validator.ts index 3e51cb605..07fbae639 100644 --- a/demo/validator/validator.ts +++ b/demo/validator/validator.ts @@ -1,40 +1,47 @@ import {validator} from '@alwatr/validator'; -// number -console.log(validator<{a: number}>({a: Number}, {a: 2})); -console.log(validator<{a: number}>({a: Number}, {a: '2'})); - -// boolean -console.log(validator<{a: boolean}>({a: Boolean}, {a: 'false'})); -console.log(validator<{a: boolean}>({a: Boolean}, {a: 'true'})); +console.log('basic test'); +console.log( + validator< + {num: number; str: string; bool: boolean; _null: null; undef: undefined; ali: 'ali'; five: 5; true: true} + >( + {num: Number, str: String, bool: Boolean, _null: null, undef: undefined, ali: 'ali', five: 5, true: true}, + {num: 123, str: 'test', bool: false, _null: null, undef: undefined, ali: 'ali', five: 5, true: true}, + ), +); -// string -console.log(validator<{a: string}>({a: String}, {a: 'salam'})); +console.log('sanitize value test'); +console.log( + validator< + {num: number; str: string; bool: boolean; _null: null; undef: undefined; ali: 'ali'; five: 5; true: true} + >( + {num: Number, str: String, bool: Boolean, _null: null, undef: undefined, ali: 'ali', five: 5, true: true}, + {num: '123', str: 'test', bool: 'false', _null: null, undef: undefined, ali: 'ali', five: 5, true: true}, + ), +); -// nested object +console.log('nested value test'); console.log( - validator<{a: number; b: {c: boolean, d: {e: number}}}>( - {a: Number, b: {c: Boolean, d: {e: Number}}}, - {a: '2', b: {c: true, d: {e: 1}}}, + validator< + {a: {num: number; str: string; bool: boolean; _null: null; undef: undefined; ali: 'ali'; five: 5; true: true}} + >( + {a: {num: Number, str: String, bool: Boolean, _null: null, undef: undefined, ali: 'ali', five: 5, true: true}}, + {a: {num: '123', str: 'test', bool: 'false', _null: null, undef: undefined, ali: 'ali', five: 5, true: true}}, ), ); -// not valid +console.log('not valid test'); try { console.log( - validator<{a: number; b: {c: boolean, d: {e: number}}}>( - {a: Number, b: {c: Boolean, d: {e: Number}}}, - {a: '2', b: {c: true, d: {e: true}}}, + validator< + {num: number} + >( + {num: Number}, + {num: 'asd'}, ), ); + new Error('validator_not_work'); } -catch (error) { - console.log(error); -} - -try { - console.log(validator<{a: boolean}>({a: Boolean}, {a: 'test'})); -} -catch (error) { - console.log(error); +catch (err) { + console.log('test ok, error cause: ', (err as Error).cause); } From d49929fca0007aa94482010b7a6245f0bb360bc0 Mon Sep 17 00:00:00 2001 From: "S. Amir Mohammad Najafi" <njfamirm@gmail.com> Date: Tue, 3 Jan 2023 17:18:19 +0330 Subject: [PATCH 23/23] feat(validator): update demo --- core/validator/src/type.ts | 2 +- demo/validator/validator.ts | 109 ++++++++++++++++++++++++++++++++---- 2 files changed, 98 insertions(+), 13 deletions(-) diff --git a/core/validator/src/type.ts b/core/validator/src/type.ts index b03c3331b..d770f237a 100644 --- a/core/validator/src/type.ts +++ b/core/validator/src/type.ts @@ -11,5 +11,5 @@ export type JsonSchema = { }; export type ValidType = { - [key: string]: ValidType | string | number | boolean | null | undefined; + [key: string]: ValidType | string | number | boolean | null; }; diff --git a/demo/validator/validator.ts b/demo/validator/validator.ts index 07fbae639..f151fe63c 100644 --- a/demo/validator/validator.ts +++ b/demo/validator/validator.ts @@ -3,30 +3,30 @@ import {validator} from '@alwatr/validator'; console.log('basic test'); console.log( validator< - {num: number; str: string; bool: boolean; _null: null; undef: undefined; ali: 'ali'; five: 5; true: true} + {num: number; str: string; bool: boolean; _null: null; ali: 'ali'; five: 5; true: true} >( - {num: Number, str: String, bool: Boolean, _null: null, undef: undefined, ali: 'ali', five: 5, true: true}, - {num: 123, str: 'test', bool: false, _null: null, undef: undefined, ali: 'ali', five: 5, true: true}, + {num: Number, str: String, bool: Boolean, _null: null, ali: 'ali', five: 5, true: true}, + {num: 123, str: 'test', bool: false, _null: null, ali: 'ali', five: 5, true: true}, ), ); console.log('sanitize value test'); console.log( validator< - {num: number; str: string; bool: boolean; _null: null; undef: undefined; ali: 'ali'; five: 5; true: true} + {num: number; str: string; bool: boolean; _null: null; ali: 'ali'; five: 5; true: true} >( - {num: Number, str: String, bool: Boolean, _null: null, undef: undefined, ali: 'ali', five: 5, true: true}, - {num: '123', str: 'test', bool: 'false', _null: null, undef: undefined, ali: 'ali', five: 5, true: true}, + {num: Number, str: String, bool: Boolean, _null: null, ali: 'ali', five: 5, true: true}, + {num: '123', str: 'test', bool: 'false', _null: null, ali: 'ali', five: 5, true: true}, ), ); console.log('nested value test'); console.log( validator< - {a: {num: number; str: string; bool: boolean; _null: null; undef: undefined; ali: 'ali'; five: 5; true: true}} + {a: {num: number; str: string; bool: boolean; _null: null; ali: 'ali'; five: 5; true: true}} >( - {a: {num: Number, str: String, bool: Boolean, _null: null, undef: undefined, ali: 'ali', five: 5, true: true}}, - {a: {num: '123', str: 'test', bool: 'false', _null: null, undef: undefined, ali: 'ali', five: 5, true: true}}, + {a: {num: Number, str: String, bool: Boolean, _null: null, ali: 'ali', five: 5, true: true}}, + {a: {num: '123', str: 'test', bool: 'false', _null: null, ali: 'ali', five: 5, true: true}}, ), ); @@ -37,11 +37,96 @@ try { {num: number} >( {num: Number}, - {num: 'asd'}, + {num: 'test'}, ), ); - new Error('validator_not_work'); + throw new Error('validator_not_work'); } catch (err) { - console.log('test ok, error cause: ', (err as Error).cause); + if ((err as Error).message !== 'validator_not_work') { + console.log('test ok, error message `%s`, error cause: %s', (err as Error).message, (err as Error).cause); + } + else { + throw err; + } +} + +try { + console.log( + validator< + {num: boolean} + >( + {num: Boolean}, + {num: 'true'}, + ), + ); + throw new Error('validator_not_work'); +} +catch (err) { + if ((err as Error).message !== 'validator_not_work') { + console.log('test ok, error message `%s`, error cause: %s', (err as Error).message, (err as Error).cause); + } + else { + throw err; + } +} + +try { + console.log( + validator< + {num: null} + >( + {num: null}, + {num: 'test'}, + ), + ); + throw new Error('validator_not_work'); +} +catch (err) { + if ((err as Error).message !== 'validator_not_work') { + console.log('test ok, error message `%s`, error cause: %s', (err as Error).message, (err as Error).cause); + } + else { + throw err; + } +} + +try { + console.log( + validator< + {num: number} + >( + {num: Number}, + {num: 'test'}, + ), + ); + throw new Error('validator_not_work'); +} +catch (err) { + if ((err as Error).message !== 'validator_not_work') { + console.log('test ok, error message `%s`, error cause: %s', (err as Error).message, (err as Error).cause); + } + else { + throw err; + } +} + +try { + console.log( + validator< + {num: string} + >( + {num: 'test'}, + {num: 'tes'}, + ), + ); + throw new Error('validator_not_work'); +} +catch (err) { + if ((err as Error).message !== 'validator_not_work') { + console.log('test ok, error message `%s`, error cause: %s', (err as Error).message, (err as Error).cause); + } + else { + throw err; + } }