import { throwInvalidType } from "../../shared/src/asserts.js";
import { isProd } from "../../shared/src/config.js";
import { hasOwn } from "../../shared/src/has-own.js";
const deepFrozenObjects = /* @__PURE__ */ new WeakSet();
function deepFreeze(v) {
  if (isProd) {
    return v;
  }
  deepFreezeInternal(v, []);
  return v;
}
function deepFreezeInternal(v, seen) {
  switch (typeof v) {
    case "undefined":
      throw new TypeError("Unexpected value undefined");
    case "boolean":
    case "number":
    case "string":
      return;
    case "object": {
      if (v === null) {
        return;
      }
      if (deepFrozenObjects.has(v)) {
        return;
      }
      deepFrozenObjects.add(v);
      if (seen.includes(v)) {
        throwInvalidType(v, "Cyclic JSON object");
      }
      seen.push(v);
      Object.freeze(v);
      if (Array.isArray(v)) {
        deepFreezeArray(v, seen);
      } else {
        deepFreezeObject(v, seen);
      }
      seen.pop();
      return;
    }
    default:
      throwInvalidType(v, "JSON value");
  }
}
function deepFreezeArray(v, seen) {
  for (const item of v) {
    deepFreezeInternal(item, seen);
  }
}
function deepFreezeObject(v, seen) {
  for (const k in v) {
    if (hasOwn(v, k)) {
      const value = v[k];
      if (value !== void 0) {
        deepFreezeInternal(value, seen);
      }
    }
  }
}
function assertDeepFrozen(v) {
  if (isProd) {
    return;
  }
  if (!isDeepFrozen(v, [])) {
    throw new Error("Expected frozen object");
  }
}
function isDeepFrozen(v, seen) {
  switch (typeof v) {
    case "boolean":
    case "number":
    case "string":
      return true;
    case "object":
      if (v === null) {
        return true;
      }
      if (deepFrozenObjects.has(v)) {
        return true;
      }
      if (!Object.isFrozen(v)) {
        return false;
      }
      if (seen.includes(v)) {
        throwInvalidType(v, "Cyclic JSON object");
      }
      seen.push(v);
      if (Array.isArray(v)) {
        for (const item of v) {
          if (!isDeepFrozen(item, seen)) {
            seen.pop();
            return false;
          }
        }
      } else {
        for (const k in v) {
          if (hasOwn(v, k)) {
            const value = v[k];
            if (value !== void 0 && !isDeepFrozen(value, seen)) {
              seen.pop();
              return false;
            }
          }
        }
      }
      deepFrozenObjects.add(v);
      seen.pop();
      return true;
    default:
      throwInvalidType(v, "JSON value");
  }
}
function deepFreezeAllowUndefined(v) {
  if (v === void 0) {
    return void 0;
  }
  return deepFreeze(v);
}
export {
  assertDeepFrozen,
  deepFreeze,
  deepFreezeAllowUndefined,
  isDeepFrozen
};
//# sourceMappingURL=frozen-json.js.map
