diff --git a/CHANGELOG.md b/CHANGELOG.md index a02e2a87ab6..d793b37b407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1776,3 +1776,43 @@ If there are any bugs, improvements, optimizations or any new feature proposal f - Dependencies updated ## [Unreleased] + +### Added + +#### web3-errors + +- `RpcErrorMessages` that contains mapping for standard RPC Errors and their messages. (#6230) + +#### web3-validator + +- Added `json-schema` as a main json schema type (#6264) + +### Fixed + +#### web3-core + +- Fixed the issue: "Version 4.x does not fire connected event for subscriptions. #6252". (#6262) + +#### web3-errors + +- Fixed: "'disconnect' in Eip1193 provider must emit ProviderRpcError #6003".(#6230) + +### Changed + +#### web3-core + +- No need to pass `CommonSubscriptionEvents &` at every child class of `Web3Subscription` (#6262) +- Implementation of `_processSubscriptionResult` and `_processSubscriptionError` has been written in the base class `Web3Subscription` and maid `public`. (#6262) +- A new optional protected method `formatSubscriptionResult` could be used to customize data formatting instead of re-implementing `_processSubscriptionResult`. (#6262) +- No more needed to pass `CommonSubscriptionEvents & ` for the first generic parameter of `Web3Subscription` when inheriting from it. (#6262) + +#### web3-validator + +- Replace `is-my-json-valid` with `zod` dependency. Related code was changed (#6264) +- Types `ValidationError` and `JsonSchema` were changed (#6264) + +### Removed + +#### web3-validator + +- Type `RawValidationError` was removed (#6264) diff --git a/packages/web3-errors/src/errors/transaction_errors.ts b/packages/web3-errors/src/errors/transaction_errors.ts index b19341111eb..2b7d7791cc8 100644 --- a/packages/web3-errors/src/errors/transaction_errors.ts +++ b/packages/web3-errors/src/errors/transaction_errors.ts @@ -555,14 +555,7 @@ export class InvalidPropertiesForTransactionTypeError extends BaseWeb3Error { txType: '0x0' | '0x1' | '0x2', ) { const invalidPropertyNames: string[] = []; - validationError.forEach(error => - invalidPropertyNames.push( - // These errors are erroneously reported, error - // has type Web3ValidationErrorObject, but eslint doesn't recognize it - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - (error.keyword.match(/data.(.+)/) as string[])[1], - ), - ); + validationError.forEach(error => invalidPropertyNames.push(error.keyword)); super( `The following properties are invalid for the transaction type ${txType}: ${invalidPropertyNames.join( ', ', diff --git a/packages/web3-errors/test/unit/errors.test.ts b/packages/web3-errors/test/unit/errors.test.ts index a6bf29bdb82..91772390af7 100644 --- a/packages/web3-errors/test/unit/errors.test.ts +++ b/packages/web3-errors/test/unit/errors.test.ts @@ -198,7 +198,7 @@ describe('errors', () => { new transactionErrors.InvalidPropertiesForTransactionTypeError( [ { - keyword: 'data.property', + keyword: 'property', instancePath: '', schemaPath: '', params: {}, diff --git a/packages/web3-eth-contract/test/fixtures/contracts/SimpleOverloaded.sol b/packages/web3-eth-contract/test/fixtures/contracts/SimpleOverloaded.sol index 515719211ae..3aa74fa05d8 100644 --- a/packages/web3-eth-contract/test/fixtures/contracts/SimpleOverloaded.sol +++ b/packages/web3-eth-contract/test/fixtures/contracts/SimpleOverloaded.sol @@ -16,10 +16,11 @@ contract SimpleOverload { return secret + numToAdd; } - function getSecret( - uint256 numToAdd, - string calldata _someString - ) public view returns (uint256, string memory) { + function getSecret(uint256 numToAdd, string calldata _someString) + public + view + returns (uint256, string memory) + { return (secret + numToAdd, string.concat(someString, _someString)); } @@ -37,14 +38,14 @@ contract SimpleOverload { } function multicall(bytes[] calldata datas) external { - for (uint i = 0; i < datas.length; i++) { + for (uint256 i = 0; i < datas.length; i++) { address(this).call(datas[i]); } } - function multicall(uint deadline, bytes[] calldata datas) external { + function multicall(uint256 deadline, bytes[] calldata datas) external { require(block.timestamp <= deadline); - for (uint i = 0; i < datas.length; i++) { + for (uint256 i = 0; i < datas.length; i++) { address(this).call(datas[i]); } } diff --git a/packages/web3-eth/test/integration/block/rpc.getBlock.test.ts b/packages/web3-eth/test/integration/block/rpc.getBlock.test.ts index e967390ec4a..63f0f3c402f 100644 --- a/packages/web3-eth/test/integration/block/rpc.getBlock.test.ts +++ b/packages/web3-eth/test/integration/block/rpc.getBlock.test.ts @@ -131,29 +131,7 @@ describe('rpc with block', () => { b.totalDifficulty = '0x0'; } - // just fix tests for oneOf validation - // @TODO: when use schemasafe remove this fix - const schema = JSON.parse(JSON.stringify(blockSchema)); - if ( - b.transactions && - Array.isArray(b.transactions) && - typeof b.transactions[0] === 'object' - ) { - // eslint-disable-next-line prefer-destructuring - schema.properties.transactions = schema.properties.transactions.oneOf[0]; - // @ts-expect-error add leading zeros remove when fixes https://github.com/web3/web3.js/issues/6060 - b.transactions[0].s = `0x${`000000000000000${b?.transactions[0]?.s.slice(2)}`.slice( - -64, - )}`; - // @ts-expect-error add leading zeros remove when fixes https://github.com/web3/web3.js/issues/6060 - b.transactions[0].r = `0x${`000000000000000${b?.transactions[0]?.r.slice(2)}`.slice( - -64, - )}`; - } else { - // eslint-disable-next-line prefer-destructuring - schema.properties.transactions = schema.properties.transactions.oneOf[1]; - } - expect(validator.validateJSONSchema(schema, b)).toBeUndefined(); + expect(validator.validateJSONSchema(blockSchema, b)).toBeUndefined(); if (hydrated && b.transactions?.length > 0) { // eslint-disable-next-line jest/no-conditional-expect expect(b.transactions).toBeInstanceOf(Array); diff --git a/packages/web3-utils/test/fixtures/converters.ts b/packages/web3-utils/test/fixtures/converters.ts index ed3f7bb9274..42d992ec34a 100644 --- a/packages/web3-utils/test/fixtures/converters.ts +++ b/packages/web3-utils/test/fixtures/converters.ts @@ -27,21 +27,21 @@ export const bytesToHexValidData: [Bytes, HexString][] = [ ]; export const bytesToHexInvalidData: [any, string][] = [ - [[9.5, 12.9], 'value "9.5,12.9" at "/0" must pass "bytes" validation'], - [[-72, 12], 'value "-72,12" at "/0" must pass "bytes" validation'], - [[567, 10098], 'value "567,10098" at "/0" must pass "bytes" validation'], - [[786, 12, 34, -2, 3], 'value "786,12,34,-2,3" at "/0" must pass "bytes" validation'], + [[9.5, 12.9], 'value "[9.5,12.9]" at "/0" must pass "bytes" validation'], + [[-72, 12], 'value "[-72,12]" at "/0" must pass "bytes" validation'], + [[567, 10098], 'value "[567,10098]" at "/0" must pass "bytes" validation'], + [[786, 12, 34, -2, 3], 'value "[786,12,34,-2,3]" at "/0" must pass "bytes" validation'], ['0x0c1g', 'value "0x0c1g" at "/0" must pass "bytes" validation'], ['0c1g', 'value "0c1g" at "/0" must pass "bytes" validation'], ['0x123', 'value "0x123" at "/0" must pass "bytes" validation'], ['data', 'value "data" at "/0" must pass "bytes" validation'], [12, 'value "12" at "/0" must pass "bytes" validation'], - [['string'], 'value "string" at "/0" must pass "bytes" validation'], + [['string'], 'value "["string"]" at "/0" must pass "bytes" validation'], // Using "null" value intentionally for validation // eslint-disable-next-line no-null/no-null - [null, 'value at "/0" must pass "bytes" validation'], + [null, 'value "null" at "/0" must pass "bytes" validation'], [undefined, 'Web3 validator found 1 error[s]:\nvalue at "/0" is required'], - [{}, 'value "[object Object]" at "/0" must pass "bytes" validation'], + [{}, 'value "{}" at "/0" must pass "bytes" validation'], ['1', 'value "1" at "/0" must pass "bytes" validation'], ['0', 'value "0" at "/0" must pass "bytes" validation'], ]; @@ -56,21 +56,21 @@ export const hexToBytesValidData: [HexString, Uint8Array][] = [ ]; export const hexToBytesInvalidData: [any, string][] = [ - [[9.5, 12.9], 'value "9.5,12.9" at "/0" must pass "bytes" validation'], - [[-72, 12], 'value "-72,12" at "/0" must pass "bytes" validation'], - [[567, 10098], 'value "567,10098" at "/0" must pass "bytes" validation'], - [[786, 12, 34, -2, 3], 'value "786,12,34,-2,3" at "/0" must pass "bytes" validation'], + [[9.5, 12.9], 'value "[9.5,12.9]" at "/0" must pass "bytes" validation'], + [[-72, 12], 'value "[-72,12]" at "/0" must pass "bytes" validation'], + [[567, 10098], 'value "[567,10098]" at "/0" must pass "bytes" validation'], + [[786, 12, 34, -2, 3], 'value "[786,12,34,-2,3]" at "/0" must pass "bytes" validation'], ['0x0c1g', 'value "0x0c1g" at "/0" must pass "bytes" validation'], ['0c1g', 'value "0x0c1g" at "/0" must pass "bytes" validation'], ['0x123', 'value "0x123" at "/0" must pass "bytes" validation'], ['data', 'value "0xdata" at "/0" must pass "bytes" validation'], [12, 'value "12" at "/0" must pass "bytes" validation'], - [['string'], 'value "string" at "/0" must pass "bytes" validation'], + [['string'], 'value "["string"]" at "/0" must pass "bytes" validation'], // Using "null" value intentionally for validation // eslint-disable-next-line no-null/no-null - [null, 'Web3 validator found 1 error[s]:\nvalue at "/0" must pass "bytes" validation'], + [null, 'Web3 validator found 1 error[s]:\nvalue "null" at "/0" must pass "bytes" validation'], [undefined, 'Web3 validator found 1 error[s]:\nvalue at "/0" is required'], - [{}, 'value "[object Object]" at "/0" must pass "bytes" validation'], + [{}, 'value "{}" at "/0" must pass "bytes" validation'], ]; export const numberToHexValidData: [Numbers, HexString][] = [ @@ -109,9 +109,9 @@ export const numberToHexInvalidData: [any, string][] = [ ['122g', 'value "122g" at "/0" must pass "int" validation'], // Using "null" value intentionally for validation // eslint-disable-next-line no-null/no-null - [null, 'value at "/0" must pass "int" validation'], + [null, 'value "null" at "/0" must pass "int" validation'], [undefined, 'Web3 validator found 1 error[s]:\nvalue at "/0" is required'], - [{}, 'value "[object Object]" at "/0" must pass "int" validation'], + [{}, 'value "{}" at "/0" must pass "int" validation'], ]; export const hexToNumberValidData: [HexString, Numbers][] = [ @@ -154,9 +154,9 @@ export const utf8ToHexInvalidData: [any, string][] = [ [BigInt(12), 'value "12" at "/0" must pass "string" validation'], // Using "null" value intentionally for validation // eslint-disable-next-line no-null/no-null - [null, 'value at "/0" must pass "string" validation'], + [null, 'value "null" at "/0" must pass "string" validation'], [undefined, 'Web3 validator found 1 error[s]:\nvalue at "/0" is required'], - [{}, 'value "[object Object]" at "/0" must pass "string" validation'], + [{}, 'value "{}" at "/0" must pass "string" validation'], [true, 'value "true" at "/0" must pass "string" validation'], [false, 'value "false" at "/0" must pass "string" validation'], ]; @@ -185,9 +185,9 @@ export const hexToUtf8InvalidData: [any, string][] = [ ], // Using "null" value intentionally for validation // eslint-disable-next-line no-null/no-null - [null, 'value at "/0" must pass "bytes" validation'], + [null, 'value "null" at "/0" must pass "bytes" validation'], [undefined, 'Web3 validator found 1 error[s]:\nvalue at "/0" is required'], - [{}, 'value "[object Object]" at "/0" must pass "bytes" validation'], + [{}, 'value "{}" at "/0" must pass "bytes" validation'], [true, 'value "true" at "/0" must pass "bytes" validation'], ]; @@ -302,9 +302,9 @@ export const fromWeiInvalidData: [[any, any], string][] = [ export const toWeiInvalidData: [[any, any], string][] = [ // Using "null" value intentionally for validation // eslint-disable-next-line no-null/no-null - [[null, 'kwei'], 'value at "/0" must pass "number" validation'], + [[null, 'kwei'], 'value "null" at "/0" must pass "number" validation'], [[undefined, 'kwei'], 'Web3 validator found 1 error[s]:\nvalue at "/0" is required'], - [[{}, 'kwei'], 'value "[object Object]" at "/0" must pass "number" validation'], + [[{}, 'kwei'], 'value "{}" at "/0" must pass "number" validation'], [['data', 'kwei'], 'value "data" at "/0" must pass "number" validation'], [['1234', 'uwei'], 'Invalid value given "uwei". Error: invalid unit.'], ]; diff --git a/packages/web3-utils/test/fixtures/string_manipulation.ts b/packages/web3-utils/test/fixtures/string_manipulation.ts index e2679530a39..be5b298c803 100644 --- a/packages/web3-utils/test/fixtures/string_manipulation.ts +++ b/packages/web3-utils/test/fixtures/string_manipulation.ts @@ -34,10 +34,13 @@ export const padLeftData: [[Numbers, number, string], HexString][] = [ export const padInvalidData: [[any, number, string], string][] = [ [[9.5, 64, 'f'], 'value "9.5" at "/0" must pass "int" validation'], // Using "null" value intentionally for validation - // eslint-disable-next-line no-null/no-null - [[null, 8, '0'], 'Web3 validator found 1 error[s]:\nvalue at "/0" must pass "int" validation'], + [ + // eslint-disable-next-line no-null/no-null + [null, 8, '0'], + 'Web3 validator found 1 error[s]:\nvalue "null" at "/0" must pass "int" validation', + ], [[undefined, 8, '0'], 'Web3 validator found 1 error[s]:\nvalue at "/0" is required'], - [[{}, 3, 'f'], 'value "[object Object]" at "/0" must pass "int" validation'], + [[{}, 3, 'f'], 'value "{}" at "/0" must pass "int" validation'], ]; export const padRightData: [[Numbers, number, string], HexString][] = [ diff --git a/packages/web3-validator/CHANGELOG.md b/packages/web3-validator/CHANGELOG.md index 2eb7361d70a..e38bdf709a5 100644 --- a/packages/web3-validator/CHANGELOG.md +++ b/packages/web3-validator/CHANGELOG.md @@ -121,3 +121,16 @@ Documentation: - Dependencies updated ## [Unreleased] + +### Changed + +- Replace `is-my-json-valid` with `zod` dependency. Related code was changed (#6264) +- Types `ValidationError` and `JsonSchema` were changed (#6264) + +### Removed + +- Type `RawValidationError` was removed (#6264) + +### Added + +- Added `json-schema` as a main json schema type (#6264) diff --git a/packages/web3-validator/package.json b/packages/web3-validator/package.json index 85dc36a8242..f03918f7fee 100644 --- a/packages/web3-validator/package.json +++ b/packages/web3-validator/package.json @@ -46,10 +46,10 @@ }, "dependencies": { "ethereum-cryptography": "^2.0.0", - "is-my-json-valid": "^2.20.6", "util": "^0.12.5", "web3-errors": "^1.0.2", - "web3-types": "^1.0.2" + "web3-types": "^1.0.2", + "zod": "^3.21.4" }, "devDependencies": { "@types/jest": "^28.1.6", diff --git a/packages/web3-validator/src/errors.ts b/packages/web3-validator/src/errors.ts index 0e8e4125c3a..8faf361248b 100644 --- a/packages/web3-validator/src/errors.ts +++ b/packages/web3-validator/src/errors.ts @@ -18,24 +18,7 @@ along with web3.js. If not, see . import { BaseWeb3Error, ERR_VALIDATION } from 'web3-errors'; import { Web3ValidationErrorObject } from 'web3-types'; -import { isNullish } from './validation/object.js'; - const errorFormatter = (error: Web3ValidationErrorObject): string => { - if (error.message && error.instancePath && error.params && !isNullish(error.params.value)) { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - return `value "${(error.params as { value: unknown }).value}" at "${error.instancePath}" ${ - error.message - }`; - } - - if (error.message && error.instancePath) { - return `value at "${error.instancePath}" ${error.message}`; - } - - if (error.instancePath) { - return `value at "${error.instancePath}" caused unspecified error`; - } - if (error.message) { return error.message; } @@ -58,7 +41,6 @@ export class Web3ValidatorError extends BaseWeb3Error { } private _compileErrors(): string[] { - const errorMsgs = this.errors.map(errorFormatter); - return errorMsgs; + return this.errors.map(errorFormatter); } } diff --git a/packages/web3-validator/src/formats.ts b/packages/web3-validator/src/formats.ts index 086b291fea4..da0c0ba6564 100644 --- a/packages/web3-validator/src/formats.ts +++ b/packages/web3-validator/src/formats.ts @@ -17,8 +17,7 @@ along with web3.js. If not, see . import { Filter } from 'web3-types'; import { ValidInputTypes } from './types.js'; import { isAddress } from './validation/address.js'; -import { isBlockNumber,isBlockNumberOrTag, - isBlockTag, } from './validation/block.js'; +import { isBlockNumber, isBlockNumberOrTag, isBlockTag } from './validation/block.js'; import { isBloom } from './validation/bloom.js'; import { isBoolean } from './validation/boolean.js'; import { isBytes } from './validation/bytes.js'; @@ -52,5 +51,6 @@ for (let size = 1; size <= 32; size += 1) { formats[`bytes${size}`] = data => isBytes(data as ValidInputTypes | Uint8Array | number[], { size }); } +formats.bytes256 = formats.bytes; export default formats; diff --git a/packages/web3-validator/src/types.ts b/packages/web3-validator/src/types.ts index 5610dd72b24..1a179e83b58 100644 --- a/packages/web3-validator/src/types.ts +++ b/packages/web3-validator/src/types.ts @@ -16,7 +16,7 @@ along with web3.js. If not, see . */ import { AbiParameter } from 'web3-types'; -import { ValidationError } from 'is-my-json-valid'; +import { ZodIssueBase } from 'zod'; export type ValidInputTypes = Uint8Array | bigint | string | number | boolean; export type EthBaseTypes = 'bool' | 'bytes' | 'string' | 'uint' | 'int' | 'address' | 'tuple'; @@ -44,12 +44,7 @@ export type EthExtendedTypes = export type FullValidationSchema = ReadonlyArray; export type ShortValidationSchema = ReadonlyArray< - | string - | EthBaseTypes - | EthExtendedTypes - | EthBaseTypesWithMeta - | EthBaseTypesWithMeta - | ShortValidationSchema + string | EthBaseTypes | EthExtendedTypes | EthBaseTypesWithMeta | ShortValidationSchema >; export type ValidationSchemaInput = FullValidationSchema | ShortValidationSchema; @@ -57,9 +52,15 @@ export type Web3ValidationOptions = { readonly silent: boolean; }; -// is-my-json-valid types export type Json = string | number | boolean | Array | { [id: string]: Json }; +export type ValidationError = ZodIssueBase; + +export interface Validate { + (value: Json): boolean; + errors?: ValidationError[]; +} + export type Schema = { // version $schema?: string; @@ -141,12 +142,5 @@ export type Schema = { readonly eth?: string; items?: Schema | Schema[]; }; -export interface Validate { - (value: Json): boolean; - errors?: ValidationError[]; -} -export type RawValidationError = ValidationError & { - schemaPath: string[]; -}; export type JsonSchema = Schema; diff --git a/packages/web3-validator/src/utils.ts b/packages/web3-validator/src/utils.ts index b6e233c98b8..93191c2fb1d 100644 --- a/packages/web3-validator/src/utils.ts +++ b/packages/web3-validator/src/utils.ts @@ -20,7 +20,6 @@ import { VALID_ETH_BASE_TYPES } from './constants.js'; import { FullValidationSchema, JsonSchema, - Schema, ShortValidationSchema, ValidationSchemaInput, ValidInputTypes, @@ -78,7 +77,7 @@ export const parseBaseType = ( const convertEthType = ( type: string, - parentSchema: Schema = {}, + parentSchema: JsonSchema = {}, ): { format?: string; required?: boolean } => { const typePropertyPresent = Object.keys(parentSchema).includes('type'); diff --git a/packages/web3-validator/src/validation/boolean.ts b/packages/web3-validator/src/validation/boolean.ts index b16c7aab502..17593c8312c 100644 --- a/packages/web3-validator/src/validation/boolean.ts +++ b/packages/web3-validator/src/validation/boolean.ts @@ -35,9 +35,6 @@ export const isBoolean = (value: ValidInputTypes) => { return value === '0x1' || value === '0x0'; } - if (typeof value === 'number') { - return value === 1 || value === 0; - } - - return false; + // type === number + return value === 1 || value === 0; }; diff --git a/packages/web3-validator/src/validator.ts b/packages/web3-validator/src/validator.ts index f57cd639480..805b2665ce5 100644 --- a/packages/web3-validator/src/validator.ts +++ b/packages/web3-validator/src/validator.ts @@ -16,53 +16,93 @@ along with web3.js. If not, see . */ import { Web3ValidationErrorObject } from 'web3-types'; -import { toHex, utf8ToBytes } from 'ethereum-cryptography/utils.js'; -import { blake2b } from 'ethereum-cryptography/blake2b.js'; -import validator from 'is-my-json-valid'; -import formats from './formats.js'; +import { z, ZodType, ZodIssue, ZodIssueCode, ZodTypeAny } from 'zod'; + +import { RawCreateParams } from 'zod/lib/types'; import { Web3ValidatorError } from './errors.js'; -import { Validate, Json, Schema, RawValidationError } from './types.js'; +import { Json, JsonSchema } from './types.js'; +import formats from './formats'; + +const convertToZod = (schema: JsonSchema): ZodType => { + if ((!schema?.type || schema?.type === 'object') && schema?.properties) { + const obj: { [key: string]: ZodType } = {}; + for (const name of Object.keys(schema.properties)) { + const zItem = convertToZod(schema.properties[name]); + if (zItem) { + obj[name] = zItem; + } + } + + if (Array.isArray(schema.required)) { + return z + .object(obj) + .partial() + .required(schema.required.reduce((acc, v: string) => ({ ...acc, [v]: true }), {})); + } + return z.object(obj).partial(); + } + + if (schema?.type === 'array' && schema?.items) { + if (Array.isArray(schema.items) && schema.items.length > 0) { + const arr: Partial<[ZodTypeAny, ...ZodTypeAny[]]> = []; + for (const item of schema.items) { + const zItem = convertToZod(item); + if (zItem) { + arr.push(zItem); + } + } + return z.tuple(arr as [ZodTypeAny, ...ZodTypeAny[]]); + } + return z.array(convertToZod(schema.items as JsonSchema)); + } + + if (schema.oneOf && Array.isArray(schema.oneOf)) { + return z.union( + schema.oneOf.map(oneOfSchema => convertToZod(oneOfSchema)) as [ + ZodTypeAny, + ZodTypeAny, + ...ZodTypeAny[], + ], + ); + } + + if (schema?.format) { + return z.any().refine(formats[schema.format], (value: unknown) => ({ + params: { value, format: schema.format }, + })); + } + + if ( + schema?.type && + schema?.type !== 'object' && + typeof (z as unknown as { [key: string]: (params?: RawCreateParams) => ZodType })[ + String(schema.type) + ] === 'function' + ) { + return (z as unknown as { [key: string]: (params?: RawCreateParams) => ZodType })[ + String(schema.type) + ](); + } + return z.object({ data: z.any() }).partial(); +}; export class Validator { // eslint-disable-next-line no-use-before-define private static validatorInstance?: Validator; + // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function - private constructor() {} public static factory(): Validator { if (!Validator.validatorInstance) { Validator.validatorInstance = new Validator(); } return Validator.validatorInstance; } - private readonly _schemas: Map = new Map(); - public getSchema(key: string) { - return this._schemas.get(key); - } - - public addSchema(key: string, schema: Schema) { - this._schemas.set(key, this.createValidator(schema)); - } - // eslint-disable-next-line class-methods-use-this - private createValidator(schema: Schema): Validate { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - // @ts-expect-error validator params correction - return validator(schema, { - formats, - greedy: true, - verbose: true, - additionalProperties: false, - }) as Validate; - } - - public validate(schema: Schema, data: Json, options?: { silent?: boolean }) { - const localValidate = this.getOrCreateValidator(schema); - if (!localValidate(data)) { - const errors = this.convertErrors( - localValidate.errors as RawValidationError[], - schema, - data, - ); + public validate(schema: JsonSchema, data: Json, options?: { silent?: boolean }) { + const zod = convertToZod(schema); + const result = zod.safeParse(data); + if (!result.success) { + const errors = this.convertErrors(result.error?.issues ?? []); if (errors) { if (options?.silent) { return errors; @@ -72,129 +112,57 @@ export class Validator { } return undefined; } - private convertErrors( - errors: RawValidationError[] | undefined, - schema: Schema, - data: Json, - ): Web3ValidationErrorObject[] | undefined { + // eslint-disable-next-line class-methods-use-this + private convertErrors(errors: ZodIssue[] | undefined): Web3ValidationErrorObject[] | undefined { if (errors && Array.isArray(errors) && errors.length > 0) { - return errors.map((error: RawValidationError) => { + return errors.map((error: ZodIssue) => { let message; let keyword; let params; let schemaPath; - schemaPath = Array.isArray(error.schemaPath) - ? error.schemaPath.slice(1).join('/') - : ''; + schemaPath = error.path.join('/'); - const { field } = error; - const _instancePath = - schemaPath || - // eslint-disable-next-line no-useless-escape - (field?.length >= 4 ? `${field.slice(4).replace(/\"|\[|\]/g, '')}` : '/'); + const field = String(error.path[error.path.length - 1]); + const instancePath = error.path.join('/'); + if (error.code === ZodIssueCode.too_big) { + keyword = 'maxItems'; + schemaPath = `${instancePath}/maxItems`; + params = { limit: error.maximum }; + message = `must NOT have more than ${error.maximum} items`; + } else if (error.code === ZodIssueCode.too_small) { + keyword = 'minItems'; + schemaPath = `${instancePath}/minItems`; + params = { limit: error.minimum }; + message = `must NOT have fewer than ${error.minimum} items`; + } else if (error.code === ZodIssueCode.custom) { + const { value, format } = (error.params ?? {}) as { + value: unknown; + format: string; + }; - const instancePath = _instancePath ? `/${_instancePath}` : ''; - if (error?.message === 'has less items than allowed') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const schemaData = this.getObjectValueByPath(schema, schemaPath); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (schemaData.minItems) { - keyword = 'minItems'; - schemaPath = `${schemaPath}/minItems`; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - params = { limit: schemaData.minItems }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions - message = `must NOT have fewer than ${schemaData.minItems} items`; - } - } else if (error?.message === 'has more items than allowed') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const schemaData = this.getObjectValueByPath(schema, schemaPath); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (schemaData.maxItems) { - keyword = 'maxItems'; - schemaPath = `${schemaPath}/maxItems`; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - params = { limit: schemaData.maxItems }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions - message = `must NOT have more than ${schemaData.maxItems} items`; - } - } else if ( - error?.message.startsWith('must be') && - error?.message.endsWith('format') - ) { - const formatName = error?.message.split(' ')[2]; - if (formatName) { - message = `must pass "${formatName}" validation`; + if (typeof value === 'undefined') { + message = `value at "/${schemaPath}" is required`; + } else { + message = `value "${ + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + typeof value === 'object' ? JSON.stringify(value) : value + }" at "/${schemaPath}" must pass "${format}" validation`; } + + params = { value }; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const dataValue = this.getObjectValueByPath(data as object, instancePath); + return { - keyword: keyword ?? error.field, - instancePath, - schemaPath: `#${schemaPath}`, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - params: params ?? { value: dataValue }, + keyword: keyword ?? field, + instancePath: instancePath ? `/${instancePath}` : '', + schemaPath: schemaPath ? `#${schemaPath}` : '#', + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + params: params ?? { value: error.message }, message: message ?? error.message, } as Web3ValidationErrorObject; }); } return undefined; } - - public getOrCreateValidator(schema: Schema): Validate { - const key = Validator.getKey(schema); - let _validator = this.getSchema(key); - if (!_validator) { - this.addSchema(key, schema); - _validator = this.getSchema(key); - } - return _validator!; - } - - public static getKey(schema: Schema) { - return toHex(blake2b(utf8ToBytes(JSON.stringify(schema)))); - } - private getObjectValueByPath(obj: object, pointer: string, objpath?: object[]) { - try { - if (typeof obj !== 'object') throw new Error('Invalid input object'); - if (typeof pointer !== 'string') throw new Error('Invalid JSON pointer'); - const parts = pointer.split('/'); - if (!['', '#'].includes(parts.shift() as string)) { - throw new Error('Invalid JSON pointer'); - } - if (parts.length === 0) return obj; - - let curr: any = obj; - for (const part of parts) { - if (typeof part !== 'string') throw new Error('Invalid JSON pointer'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument - if (objpath) objpath.push(curr); // does not include target itself, but includes head - const prop = this.untilde(part); - if (typeof curr !== 'object') return undefined; - if (!Object.prototype.hasOwnProperty.call(curr, prop)) return undefined; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - curr = curr[prop]; - } - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return curr; - } catch (e) { - return ''; - } - } - // eslint-disable-next-line class-methods-use-this - private untilde(string: string) { - if (!string.includes('~')) return string; - return string.replace(/~[01]/g, match => { - switch (match) { - case '~1': - return '/'; - case '~0': - return '~'; - default: - throw new Error('Unreachable'); - } - }); - } } diff --git a/packages/web3-validator/src/web3_validator.ts b/packages/web3-validator/src/web3_validator.ts index f56cc36e13f..a165a36bef1 100644 --- a/packages/web3-validator/src/web3_validator.ts +++ b/packages/web3-validator/src/web3_validator.ts @@ -23,11 +23,9 @@ import { Web3ValidatorError } from './errors.js'; export class Web3Validator { private readonly _validator: Validator; - public constructor() { this._validator = Validator.factory(); } - public validateJSONSchema( schema: object, data: object, @@ -35,7 +33,6 @@ export class Web3Validator { ): Web3ValidationErrorObject[] | undefined { return this._validator.validate(schema, data as Json, options); } - public validate( schema: ValidationSchemaInput, data: ReadonlyArray, diff --git a/packages/web3-validator/test/fixtures/errors.ts b/packages/web3-validator/test/fixtures/errors.ts index dca0a2bc7e4..9b92474d582 100644 --- a/packages/web3-validator/test/fixtures/errors.ts +++ b/packages/web3-validator/test/fixtures/errors.ts @@ -39,25 +39,6 @@ export const fullErrors: any[] = [ }, ]; -export const fullErrorsWithInstance: any[] = [ - { - message: 'must pass "uint" validation', - keyword: 'eth', - params: { value: -1 }, - instancePath: '/0', - schemaPath: '#/items/0/eth', - }, -]; - -export const errorsWithInstanceNoParams: any[] = [ - { - message: 'must pass "uint" validation', - keyword: 'eth', - instancePath: '/0', - schemaPath: '#/items/0/eth', - }, -]; - export const errorsWithInstanceNoParamsNoMessage: any[] = [ { keyword: 'eth', diff --git a/packages/web3-validator/test/unit/convert_to_zod.test.ts b/packages/web3-validator/test/unit/convert_to_zod.test.ts new file mode 100644 index 00000000000..358e6a8b801 --- /dev/null +++ b/packages/web3-validator/test/unit/convert_to_zod.test.ts @@ -0,0 +1,79 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +import { Web3Validator } from '../../src/web3_validator'; + +describe('convert-to-zod', () => { + let validator: Web3Validator; + + beforeAll(() => { + validator = new Web3Validator(); + }); + + it('simple array', () => { + expect(() => + validator.validateJSONSchema( + { + type: 'array', + items: { type: 'string' }, + }, + ['a', 'b', 'c'], + ), + ).not.toThrow(); + }); + + it('simple object', () => { + expect(() => + validator.validateJSONSchema( + { + type: 'object', + properties: { + a: { type: 'number' }, + }, + }, + { a: 1 }, + ), + ).not.toThrow(); + }); + it('incorrect type to object', () => { + expect(() => + validator.validateJSONSchema( + { + type: 'object2', + properties: { + a: { type: 'number' }, + }, + }, + { a: 1 }, + ), + ).not.toThrow(); + }); + it('format with undefined value', () => { + expect(() => + validator.validateJSONSchema( + { + required: ['a'], + type: 'object', + properties: { + a: { format: 'uint' }, + }, + }, + { a: undefined }, + ), + ).toThrow(); + }); +}); diff --git a/packages/web3-validator/test/unit/error.test.ts b/packages/web3-validator/test/unit/error.test.ts index 74accbc8ed1..14cf631c7c5 100644 --- a/packages/web3-validator/test/unit/error.test.ts +++ b/packages/web3-validator/test/unit/error.test.ts @@ -19,8 +19,6 @@ import { Web3ValidationErrorObject } from 'web3-types'; import { Web3ValidatorError } from '../../src/errors'; import { fullErrors, - fullErrorsWithInstance, - errorsWithInstanceNoParams, errorsWithInstanceNoParamsNoMessage, unspecifiedErrors, } from '../fixtures/errors'; @@ -33,32 +31,6 @@ describe('Web3ValidationError', () => { expect(validationError.message).toBe(`Web3 validator found 1 error[s]:\n${error.message}`); }); - it.each(fullErrorsWithInstance)( - 'errors with message, instance and params', - (error: Web3ValidationErrorObject) => { - const validationError = new Web3ValidatorError([error]); - - expect(validationError).toBeInstanceOf(Web3ValidatorError); - expect(validationError.message).toBe( - `Web3 validator found 1 error[s]:\nvalue "${ - (error.params as { value: unknown }).value - }" at "${error.instancePath}" ${error.message}`, - ); - }, - ); - - it.each(errorsWithInstanceNoParams)( - 'errors with only message and instance', - (error: Web3ValidationErrorObject) => { - const validationError = new Web3ValidatorError([error]); - - expect(validationError).toBeInstanceOf(Web3ValidatorError); - expect(validationError.message).toBe( - `Web3 validator found 1 error[s]:\nvalue at "${error.instancePath}" ${error.message}`, - ); - }, - ); - it.each(errorsWithInstanceNoParamsNoMessage)( 'errors with only instance', (error: Web3ValidationErrorObject) => { @@ -66,7 +38,7 @@ describe('Web3ValidationError', () => { expect(validationError).toBeInstanceOf(Web3ValidatorError); expect(validationError.message).toBe( - `Web3 validator found 1 error[s]:\nvalue at "${error.instancePath}" caused unspecified error`, + `Web3 validator found 1 error[s]:\nunspecified error`, ); }, ); diff --git a/packages/web3-validator/test/unit/load.test.ts b/packages/web3-validator/test/unit/load.test.ts new file mode 100644 index 00000000000..919214ef7af --- /dev/null +++ b/packages/web3-validator/test/unit/load.test.ts @@ -0,0 +1,184 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +import { Web3Validator } from '../../src/web3_validator'; +import { Json, JsonSchema, ValidationSchemaInput } from '../..'; + +const abi = [ + { indexed: true, internalType: 'address', name: 'from', type: 'address' }, + { indexed: true, internalType: 'address', name: 'to', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'value', type: 'uint256' }, +]; + +const abiJsonSchema = { + type: 'array', + items: [ + { name: 'from', format: 'address' }, + { name: 'to', format: 'address' }, + { name: 'value', format: 'uint256' }, + ], +}; + +const abiData = [ + '0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b', + '0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b', + '0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b', +]; + +const simpleSchema = { + type: 'object', + required: ['blockHash', 'blockNumber', 'from', 'to', 'data'], + properties: { + blockHash: { + format: 'bytes32', + }, + blockNumber: { + format: 'uint', + }, + from: { + format: 'address', + }, + to: { + oneOf: [{ format: 'address' }, { type: 'null' }], + }, + data: { + format: 'bytes', + }, + }, +}; + +const simpleData = { + blockHash: '0x0dec0518fa672a70027b04c286582e543ab17319fbdd384fa7bc8f3d5a542c0b', + blockNumber: BigInt(2), + from: '0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b', + to: '0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b', + data: '0xafea', +} as unknown as ValidationSchemaInput; + +const createHugeSchema = ( + schema: JsonSchema, + data: Json, + n = 3, +): { schema: JsonSchema; data: Json } => { + if (n > 0) { + const { data: resultData, schema: resultSchema } = createHugeSchema( + { ...simpleSchema } as JsonSchema, + { ...simpleData } as Json, + n - 1, + ); + return { + data: { ...(data as unknown as object), simple: resultData }, + schema: { ...schema, properties: { ...schema.properties, simple: resultSchema } }, + }; + } + return { + schema, + data, + }; +}; + +const { schema: hugeSchema, data: hugeData } = createHugeSchema( + { ...simpleSchema } as JsonSchema, + { ...simpleData } as Json, + 500, +); + +const { schema: hugeSchema1000, data: hugeData1000 } = createHugeSchema( + { ...simpleSchema } as JsonSchema, + { ...simpleData } as Json, + 1000, +); +describe('instance of validator', () => { + let validator: Web3Validator; + beforeAll(() => { + validator = new Web3Validator(); + }); + + it('huge schema', () => { + let t = 0; + expect(() => { + const t1 = Number(new Date()); + validator.validateJSONSchema(hugeSchema, hugeData as object); + t = Number(new Date()) - t1; + }).not.toThrow(); + expect(t).toBeLessThan(6000); + expect(t).toBeGreaterThan(0); + }); + + it('huge schema 1000', () => { + let t = 0; + expect(() => { + const t1 = Number(new Date()); + validator.validateJSONSchema(hugeSchema1000, hugeData1000 as object); + t = Number(new Date()) - t1; + }).not.toThrow(); + expect(t).toBeLessThan(6000); + expect(t).toBeGreaterThan(0); + }); + + it('simple schema multiple times', () => { + let t = 0; + expect(() => { + const t1 = Number(new Date()); + for (let i = 0; i < 500; i += 1) { + validator.validateJSONSchema(simpleSchema, simpleData as object); + } + t = Number(new Date()) - t1; + }).not.toThrow(); + expect(t).toBeLessThan(3000); + expect(t).toBeGreaterThan(0); + }); + + it('simple schema 1000 times', () => { + let t = 0; + expect(() => { + const t1 = Number(new Date()); + for (let i = 0; i < 1000; i += 1) { + validator.validateJSONSchema(simpleSchema, simpleData as object); + } + t = Number(new Date()) - t1; + }).not.toThrow(); + expect(t).toBeLessThan(4000); + expect(t).toBeGreaterThan(0); + }); + + it('simple JSON schema 1000 times', () => { + let t = 0; + expect(() => { + const t1 = Number(new Date()); + for (let i = 0; i < 1000; i += 1) { + validator.validateJSONSchema(abiJsonSchema, abiData as object); + } + t = Number(new Date()) - t1; + }).not.toThrow(); + expect(t).toBeLessThan(4000); + expect(t).toBeGreaterThan(0); + }); + + it('simple ABI 1000 times', () => { + let t = 0; + expect(() => { + const t1 = Number(new Date()); + for (let i = 0; i < 1000; i += 1) { + validator.validate(abi, abiData); + } + t = Number(new Date()) - t1; + }).not.toThrow(); + expect(t).toBeLessThan(4000); + expect(t).toBeGreaterThan(0); + }); +}); diff --git a/packages/web3-validator/test/unit/validator.test.ts b/packages/web3-validator/test/unit/validator.test.ts index 7bf08497161..e8afc8045d0 100644 --- a/packages/web3-validator/test/unit/validator.test.ts +++ b/packages/web3-validator/test/unit/validator.test.ts @@ -42,20 +42,8 @@ describe('instance of validator', () => { it('instance', () => { expect(validator).toBeDefined(); expect(validator.validate).toBeDefined(); - expect(validator.addSchema).toBeDefined(); - expect(validator.getOrCreateValidator).toBeDefined(); - expect(validator.getSchema).toBeDefined(); - }); - it('add/get schema', () => { - const schema = { - type: 'array', - items: { - format: 'uint', - }, - }; - validator.addSchema('k', schema); - expect(typeof validator.getSchema('k')).toBe('function'); }); + it('convertErrors', () => { const schema = { type: 'array', @@ -66,18 +54,6 @@ describe('instance of validator', () => { // @ts-expect-error-next-line expect(validator.convertErrors(undefined, schema, [])).toBeUndefined(); }); - it('getObjectValueByPath', () => { - // @ts-expect-error-next-line - expect(validator.getObjectValueByPath({}, '$')).toBe(''); - }); - it('untilde', () => { - // @ts-expect-error-next-line - expect(validator.untilde('~1')).toBe('/'); - // @ts-expect-error-next-line - expect(validator.untilde('~0')).toBe('~'); - // @ts-expect-error-next-line - expect(validator.untilde('123')).toBe('123'); - }); it('formats exists', () => { for (const f of formatNames) { expect(typeof formats[f]).toBe('function'); diff --git a/packages/web3-validator/test/unit/web3_validator.test.ts b/packages/web3-validator/test/unit/web3_validator.test.ts index e336ad03965..e1a49fa6e3a 100644 --- a/packages/web3-validator/test/unit/web3_validator.test.ts +++ b/packages/web3-validator/test/unit/web3_validator.test.ts @@ -64,12 +64,10 @@ describe('web3-validator', () => { expect(validator.validate(['uint'], [-1], { silent: true })).toEqual([ { instancePath: '/0', - keyword: 'data["0"]', - // keyword: 'eth', - message: 'must pass "uint" validation', + keyword: '0', + message: 'value "-1" at "/0" must pass "uint" validation', params: { value: -1 }, - schemaPath: '#', - // schemaPath: '#/items/0/eth', + schemaPath: '#0', }, ]); }); @@ -83,9 +81,7 @@ describe('web3-validator', () => { const testFunction = () => { validator.validate([], data); }; - expect(testFunction).toThrow( - 'value at "/0" empty schema against data can not be validated', - ); + expect(testFunction).toThrow('empty schema against data can not be validated'); expect(testFunction).toThrow(Web3ValidatorError); }); diff --git a/yarn.lock b/yarn.lock index 0be1742231a..729d09c7273 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5722,20 +5722,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -generate-function@^2.0.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" - integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== - dependencies: - is-property "^1.0.2" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - integrity sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ== - dependencies: - is-property "^1.0.0" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -6744,22 +6730,6 @@ is-lambda@^1.0.1: resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== -is-my-ip-valid@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.1.tgz#f7220d1146257c98672e6fba097a9f3f2d348442" - integrity sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg== - -is-my-json-valid@^2.20.6: - version "2.20.6" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.20.6.tgz#a9d89e56a36493c77bda1440d69ae0dc46a08387" - integrity sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw== - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - is-my-ip-valid "^1.0.0" - jsonpointer "^5.0.0" - xtend "^4.0.0" - is-nan@^1.2.1: version "1.3.2" resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" @@ -6848,11 +6818,6 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-property@^1.0.0, is-property@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -7659,11 +7624,6 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsonpointer@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" - integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== - jsprim@^1.2.2: version "1.4.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" @@ -12117,3 +12077,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod@^3.21.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" + integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==