import { isReactive, isReadonly, isRef, toRaw, unref } from "vue"; export const CIRCULAR_REFERENCE_TEXT = "[Circular]"; export interface CircularReferenceMarker { __jsonViewCircular: true; path?: string; } export function createCircularReferenceMarker(path?: string): CircularReferenceMarker { return { __jsonViewCircular: true, path, }; } export function isCircularReferenceMarker(value: unknown): value is CircularReferenceMarker { return Boolean( value && typeof value === "object" && "__jsonViewCircular" in value && (value as { __jsonViewCircular?: boolean }).__jsonViewCircular ); } export function formatCircularReference(path?: string) { return path ? `[Circular: ${path}]` : CIRCULAR_REFERENCE_TEXT; } export function normalizeVueValue( value: unknown, currentPath?: string, seenRefs = new WeakMap() ): unknown { let current = value; while (isRef(current)) { const reference = current as object; const existingPath = seenRefs.get(reference); if (existingPath !== undefined || seenRefs.has(reference)) { return createCircularReferenceMarker(existingPath ?? currentPath); } seenRefs.set(reference, currentPath); current = unref(current); } if (current !== null && typeof current === "object" && (isReactive(current) || isReadonly(current))) { return toRaw(current); } return current; }