Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 0 additions & 31 deletions src/accessDeep.test.ts

This file was deleted.

85 changes: 2 additions & 83 deletions src/accessDeep.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
import { isMap, isArray, isPlainObject, isSet } from './is.js';
import { isArray, isPlainObject } from './is.js';
import { includes } from './util.js';

const getNthKey = (value: Map<any, any> | Set<any>, n: number): any => {
if (n > value.size) throw new Error('index out of bounds');
const keys = value.keys();
while (n > 0) {
keys.next();
n--;
}

return keys.next().value;
};

function validatePath(path: (string | number)[]) {
if (includes(path, '__proto__')) {
throw new Error('__proto__ is not allowed as a property');
Expand All @@ -29,24 +18,7 @@ export const getDeep = (object: object, path: (string | number)[]): object => {

for (let i = 0; i < path.length; i++) {
const key = path[i];
if (isSet(object)) {
object = getNthKey(object, +key);
} else if (isMap(object)) {
const row = +key;
const type = +path[++i] === 0 ? 'key' : 'value';

const keyOfRow = getNthKey(object, row);
switch (type) {
case 'key':
object = keyOfRow;
break;
case 'value':
object = object.get(keyOfRow);
break;
}
} else {
object = (object as any)[key];
}
object = (object as any)[key];
}

return object;
Expand All @@ -73,27 +45,6 @@ export const setDeep = (
parent = parent[index];
} else if (isPlainObject(parent)) {
parent = parent[key];
} else if (isSet(parent)) {
const row = +key;
parent = getNthKey(parent, row);
} else if (isMap(parent)) {
const isEnd = i === path.length - 2;
if (isEnd) {
break;
}

const row = +key;
const type = +path[++i] === 0 ? 'key' : 'value';

const keyOfRow = getNthKey(parent, row);
switch (type) {
case 'key':
parent = keyOfRow;
break;
case 'value':
parent = parent.get(keyOfRow);
break;
}
}
}

Expand All @@ -105,37 +56,5 @@ export const setDeep = (
parent[lastKey] = mapper(parent[lastKey]);
}

if (isSet(parent)) {
const oldValue = getNthKey(parent, +lastKey);
const newValue = mapper(oldValue);
if (oldValue !== newValue) {
parent.delete(oldValue);
parent.add(newValue);
}
}

if (isMap(parent)) {
const row = +path[path.length - 2];
const keyToRow = getNthKey(parent, row);

const type = +lastKey === 0 ? 'key' : 'value';
switch (type) {
case 'key': {
const newKey = mapper(keyToRow);
parent.set(newKey, parent.get(keyToRow));

if (newKey !== keyToRow) {
parent.delete(keyToRow);
}
break;
}

case 'value': {
parent.set(keyToRow, mapper(parent.get(keyToRow)));
break;
}
}
}

return object;
};
121 changes: 108 additions & 13 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,113 @@ describe('stringify & parse', () => {
},
},
},
'regression #347: shared regex': {
input: () => {
const regex = /shared-regex/g;
return {
a: regex,
b: regex,
};
},
output: {
a: '/shared-regex/g',
b: '/shared-regex/g',
},
outputAnnotations: {
values: {
a: ['regexp'],
b: ['regexp'],
},
referentialEqualities: {
a: ['b'],
},
},
customExpectations: output => {
expect(output.a).toBe(output.b);
},
},
'regression #347: circular set and map': {
input: () => {
const set = new Set<any>();
set.add(set);

const map = new Map<any, any>();
map.set(map, map);
return {
a: set,
b: map,
};
},
output: {
a: [null],
b: [[null, null]]
},
outputAnnotations: {
values: {
a: ['set'],
b: ['map'],
},
referentialEqualities: {
'a': ['a.0'],
'b': ['b.0.0', 'b.0.1']
},
},
customExpectations: output => {
expect(output.a.values().next().value).toBe(output.a);
expect(output.b.values().next().value).toBe(output.b);
},
},
'regression #347: circular set in root': {
input: () => {
const set = new Set<any>();
set.add(set);
return set;
},
output: [null],
outputAnnotations: {
values: ['set'],
referentialEqualities: [['0']],
},
customExpectations: output => {
expect(output.values().next().value).toBe(output);
},
},
'regression #347: circular map in root': {
input: () => {
const map = new Map<any, any>();
map.set(map, map);
return map;
},
output: [[null, null]],
outputAnnotations: {
values: ['map'],
referentialEqualities: [['0.0', '0.1']],
},
customExpectations: output => {
expect(output.values().next().value).toBe(output);
expect(output.keys().next().value).toBe(output);
},
},
'regression #347: onyl referential equalities': {
input: () => {
const a = {};
a['a'] = a;
return {
a: a,
};
},
output: {
a: { a: null },
},
outputAnnotations: {
referentialEqualities: {
'a': ['a.a'],
},
},
customExpectations: output => {
expect(output.a).toBe(output.a.a);
},
}
};

function deepFreeze(object: any, alreadySeenObjects = new Set()) {
Expand Down Expand Up @@ -846,7 +953,7 @@ describe('stringify & parse', () => {
const { json, meta } = SuperJSON.serialize({
s7: new Train(100, 'yellow', 'Bombardier', new Set([new Carriage('front'), new Carriage('back')])) as any,
});

expect(json).toEqual({
s7: {
topSpeed: 100,
Expand Down Expand Up @@ -1296,18 +1403,6 @@ test('dedupe=true on a large complicated schema', () => {
expect(dedupedOut).toEqual(deserialized);
});

test('doesnt iterate to keys that dont exist', () => {
const robbyBubble = { id: 5 };
const highscores = new Map([[robbyBubble, 5000]]);
const objectWithReferentialEquality = { highscores, topScorer: robbyBubble };
const res = SuperJSON.serialize(objectWithReferentialEquality);

expect(res.meta.referentialEqualities.topScorer).toEqual(['highscores.0.0']);
res.meta.referentialEqualities.topScorer = ['highscores.99999.0'];

expect(() => SuperJSON.deserialize(res)).toThrowError('index out of bounds');
});

// https://github.com/flightcontrolhq/superjson/issues/319
test('deserialize in place', () => {
const serialized = SuperJSON.serialize({ a: new Date() });
Expand Down
13 changes: 1 addition & 12 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
CustomTransformerRegistry,
} from './custom-transformer-registry.js';
import {
applyReferentialEqualityAnnotations,
applyValueAnnotations,
generateReferentialEqualityAnnotations,
walker,
Expand Down Expand Up @@ -65,17 +64,7 @@ export default class SuperJSON {

let result: T = options?.inPlace ? json : copy(json) as any;

if (meta?.values) {
result = applyValueAnnotations(result, meta.values, meta.v ?? 0, this);
}

if (meta?.referentialEqualities) {
result = applyReferentialEqualityAnnotations(
result,
meta.referentialEqualities,
meta.v ?? 0
);
}
result = applyValueAnnotations(result, meta?.values, meta?.referentialEqualities, meta?.v ?? 0, this);

return result;
}
Expand Down
Loading