|
|
"use strict"; |
|
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
|
exports.asyncToStringMethod = exports.toStringMethod = void 0; |
|
|
exports.hasToStringMethod = hasToStringMethod; |
|
|
exports.hasAsyncToStringMethod = hasAsyncToStringMethod; |
|
|
exports.stringifyInternal = stringifyInternal; |
|
|
exports.stringify = stringify; |
|
|
exports.possiblyAsyncStringify = possiblyAsyncStringify; |
|
|
exports.asyncStringify = asyncStringify; |
|
|
const globals_1 = require("./globals"); |
|
|
const safeArrayFrom = Array.from; |
|
|
const safeBufferIsBuffer = typeof Buffer !== 'undefined' ? Buffer.isBuffer : undefined; |
|
|
const safeJsonStringify = JSON.stringify; |
|
|
const safeNumberIsNaN = Number.isNaN; |
|
|
const safeObjectKeys = Object.keys; |
|
|
const safeObjectGetOwnPropertySymbols = Object.getOwnPropertySymbols; |
|
|
const safeObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
|
|
const safeObjectGetPrototypeOf = Object.getPrototypeOf; |
|
|
const safeNegativeInfinity = Number.NEGATIVE_INFINITY; |
|
|
const safePositiveInfinity = Number.POSITIVE_INFINITY; |
|
|
exports.toStringMethod = Symbol.for('fast-check/toStringMethod'); |
|
|
function hasToStringMethod(instance) { |
|
|
return (instance !== null && |
|
|
(typeof instance === 'object' || typeof instance === 'function') && |
|
|
exports.toStringMethod in instance && |
|
|
typeof instance[exports.toStringMethod] === 'function'); |
|
|
} |
|
|
exports.asyncToStringMethod = Symbol.for('fast-check/asyncToStringMethod'); |
|
|
function hasAsyncToStringMethod(instance) { |
|
|
return (instance !== null && |
|
|
(typeof instance === 'object' || typeof instance === 'function') && |
|
|
exports.asyncToStringMethod in instance && |
|
|
typeof instance[exports.asyncToStringMethod] === 'function'); |
|
|
} |
|
|
const findSymbolNameRegex = /^Symbol\((.*)\)$/; |
|
|
function getSymbolDescription(s) { |
|
|
if (s.description !== undefined) |
|
|
return s.description; |
|
|
const m = findSymbolNameRegex.exec((0, globals_1.String)(s)); |
|
|
return m && m[1].length ? m[1] : null; |
|
|
} |
|
|
function stringifyNumber(numValue) { |
|
|
switch (numValue) { |
|
|
case 0: |
|
|
return 1 / numValue === safeNegativeInfinity ? '-0' : '0'; |
|
|
case safeNegativeInfinity: |
|
|
return 'Number.NEGATIVE_INFINITY'; |
|
|
case safePositiveInfinity: |
|
|
return 'Number.POSITIVE_INFINITY'; |
|
|
default: |
|
|
return numValue === numValue ? (0, globals_1.String)(numValue) : 'Number.NaN'; |
|
|
} |
|
|
} |
|
|
function isSparseArray(arr) { |
|
|
let previousNumberedIndex = -1; |
|
|
for (const index in arr) { |
|
|
const numberedIndex = Number(index); |
|
|
if (numberedIndex !== previousNumberedIndex + 1) |
|
|
return true; |
|
|
previousNumberedIndex = numberedIndex; |
|
|
} |
|
|
return previousNumberedIndex + 1 !== arr.length; |
|
|
} |
|
|
function stringifyInternal(value, previousValues, getAsyncContent) { |
|
|
const currentValues = [...previousValues, value]; |
|
|
if (typeof value === 'object') { |
|
|
if ((0, globals_1.safeIndexOf)(previousValues, value) !== -1) { |
|
|
return '[cyclic]'; |
|
|
} |
|
|
} |
|
|
if (hasAsyncToStringMethod(value)) { |
|
|
const content = getAsyncContent(value); |
|
|
if (content.state === 'fulfilled') { |
|
|
return content.value; |
|
|
} |
|
|
} |
|
|
if (hasToStringMethod(value)) { |
|
|
try { |
|
|
return value[exports.toStringMethod](); |
|
|
} |
|
|
catch (err) { |
|
|
} |
|
|
} |
|
|
switch ((0, globals_1.safeToString)(value)) { |
|
|
case '[object Array]': { |
|
|
const arr = value; |
|
|
if (arr.length >= 50 && isSparseArray(arr)) { |
|
|
const assignments = []; |
|
|
for (const index in arr) { |
|
|
if (!safeNumberIsNaN(Number(index))) |
|
|
(0, globals_1.safePush)(assignments, `${index}:${stringifyInternal(arr[index], currentValues, getAsyncContent)}`); |
|
|
} |
|
|
return assignments.length !== 0 |
|
|
? `Object.assign(Array(${arr.length}),{${(0, globals_1.safeJoin)(assignments, ',')}})` |
|
|
: `Array(${arr.length})`; |
|
|
} |
|
|
const stringifiedArray = (0, globals_1.safeJoin)((0, globals_1.safeMap)(arr, (v) => stringifyInternal(v, currentValues, getAsyncContent)), ','); |
|
|
return arr.length === 0 || arr.length - 1 in arr ? `[${stringifiedArray}]` : `[${stringifiedArray},]`; |
|
|
} |
|
|
case '[object BigInt]': |
|
|
return `${value}n`; |
|
|
case '[object Boolean]': { |
|
|
const unboxedToString = value == true ? 'true' : 'false'; |
|
|
return typeof value === 'boolean' ? unboxedToString : `new Boolean(${unboxedToString})`; |
|
|
} |
|
|
case '[object Date]': { |
|
|
const d = value; |
|
|
return safeNumberIsNaN((0, globals_1.safeGetTime)(d)) ? `new Date(NaN)` : `new Date(${safeJsonStringify((0, globals_1.safeToISOString)(d))})`; |
|
|
} |
|
|
case '[object Map]': |
|
|
return `new Map(${stringifyInternal(Array.from(value), currentValues, getAsyncContent)})`; |
|
|
case '[object Null]': |
|
|
return `null`; |
|
|
case '[object Number]': |
|
|
return typeof value === 'number' ? stringifyNumber(value) : `new Number(${stringifyNumber(Number(value))})`; |
|
|
case '[object Object]': { |
|
|
try { |
|
|
const toStringAccessor = value.toString; |
|
|
if (typeof toStringAccessor === 'function' && toStringAccessor !== Object.prototype.toString) { |
|
|
return value.toString(); |
|
|
} |
|
|
} |
|
|
catch (err) { |
|
|
return '[object Object]'; |
|
|
} |
|
|
const mapper = (k) => `${k === '__proto__' |
|
|
? '["__proto__"]' |
|
|
: typeof k === 'symbol' |
|
|
? `[${stringifyInternal(k, currentValues, getAsyncContent)}]` |
|
|
: safeJsonStringify(k)}:${stringifyInternal(value[k], currentValues, getAsyncContent)}`; |
|
|
const stringifiedProperties = [ |
|
|
...(0, globals_1.safeMap)(safeObjectKeys(value), mapper), |
|
|
...(0, globals_1.safeMap)((0, globals_1.safeFilter)(safeObjectGetOwnPropertySymbols(value), (s) => { |
|
|
const descriptor = safeObjectGetOwnPropertyDescriptor(value, s); |
|
|
return descriptor && descriptor.enumerable; |
|
|
}), mapper), |
|
|
]; |
|
|
const rawRepr = '{' + (0, globals_1.safeJoin)(stringifiedProperties, ',') + '}'; |
|
|
if (safeObjectGetPrototypeOf(value) === null) { |
|
|
return rawRepr === '{}' ? 'Object.create(null)' : `Object.assign(Object.create(null),${rawRepr})`; |
|
|
} |
|
|
return rawRepr; |
|
|
} |
|
|
case '[object Set]': |
|
|
return `new Set(${stringifyInternal(Array.from(value), currentValues, getAsyncContent)})`; |
|
|
case '[object String]': |
|
|
return typeof value === 'string' ? safeJsonStringify(value) : `new String(${safeJsonStringify(value)})`; |
|
|
case '[object Symbol]': { |
|
|
const s = value; |
|
|
if (globals_1.Symbol.keyFor(s) !== undefined) { |
|
|
return `Symbol.for(${safeJsonStringify(globals_1.Symbol.keyFor(s))})`; |
|
|
} |
|
|
const desc = getSymbolDescription(s); |
|
|
if (desc === null) { |
|
|
return 'Symbol()'; |
|
|
} |
|
|
const knownSymbol = desc.startsWith('Symbol.') && globals_1.Symbol[desc.substring(7)]; |
|
|
return s === knownSymbol ? desc : `Symbol(${safeJsonStringify(desc)})`; |
|
|
} |
|
|
case '[object Promise]': { |
|
|
const promiseContent = getAsyncContent(value); |
|
|
switch (promiseContent.state) { |
|
|
case 'fulfilled': |
|
|
return `Promise.resolve(${stringifyInternal(promiseContent.value, currentValues, getAsyncContent)})`; |
|
|
case 'rejected': |
|
|
return `Promise.reject(${stringifyInternal(promiseContent.value, currentValues, getAsyncContent)})`; |
|
|
case 'pending': |
|
|
return `new Promise(() => {/*pending*/})`; |
|
|
case 'unknown': |
|
|
default: |
|
|
return `new Promise(() => {/*unknown*/})`; |
|
|
} |
|
|
} |
|
|
case '[object Error]': |
|
|
if (value instanceof Error) { |
|
|
return `new Error(${stringifyInternal(value.message, currentValues, getAsyncContent)})`; |
|
|
} |
|
|
break; |
|
|
case '[object Undefined]': |
|
|
return `undefined`; |
|
|
case '[object Int8Array]': |
|
|
case '[object Uint8Array]': |
|
|
case '[object Uint8ClampedArray]': |
|
|
case '[object Int16Array]': |
|
|
case '[object Uint16Array]': |
|
|
case '[object Int32Array]': |
|
|
case '[object Uint32Array]': |
|
|
case '[object Float32Array]': |
|
|
case '[object Float64Array]': |
|
|
case '[object BigInt64Array]': |
|
|
case '[object BigUint64Array]': { |
|
|
if (typeof safeBufferIsBuffer === 'function' && safeBufferIsBuffer(value)) { |
|
|
return `Buffer.from(${stringifyInternal(safeArrayFrom(value.values()), currentValues, getAsyncContent)})`; |
|
|
} |
|
|
const valuePrototype = safeObjectGetPrototypeOf(value); |
|
|
const className = valuePrototype && valuePrototype.constructor && valuePrototype.constructor.name; |
|
|
if (typeof className === 'string') { |
|
|
const typedArray = value; |
|
|
const valuesFromTypedArr = typedArray.values(); |
|
|
return `${className}.from(${stringifyInternal(safeArrayFrom(valuesFromTypedArr), currentValues, getAsyncContent)})`; |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
try { |
|
|
return value.toString(); |
|
|
} |
|
|
catch (_a) { |
|
|
return (0, globals_1.safeToString)(value); |
|
|
} |
|
|
} |
|
|
function stringify(value) { |
|
|
return stringifyInternal(value, [], () => ({ state: 'unknown', value: undefined })); |
|
|
} |
|
|
function possiblyAsyncStringify(value) { |
|
|
const stillPendingMarker = (0, globals_1.Symbol)(); |
|
|
const pendingPromisesForCache = []; |
|
|
const cache = new globals_1.Map(); |
|
|
function createDelay0() { |
|
|
let handleId = null; |
|
|
const cancel = () => { |
|
|
if (handleId !== null) { |
|
|
clearTimeout(handleId); |
|
|
} |
|
|
}; |
|
|
const delay = new Promise((resolve) => { |
|
|
handleId = setTimeout(() => { |
|
|
handleId = null; |
|
|
resolve(stillPendingMarker); |
|
|
}, 0); |
|
|
}); |
|
|
return { delay, cancel }; |
|
|
} |
|
|
const unknownState = { state: 'unknown', value: undefined }; |
|
|
const getAsyncContent = function getAsyncContent(data) { |
|
|
const cacheKey = data; |
|
|
if (cache.has(cacheKey)) { |
|
|
return cache.get(cacheKey); |
|
|
} |
|
|
const delay0 = createDelay0(); |
|
|
const p = exports.asyncToStringMethod in data |
|
|
? Promise.resolve().then(() => data[exports.asyncToStringMethod]()) |
|
|
: data; |
|
|
p.catch(() => { }); |
|
|
pendingPromisesForCache.push(Promise.race([p, delay0.delay]).then((successValue) => { |
|
|
if (successValue === stillPendingMarker) |
|
|
cache.set(cacheKey, { state: 'pending', value: undefined }); |
|
|
else |
|
|
cache.set(cacheKey, { state: 'fulfilled', value: successValue }); |
|
|
delay0.cancel(); |
|
|
}, (errorValue) => { |
|
|
cache.set(cacheKey, { state: 'rejected', value: errorValue }); |
|
|
delay0.cancel(); |
|
|
})); |
|
|
cache.set(cacheKey, unknownState); |
|
|
return unknownState; |
|
|
}; |
|
|
function loop() { |
|
|
const stringifiedValue = stringifyInternal(value, [], getAsyncContent); |
|
|
if (pendingPromisesForCache.length === 0) { |
|
|
return stringifiedValue; |
|
|
} |
|
|
return Promise.all(pendingPromisesForCache.splice(0)).then(loop); |
|
|
} |
|
|
return loop(); |
|
|
} |
|
|
async function asyncStringify(value) { |
|
|
return Promise.resolve(possiblyAsyncStringify(value)); |
|
|
} |
|
|
|