Skip to content

Commit

Permalink
Merge pull request #170 from GaetanCottrez/bugfix/circular-ref-json-s…
Browse files Browse the repository at this point in the history
…tringify

fix(utils): fix circular ref json.stringify
  • Loading branch information
4lessandrodev committed Jul 18, 2024
2 parents 9764adf + 7d0e91b commit 55e2d28
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 21 deletions.
76 changes: 76 additions & 0 deletions lib/utils/stringify.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const {stringify: $stringify} = JSON;
const {keys} = Object;

const Primitive = String; // it could be Number
const primitive = 'string'; // it could be 'number'

const ignore = {};
const object = 'object';

const noop = (_, value) => value;

const revive = (input: { [x: string]: any; }, parsed: { has: (arg0: any) => any; add: (arg0: any) => void; }, output: {
[x: string]: any;
}, $: { call: (arg0: any, arg1: string, arg2: any) => any; }) => {
const lazy: any[] = [];
for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
const k = ke[y];
const value = output[k];
if (value instanceof Primitive) {
const tmp = input[value as string];
if (typeof tmp === object && !parsed.has(tmp)) {
parsed.add(tmp);
output[k] = ignore;
lazy.push({k, a: [input, parsed, tmp, $]});
}
else
output[k] = $.call(output, k, tmp);
}
else if (output[k] !== ignore)
output[k] = $.call(output, k, value);
}
for (let {length} = lazy, i = 0; i < length; i++) {
const {k, a} = lazy[i];
output[k] = $.call(output, k, revive.apply(null, a));
}
return output;
};

const set = (known, input, value) => {
const index = Primitive(input.push(value) - 1);
known.set(value, index);
return index;
};

/**
* Converts a JS value into a specialized flatted string.
* @param {any} value
* @returns {string}
*/
export const stringify = (value: any): string => {
const $ = noop;
const known = new Map;
const input = [];
const output: string[] = [];
let i = +set(known, input, $.call({'': value}, '', value));
let firstRun = !i;
while (i < input.length) {
firstRun = true;
output[i] = $stringify(input[i++], replace);
}
return '[' + output.join(',') + ']';
function replace(this: any, key: any, value: any) {
if (firstRun) {
firstRun = !firstRun;
return value;
}
const after = $.call(this, key, value);
switch (typeof after) {
case object:
if (after === null) return after;
case primitive:
return known.get(after) || set(known, input, after);
}
return after;
}
};
11 changes: 5 additions & 6 deletions lib/utils/validator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Aggregate, Entity, ID, ValueObject } from "../core";

import {stringify} from '../utils/stringify.util';
export class Validator {
private static instance: Validator = null as unknown as Validator;
private constructor() { }
Expand All @@ -16,7 +16,7 @@ export class Validator {
return (
asciiCode >= 33 && asciiCode <= 47 ||
asciiCode >= 58 && asciiCode <= 64 ||
asciiCode >= 91 && asciiCode <= 96 ||
asciiCode >= 91 && asciiCode <= 96 ||
asciiCode >= 123 && asciiCode <= 126
);
}
Expand All @@ -34,10 +34,9 @@ export class Validator {
return props instanceof Date;
}
isObject(props: any): boolean {
delete props?._domainEvents;
const isObj = typeof props === 'object';
if (!isObj || props === null) return false;
if (JSON.stringify(props) === JSON.stringify({})) return true;
if (stringify(props) === stringify({})) return true;
const hasKeys = Object.keys(props).length > 0;
const isNotArray = !Validator.instance.isArray(props);
const isNotEntity = !Validator.instance.isEntity(props);
Expand Down Expand Up @@ -112,11 +111,11 @@ export class Validator {
(Validator.instance.isString(target) &&
target.trim() === ''),
match:(regex: RegExp): boolean => regex.test(target),
hasOnlyNumbers: (): boolean => Validator.instance.isString(target) &&
hasOnlyNumbers: (): boolean => Validator.instance.isString(target) &&
target.split('')
.map((n) => n.charCodeAt(0) >= 48 && n.charCodeAt(0) <= 57)
.every((v) => v === true),
hasOnlyLetters: (): boolean => Validator.instance.isString(target) &&
hasOnlyLetters: (): boolean => Validator.instance.isString(target) &&
target.toUpperCase()
.split('')
.map((n) => n.charCodeAt(0) >= 65 && n.charCodeAt(0) <= 90)
Expand Down
File renamed without changes.
44 changes: 29 additions & 15 deletions tests/utils/validator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ describe('check-types', () => {
super(props)
}

public static create(): IResult<Entity<any>, string> {
return Result.Ok(new Ent({ value: 'hello' }));
public static create(props: any = { value: 'hello' }): IResult<Entity<any>, string> {
return Result.Ok(new Ent(props));
}
};

Expand Down Expand Up @@ -97,7 +97,7 @@ describe('check-types', () => {
const result = checker.isString(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isString(vo.value());
Expand Down Expand Up @@ -174,7 +174,7 @@ describe('check-types', () => {
const result = checker.isValueObject(agg.value());
expect(result).toBeFalsy();
});

it('should return true if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isValueObject(vo.value());
Expand Down Expand Up @@ -251,7 +251,7 @@ describe('check-types', () => {
const result = checker.isEntity(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isEntity(vo.value());
Expand Down Expand Up @@ -327,7 +327,7 @@ describe('check-types', () => {
const result = checker.isAggregate(agg.value());
expect(result).toBeTruthy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isAggregate(vo.value());
Expand Down Expand Up @@ -403,7 +403,7 @@ describe('check-types', () => {
const result = checker.isArray(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isArray(vo.value());
Expand Down Expand Up @@ -479,7 +479,7 @@ describe('check-types', () => {
const result = checker.isBoolean(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isBoolean(vo.value());
Expand Down Expand Up @@ -556,7 +556,7 @@ describe('check-types', () => {
const result = checker.isNumber(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isNumber(vo.value());
Expand Down Expand Up @@ -672,7 +672,7 @@ describe('check-types', () => {
const result = checker.isDate(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isDate(vo.value());
Expand Down Expand Up @@ -749,7 +749,7 @@ describe('check-types', () => {
const result = checker.isNull(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isNull(vo.value());
Expand Down Expand Up @@ -826,7 +826,7 @@ describe('check-types', () => {
const result = checker.isUndefined(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isUndefined(vo.value());
Expand Down Expand Up @@ -903,7 +903,7 @@ describe('check-types', () => {
const result = checker.isFunction(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isFunction(vo.value());
Expand Down Expand Up @@ -980,7 +980,7 @@ describe('check-types', () => {
const result = checker.isObject(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isObject(vo.value());
Expand All @@ -992,6 +992,20 @@ describe('check-types', () => {
const result = checker.isObject(ent.value());
expect(result).toBeFalsy();
});

it('should return false if is Entity with Circular Reference', () => {
const ent1 = Ent.create();
const ent2 = Ent.create({ value: ent1 });
console.log('should return false if is Entity with Circular Reference')
console.log(ent2.toObject())
const result = checker.isObject(ent2.value());
expect(result).toBeFalsy();
});

it('should return false if is string', () => {
const result = checker.isObject("lorem ipsum");
expect(result).toBeFalsy();
});
});


Expand Down Expand Up @@ -1068,7 +1082,7 @@ describe('check-types', () => {
const result = checker.isID(agg.value());
expect(result).toBeFalsy();
});

it('should return false if is ValueObject', () => {
const vo = Vo.create();
const result = checker.isID(vo.value());
Expand Down

0 comments on commit 55e2d28

Please sign in to comment.