Skip to content

Commit c0ce88e

Browse files
committed
Fixed prune and stringify tests
1 parent 795077a commit c0ce88e

File tree

2 files changed

+152
-230
lines changed

2 files changed

+152
-230
lines changed

packages/core/src/Utils.ts

+28-137
Original file line numberDiff line numberDiff line change
@@ -174,20 +174,20 @@ export function endsWith(input: string, suffix: string): boolean {
174174
* The following rules will be applied:
175175
* 1. If the value is null or undefined, it will be returned as is.
176176
* 2. If the value is a function or unsupported type it will be return undefined.
177-
* 3. If the value is an array, it will be pruned to the specified depth and truncated.
177+
* 3. If the value is an array, it will be pruned to the specified depth.
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.
180180
* 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.
183183
* 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.
184+
* f. If the object is is uniterable and not cloneable (e.g., WeakMap, WeakSet, etc.), it will return undefined.
185185
* g. If a symbol property is encountered, it will be converted to a string representation and could overwrite existing object keys.
186186
* 5. If the value is an Error, we will treat it as an object.
187-
* 6. If the value is a primitive, it will be returned as is unless it is a string could be truncated.
187+
* 6. If the value is a primitive, it will be returned as is.
188188
* 7. If the value is a Regexp, Symbol we will convert it to the string representation.
189189
* 8. BigInt and other typed arrays will be converted to a string unless number type works.
190-
* 9. All other values will be returned as undedfined (E.g., Buffer, DataView, Promises, Generators etc..)
190+
* 9. All other values will be returned as undefined (E.g., Buffer, DataView, Promises, Generators etc..)
191191
*/
192192
export function prune(value: unknown, depth: number = 10): unknown {
193193
function isUnsupportedType(value: unknown): boolean {
@@ -236,12 +236,21 @@ export function prune(value: unknown, depth: number = 10): unknown {
236236
return value;
237237
}
238238

239-
if (value instanceof RegExp) {
240-
return value.toString();
239+
if (value instanceof Date) {
240+
return value;
241241
}
242242

243243
if (value instanceof Map) {
244-
return Object.fromEntries(value.entries());
244+
const result: Record<PropertyKey, unknown> = {};
245+
for (const [key, val] of value) {
246+
result[key] = val;
247+
}
248+
249+
return result;
250+
}
251+
252+
if (value instanceof RegExp) {
253+
return value.toString();
245254
}
246255

247256
if (value instanceof Set) {
@@ -284,10 +293,6 @@ export function prune(value: unknown, depth: number = 10): unknown {
284293

285294
const normalizedValue = normalizeValue(value);
286295
if (typeof normalizedValue === "object") {
287-
if (normalizedValue instanceof Date) {
288-
return normalizedValue;
289-
}
290-
291296
if (currentDepth == maxDepth) {
292297
return undefined;
293298
}
@@ -298,6 +303,10 @@ export function prune(value: unknown, depth: number = 10): unknown {
298303
return normalizedValue.map(e => pruneImpl(e, maxDepth, depth, seen, true));
299304
}
300305

306+
if (normalizedValue instanceof Date) {
307+
return normalizedValue;
308+
}
309+
301310
// Check for circular references
302311
if (Object.prototype.toString.call(normalizedValue) === "[object Object]") {
303312
if (seen.has(normalizedValue as object)) {
@@ -307,17 +316,19 @@ export function prune(value: unknown, depth: number = 10): unknown {
307316
seen.add(normalizedValue as object);
308317
}
309318

310-
const result: Record<PropertyKey, unknown> = {};
319+
const keys = new Set<PropertyKey>([...Object.getOwnPropertyNames(normalizedValue), ...Object.getOwnPropertySymbols(normalizedValue)]);
320+
// Loop over and add any inherited prototype properties
311321
for (const key in normalizedValue) {
312-
const objectValue = (normalizedValue as { [index: PropertyKey]: unknown })[key];
313-
result[key] = pruneImpl(objectValue, maxDepth, currentDepth + 1, seen);
322+
keys.add(key);
314323
}
315324

316-
for (const symbolKey of Object.getOwnPropertySymbols(normalizedValue)) {
325+
type NonSymbolPropertyKey = Exclude<PropertyKey, symbol>;
326+
const result: Record<NonSymbolPropertyKey, unknown> = {};
327+
for (const key of keys) {
317328
// Normalize the key so Symbols are converted to strings.
318-
const normalizedKey = normalizeValue(symbolKey) as PropertyKey;
329+
const normalizedKey = normalizeValue(key) as NonSymbolPropertyKey;
319330

320-
const objectValue = (normalizedValue as { [index: PropertyKey]: unknown })[symbolKey];
331+
const objectValue = (normalizedValue as { [index: PropertyKey]: unknown })[key];
321332
result[normalizedKey] = pruneImpl(objectValue, maxDepth, currentDepth + 1, seen);
322333
}
323334

@@ -349,126 +360,6 @@ export function stringify(data: unknown, exclusions?: string[], maxDepth: number
349360
return stringifyImpl(prunedData, exclusions || []);
350361
}
351362

352-
// https://stackoverflow.com/questions/8024149/is-it-possible-to-get-the-non-enumerable-inherited-property-names-of-an-object
353-
// function getAllKeysConditionally(obj: object, includeSelf = true, includePrototypeChain = true, includeTop = false, includeEnumerables = true, includeNonenumerables = true, includeStrings = true, includeSymbols = true) {
354-
//
355-
// // Boolean (mini-)functions to determine unknown given key's eligibility:
356-
// const isEnumerable = (obj: object, key: PropertyKey) => Object.propertyIsEnumerable.call(obj, key);
357-
// const isString = (key: PropertyKey) => typeof key === 'string';
358-
// const isSymbol = (key: PropertyKey) => typeof key === 'symbol';
359-
// const includeBasedOnEnumerability = (obj: object, key: PropertyKey) => (includeEnumerables && isEnumerable(obj, key)) || (includeNonenumerables && !isEnumerable(obj, key));
360-
// const includeBasedOnKeyType = (key: PropertyKey) => (includeStrings && isString(key)) || (includeSymbols && isSymbol(key));
361-
// const include = (obj: object, key: PropertyKey) => includeBasedOnEnumerability(obj, key) && includeBasedOnKeyType(key);
362-
// const notYetRetrieved = (keys: PropertyKey[], key: PropertyKey) => !keys.includes(key);
363-
//
364-
// // filter function putting all the above together:
365-
// const filterFn = (key: PropertyKey) => notYetRetrieved(keys, key) && include(obj, key);
366-
//
367-
// // conditional chooses one of two functions to determine whether to exclude the top level or not:
368-
// const stopFn = includeTop ? ((obj: unknown) => obj === null) : ((obj: unknown) => Object.getPrototypeOf(obj) === null);
369-
//
370-
// // and now the loop to collect and filter everything:
371-
// let keys: PropertyKey[] = [];
372-
// while (!stopFn(obj)) {
373-
// if (includeSelf) {
374-
// const ownKeys = Reflect.ownKeys(obj).filter(filterFn);
375-
// keys = keys.concat(ownKeys);
376-
// }
377-
// if (!includePrototypeChain) { break; }
378-
// else {
379-
// includeSelf = true;
380-
// obj = Object.getPrototypeOf(obj);
381-
// }
382-
// }
383-
// return keys;
384-
// }
385-
386-
// export function stringify(data: unknown, exclusions?: string[], maxDepth?: number = Infinity): string | undefined {
387-
// function stringifyImpl(obj: unknown, excludedKeys: string[], maxDepth?: number = Infinity, currentDepth: number, cache: WeakSet): string | undefined {
388-
// if (currentDepth > maxDepth) {
389-
// return;
390-
// }
391-
//
392-
// if (typeof obj === "function" || obj === undefined) {
393-
// return undefined;
394-
// }
395-
//
396-
// if (obj === null) {
397-
// return "null";
398-
// }
399-
//
400-
// if (typeof obj === "string") {
401-
// return `"${obj}"`;
402-
// }
403-
//
404-
// if (typeof obj === "number" || typeof obj === "boolean") {
405-
// return obj.toString();
406-
// }
407-
//
408-
// if (obj instanceof Date) {
409-
// return `"${obj.toISOString()}"`;
410-
// }
411-
//
412-
// if (obj instanceof RegExp) {
413-
// return obj.toString();
414-
// }
415-
//
416-
// if (typeof obj === "symbol") {
417-
// return obj.toString();
418-
// }
419-
//
420-
// if (typeof obj === "bigint") {
421-
// return obj.toString() + "n";
422-
// }
423-
//
424-
// if (typeof obj === "object") {
425-
// if (Array.isArray(obj)) {
426-
// const items = obj.map(item => stringify(item, maxDepth, currentDepth + 1));
427-
// return `[${items.join(",")}]`;
428-
// } else {
429-
// const keys = Object.keys(obj).sort(); // sort keys alphabetically
430-
// const items = keys.map(key => `"${key}":${stringify(obj[key], maxDepth, currentDepth + 1) || "null"}`);
431-
// return `{${items.join(",")}}`;
432-
// }
433-
// }
434-
//
435-
// // handle circular references
436-
// if (cache.indexOf(obj)) {
437-
// return "[Circular Reference]";
438-
// }
439-
// cache.push(obj);
440-
//
441-
// // handle objects that define their own toJSON method
442-
// const toJSON = obj.toJSON;
443-
// if (typeof toJSON === "function") {
444-
// return stringify(toJSON.call(obj), maxDepth, currentDepth + 1);
445-
// }
446-
//
447-
// // handle objects that implement the iterator protocol (Maps, Sets, etc.)
448-
// const iterator = obj[Symbol.iterator];
449-
// if (typeof iterator === "function") {
450-
// const items = [];
451-
// for (let item of obj) {
452-
// items.push(stringify(item, maxDepth, currentDepth + 1));
453-
// }
454-
// return `[${items.join(",")}]`;
455-
// }
456-
//
457-
// // handle objects that implement the async iterator protocol (Streams, etc.)
458-
// const asyncIterator = obj[Symbol.asyncIterator];
459-
// if (typeof asyncIterator === "function") {
460-
// return stringify(asyncIterator.call(obj).next().then(result => {
461-
// if (result.done) {
462-
// return null;
463-
// }
464-
// return stringify(result.value, maxDepth, currentDepth + 1);
465-
// }), maxDepth, currentDepth + 1);
466-
// }
467-
//
468-
// return undefined;
469-
// }
470-
// }
471-
472363
export function toBoolean(input: unknown, defaultValue: boolean = false): boolean {
473364
if (typeof input === "boolean") {
474365
return input;

0 commit comments

Comments
 (0)