const PrimitiveType = 1;
const ObjectType = 2;
const ArrayType = 3;
const PromiseType = 4;
const ReadableStringType = 5;
const ReadableObjectType = 6;
// https://tc39.es/ecma262/#table-json-single-character-escapes
const escapableCharCodeSubstitution = { // JSON Single Character Escape Sequences
0x08: '\\b',
0x09: '\\t',
0x0a: '\\n',
0x0c: '\\f',
0x0d: '\\r',
0x22: '\\\"',
0x5c: '\\\\'
};
function isLeadingSurrogate(code) {
return code >= 0xD800 && code <= 0xDBFF;
}
function isTrailingSurrogate(code) {
return code >= 0xDC00 && code <= 0xDFFF;
}
function isReadableStream(value) {
return (
typeof value.pipe === 'function' &&
typeof value._read === 'function' &&
typeof value._readableState === 'object' && value._readableState !== null
);
}
function replaceValue(holder, key, value, replacer) {
if (value && typeof value.toJSON === 'function') {
value = value.toJSON();
}
if (replacer !== null) {
value = replacer.call(holder, String(key), value);
}
switch (typeof value) {
case 'function':
case 'symbol':
value = undefined;
break;
case 'object':
if (value !== null) {
const cls = value.constructor;
if (cls === String || cls === Number || cls === Boolean) {
value = value.valueOf();
}
}
break;
}
return value;
}
function getTypeNative(value) {
if (value === null || typeof value !== 'object') {
return PrimitiveType;
}
if (Array.isArray(value)) {
return ArrayType;
}
return ObjectType;
}
function getTypeAsync(value) {
if (value === null || typeof value !== 'object') {
return PrimitiveType;
}
if (typeof value.then === 'function') {
return PromiseType;
}
if (isReadableStream(value)) {
return value._readableState.objectMode ? ReadableObjectType : ReadableStringType;
}
if (Array.isArray(value)) {
return ArrayType;
}
return ObjectType;
}
function normalizeReplacer(replacer) {
if (typeof replacer === 'function') {
return replacer;
}
if (Array.isArray(replacer)) {
const allowlist = new Set(replacer
.map(item => {
const cls = item && item.constructor;
return cls === String || cls === Number ? String(item) : null;
})
.filter(item => typeof item === 'string')
);
return [...allowlist];
}
return null;
}
function normalizeSpace(space) {
if (typeof space === 'number') {
if (!Number.isFinite(space) || space < 1) {
return false;
}
return ' '.repeat(Math.min(space, 10));
}
if (typeof space === 'string') {
return space.slice(0, 10) || false;
}
return false;
}
module.exports = {
escapableCharCodeSubstitution,
isLeadingSurrogate,
isTrailingSurrogate,
type: {
PRIMITIVE: PrimitiveType,
PROMISE: PromiseType,
ARRAY: ArrayType,
OBJECT: ObjectType,
STRING_STREAM: ReadableStringType,
OBJECT_STREAM: ReadableObjectType
},
isReadableStream,
replaceValue,
getTypeNative,
getTypeAsync,
normalizeReplacer,
normalizeSpace
};