diff --git a/__tests__/validation-algorithms/create-validator.test.ts b/__tests__/validation-algorithms/create-validator.test.ts index 63670d0..944c92e 100644 --- a/__tests__/validation-algorithms/create-validator.test.ts +++ b/__tests__/validation-algorithms/create-validator.test.ts @@ -3096,6 +3096,62 @@ describe("createValidator", () => { expect(() => validate(data2)).not.toThrowError(); expect(validate(data2)).toEqual(true); }); + + it("multiple neighboring circular types should correctly validate", () => { + const typeDef = DataType.RecordOf({ + a: DataType.Circular((self) => + DataType.RecordOf({ + tag: DataType.String, + children: DataType.ArrayOf(self), + }) + ), + b: DataType.Circular((self) => + DataType.RecordOf({ + tag: DataType.Number, + }) + ), + }); + + const validate = createValidator(typeDef); + + const a = { + tag: "div", + children: [], + }; + + const data = { + a: a, + b: a, + }; + + expect(validate(data)).toEqual(false); + + const typeDef2 = DataType.RecordOf({ + a: DataType.Circular((self) => + DataType.RecordOf({ + tag: DataType.String, + children: DataType.ArrayOf(self), + notSelf: DataType.Circular((s) => + DataType.RecordOf({ foo: DataType.String }) + ), + }) + ), + }); + + const validate2 = createValidator(typeDef2); + + const data2 = { + a: { + tag: "div", + children: [], + notSelf: {}, + }, + }; + + data2.a.notSelf = data2.a; + + expect(validate2(data2)).toEqual(false); + }); }); }); }); diff --git a/src/validation-algorithms/validators/helper-validated-circ-values.ts b/src/validation-algorithms/validators/helper-validated-circ-values.ts index a0dde35..464281b 100644 --- a/src/validation-algorithms/validators/helper-validated-circ-values.ts +++ b/src/validation-algorithms/validators/helper-validated-circ-values.ts @@ -1,3 +1,5 @@ +import type { AnyDataType } from "@DataTypes/types"; + /** * Collection of values that have been ran through the validator * already, and were expected to possibly contain circular @@ -10,4 +12,21 @@ * * This Set should be cleared after each validation pass. */ -export const validatedCircularValues = new Set(); +export const validatedCircularValues = new Map>(); + +export const wasCircValidated = (type: AnyDataType, data: unknown) => { + let set = validatedCircularValues.get(type); + + if (!set) { + set = new Set([data]); + validatedCircularValues.set(type, set); + return false; + } + + if (set.has(data)) { + return true; + } + + set.add(data); + return false; +}; diff --git a/src/validation-algorithms/validators/validate-circular.ts b/src/validation-algorithms/validators/validate-circular.ts index 679d953..623f4df 100644 --- a/src/validation-algorithms/validators/validate-circular.ts +++ b/src/validation-algorithms/validators/validate-circular.ts @@ -1,14 +1,13 @@ import type { Circular, CircularRef } from "@DataTypes/data-types"; import type { AnyDataType } from "@DataTypes/types"; import type { Path } from "@Validation/path"; -import { validatedCircularValues } from "@Validation/validators/helper-validated-circ-values"; +import { wasCircValidated } from "@Validation/validators/helper-validated-circ-values"; import { validatorsLookupMap } from "@Validation/validators/validate-type"; export const validateCircular = (path: Path, type: Circular, data: unknown) => { - if (validatedCircularValues.has(data)) { + if (wasCircValidated(type, data)) { return; } - validatedCircularValues.add(data); const childType = type.type as AnyDataType; return validatorsLookupMap.get(childType.kind)!(path, childType, data); @@ -19,10 +18,9 @@ export const validateCircularRef = ( type: CircularRef, data: unknown ) => { - if (validatedCircularValues.has(data)) { + if (wasCircValidated(type._getReferencedType(), data)) { return; } - validatedCircularValues.add(data); const refType = type._getReferencedType() as AnyDataType; return validatorsLookupMap.get(refType.kind)!(path, refType, data);