import {assertObject, throwInvalidType} from './asserts';import {skipAssertJSONValue} from './config.js';import {hasOwn} from './has-own';
export type JSONValue = | null | string | boolean | number | Array<JSONValue> | JSONObject;
export type JSONObject = Partial<{[key: string]: JSONValue}>;
export type ReadonlyJSONValue = | null | string | boolean | number | ReadonlyArray<ReadonlyJSONValue> | ReadonlyJSONObject;
export type ReadonlyJSONObject = Partial<{ readonly [key: string]: ReadonlyJSONValue;}>;
export function deepEqual( a: ReadonlyJSONValue | undefined, b: ReadonlyJSONValue | undefined,): boolean { if (a === b) { return true; }
if (typeof a !== typeof b) { return false; }
switch (typeof a) { case 'boolean': case 'number': case 'string': return false; }
a = a!;
if (Array.isArray(a)) { if (!Array.isArray(b)) { return false; } if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (!deepEqual(a[i], b[i])) { return false; } } return true; }
if (a === null || b === null) { return false; }
if (Array.isArray(b)) { return false; }
a = a as ReadonlyJSONObject; b = b as ReadonlyJSONObject;
let aSize = 0; for (const key in a) { if (hasOwn(a, key)) { if (!deepEqual(a[key], b[key])) { return false; } aSize++; } }
let bSize = 0; for (const key in b) { if (hasOwn(b, key)) { bSize++; } }
return aSize === bSize;}
export function deepClone(value: ReadonlyJSONValue): JSONValue { const seen: Array<ReadonlyJSONObject | ReadonlyArray<ReadonlyJSONValue>> = []; return internalDeepClone(value, seen);}
export function internalDeepClone( value: ReadonlyJSONValue, seen: Array<ReadonlyJSONObject | ReadonlyArray<ReadonlyJSONValue>>,): JSONValue { switch (typeof value) { case 'boolean': case 'number': case 'string': case 'undefined': return value; case 'object': { if (value === null) { return null; } if (seen.includes(value)) { throw new Error('Cyclic object'); } seen.push(value); if (Array.isArray(value)) { const rv = value.map(v => internalDeepClone(v, seen)); seen.pop(); return rv; }
const obj: JSONObject = {};
for (const k in value) { if (hasOwn(value, k)) { const v = (value as ReadonlyJSONObject)[k]; if (v !== undefined) { obj[k] = internalDeepClone(v, seen); } } } seen.pop(); return obj; }
default: throw new Error(`Invalid type: ${typeof value}`); }}
const SIZE_TAG = 1;const SIZE_INT32 = 4;const SIZE_DOUBLE = 8;
export function getSizeOfValue(value: ReadonlyJSONValue): number { switch (typeof value) { case 'string': return SIZE_TAG + SIZE_INT32 + value.length; case 'number': if (isSmi(value)) { if (value <= -(2 ** 30) || value >= 2 ** 30 - 1) { return SIZE_TAG + 5; } return SIZE_TAG + SIZE_INT32; } return SIZE_TAG + SIZE_DOUBLE; case 'boolean': return SIZE_TAG; case 'object': if (value === null) { return SIZE_TAG; }
if (Array.isArray(value)) { let sum = 2 * SIZE_TAG + SIZE_INT32; for (const element of value) { sum += getSizeOfValue(element); } return sum; }
{ const val = value as ReadonlyJSONObject; let sum: number = SIZE_TAG; for (const k in val) { if (hasOwn(val, k)) { const v = val[k]; if (v !== undefined) { sum += getSizeOfValue(k) + getSizeOfValue(v); } } } return sum + SIZE_INT32 + SIZE_TAG; } }
throw new Error('invalid value');}
function isSmi(value: number): boolean { return value === (value | 0);}
export function assertJSONValue(v: unknown): asserts v is JSONValue { if (skipAssertJSONValue) { return; } switch (typeof v) { case 'boolean': case 'number': case 'string': return; case 'object': if (v === null) { return; } if (Array.isArray(v)) { return assertJSONArray(v); } return assertObjectIsJSONObject(v as Record<string, unknown>); } throwInvalidType(v, 'JSON value');}
export function assertJSONObject(v: unknown): asserts v is JSONObject { assertObject(v); assertObjectIsJSONObject(v);}
function assertObjectIsJSONObject( v: Record<string, unknown>,): asserts v is JSONObject { for (const k in v) { if (hasOwn(v, k)) { const val = v[k]; if (val !== undefined) { assertJSONValue(v[k]); } } }}
function assertJSONArray(v: unknown[]): asserts v is JSONValue[] { for (let i = 0; i < v.length; i++) { const val = v[i]; if (val !== undefined) { assertJSONValue(val); } }}