Skip to content

Commit 795077a

Browse files
committed
Added new depth implementation, support for toJSON, support symbol property keys
1 parent 0ba2c45 commit 795077a

File tree

2 files changed

+236
-110
lines changed

2 files changed

+236
-110
lines changed

packages/core/src/Utils.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,12 @@ export function endsWith(input: string, suffix: string): boolean {
177177
* 3. If the value is an array, it will be pruned to the specified depth and truncated.
178178
* 4. If the value is an object, it will be pruned to the specified depth and
179179
* a. If the object is a Circular Reference it will return undefined.
180-
* b. If the object is a Map, it will be converted to an object.
180+
* b. If the object is a Map, it will be converted to an object. Some data loss might occur if map keys are object types as last in wins.
181181
* c. If the object is a Set, it will be converted to an array.
182182
* d. If the object contains prototype properties, they will be picked up.
183-
* e. If the object is is uniterable and not clonable (e.g., WeakMap, WeakSet, etc.), it will return undefined.
183+
* e. If the object contains a toJSON function, it will be called and it's value will be normalized.
184+
* f. If the object is is uniterable and not clonable (e.g., WeakMap, WeakSet, etc.), it will return undefined.
185+
* g. If a symbol property is encountered, it will be converted to a string representation and could overwrite existing object keys.
184186
* 5. If the value is an Error, we will treat it as an object.
185187
* 6. If the value is a primitive, it will be returned as is unless it is a string could be truncated.
186188
* 7. If the value is a Regexp, Symbol we will convert it to the string representation.
@@ -221,6 +223,10 @@ export function prune(value: unknown, depth: number = 10): unknown {
221223
}
222224

223225
function normalizeValue(value: unknown): unknown {
226+
function hasToJSONFunction(value: unknown): value is { toJSON: () => unknown } {
227+
return value !== null && typeof value === "object" && typeof (value as { toJSON?: unknown }).toJSON === "function";
228+
}
229+
224230
if (typeof value === "bigint") {
225231
return `${value.toString()}n`;
226232
}
@@ -248,6 +254,11 @@ export function prune(value: unknown, depth: number = 10): unknown {
248254
return Array.from(value as Iterable<unknown>);
249255
}
250256

257+
if (hasToJSONFunction(value)) {
258+
// NOTE: We are not checking for circular references or overflow
259+
return normalizeValue(value.toJSON());
260+
}
261+
251262
return value;
252263
}
253264

@@ -258,7 +269,7 @@ export function prune(value: unknown, depth: number = 10): unknown {
258269
return value;
259270
}
260271

261-
function pruneImpl(value: unknown, maxDepth: number, currentDepth: number = 10, seen: WeakSet<object> = new WeakSet()): unknown {
272+
function pruneImpl(value: unknown, maxDepth: number, currentDepth: number = 10, seen: WeakSet<object> = new WeakSet(), parentIsArray: boolean = false): unknown {
262273
if (value === null || value === undefined) {
263274
return value;
264275
}
@@ -277,8 +288,14 @@ export function prune(value: unknown, depth: number = 10): unknown {
277288
return normalizedValue;
278289
}
279290

291+
if (currentDepth == maxDepth) {
292+
return undefined;
293+
}
294+
280295
if (Array.isArray(normalizedValue)) {
281-
return normalizedValue.map(e => pruneImpl(e, maxDepth, currentDepth + 1, seen));
296+
// Treat an object inside of an array as a single level
297+
const depth: number = parentIsArray ? currentDepth + 1 : currentDepth;
298+
return normalizedValue.map(e => pruneImpl(e, maxDepth, depth, seen, true));
282299
}
283300

284301
// Check for circular references
@@ -291,9 +308,17 @@ export function prune(value: unknown, depth: number = 10): unknown {
291308
}
292309

293310
const result: Record<PropertyKey, unknown> = {};
294-
for (const key in value) {
295-
const val = (value as { [index: PropertyKey]: unknown })[key];
296-
result[key] = pruneImpl(val, maxDepth, currentDepth + 1, seen);
311+
for (const key in normalizedValue) {
312+
const objectValue = (normalizedValue as { [index: PropertyKey]: unknown })[key];
313+
result[key] = pruneImpl(objectValue, maxDepth, currentDepth + 1, seen);
314+
}
315+
316+
for (const symbolKey of Object.getOwnPropertySymbols(normalizedValue)) {
317+
// Normalize the key so Symbols are converted to strings.
318+
const normalizedKey = normalizeValue(symbolKey) as PropertyKey;
319+
320+
const objectValue = (normalizedValue as { [index: PropertyKey]: unknown })[symbolKey];
321+
result[normalizedKey] = pruneImpl(objectValue, maxDepth, currentDepth + 1, seen);
297322
}
298323

299324
return result;

0 commit comments

Comments
 (0)