import {
  MarkIcon
} from "./chunk-VZOYWIRW.js";
import {
  Zero
} from "./chunk-2OFTKN5C.js";
import "./chunk-ASRS2LFV.js";
import {
  DEFAULT_TTL_MS
} from "./chunk-OZZJ3HSP.js";
import {
  hasOwn
} from "./chunk-EZM3XBAB.js";
import "./chunk-424PT5DM.js";

// ../zero-react/src/components/zero-inspector.tsx
import { lazy, Suspense, useState } from "react";
import { jsx } from "react/jsx-runtime";
var Inspector = lazy(() => import("./inspector-IU2HG74I.js"));
function ZeroInspector({ zero }) {
  const [show, setShow] = useState(false);
  return show ? /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { children: "Loading Inspector..." }), children: /* @__PURE__ */ jsx(
    Inspector,
    {
      zero,
      onClose: () => setShow(false)
    }
  ) }) : /* @__PURE__ */ jsx(
    "button",
    {
      onClick: () => setShow(!show),
      style: {
        position: "fixed",
        bottom: 0,
        right: 0,
        zIndex: 1e3,
        padding: "5px",
        color: "white",
        backgroundColor: "#333",
        borderTopLeftRadius: "8px",
        opacity: 0.95
      },
      children: /* @__PURE__ */ jsx(
        MarkIcon,
        {
          style: {
            width: "20px",
            height: "20px",
            fill: "currentColor"
          }
        }
      )
    }
  );
}

// ../zero-react/src/use-query.tsx
import { resolver } from "@rocicorp/resolver";
import React, { useSyncExternalStore } from "react";

// ../shared/src/deep-clone.ts
function deepClone(value) {
  const seen = [];
  return internalDeepClone(value, seen);
}
function internalDeepClone(value, seen) {
  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 = {};
      for (const k in value) {
        if (hasOwn(value, k)) {
          const v = value[k];
          if (v !== void 0) {
            obj[k] = internalDeepClone(v, seen);
          }
        }
      }
      seen.pop();
      return obj;
    }
    default:
      throw new Error(`Invalid type: ${typeof value}`);
  }
}

// ../zero-react/src/zero-provider.tsx
import {
  createContext,
  useContext,
  useEffect,
  useState as useState2
} from "react";
import { jsx as jsx2 } from "react/jsx-runtime";
var ZeroContext = createContext(void 0);
function useZero() {
  const zero = useContext(ZeroContext);
  if (zero === void 0) {
    throw new Error("useZero must be used within a ZeroProvider");
  }
  return zero;
}
function createUseZero() {
  return () => useZero();
}
function ZeroProvider({ children, init, ...props }) {
  const [zero, setZero] = useState2(
    "zero" in props ? props.zero : void 0
  );
  useEffect(() => {
    if ("zero" in props) {
      setZero(props.zero);
      return;
    }
    const z = new Zero(props);
    init?.(z);
    setZero(z);
    return () => {
      void z.close();
      setZero(void 0);
    };
  }, [init, ...Object.values(props)]);
  return zero && /* @__PURE__ */ jsx2(ZeroContext.Provider, { value: zero, children });
}

// ../zero-react/src/use-query.tsx
var reactUse = React.use;
var suspend = reactUse ? reactUse : (p) => {
  throw p;
};
function useQuery(query, options) {
  let enabled = true;
  let ttl = DEFAULT_TTL_MS;
  if (typeof options === "boolean") {
    enabled = options;
  } else if (options) {
    ({ enabled = true, ttl = DEFAULT_TTL_MS } = options);
  }
  const view = viewStore.getView(
    useZero(),
    query,
    enabled,
    ttl
  );
  return useSyncExternalStore(
    view.subscribeReactInternals,
    view.getSnapshot,
    view.getSnapshot
  );
}
function useSuspenseQuery(query, options) {
  let enabled = true;
  let ttl = DEFAULT_TTL_MS;
  let suspendUntil = "partial";
  if (typeof options === "boolean") {
    enabled = options;
  } else if (options) {
    ({
      enabled = true,
      ttl = DEFAULT_TTL_MS,
      suspendUntil = "complete"
    } = options);
  }
  const view = viewStore.getView(
    useZero(),
    query,
    enabled,
    ttl
  );
  const snapshot = useSyncExternalStore(
    view.subscribeReactInternals,
    view.getSnapshot,
    view.getSnapshot
  );
  if (enabled) {
    if (suspendUntil === "complete" && !view.complete) {
      suspend(view.waitForComplete());
    }
    if (suspendUntil === "partial" && !view.nonEmpty) {
      suspend(view.waitForNonEmpty());
    }
  }
  return snapshot;
}
var emptyArray = [];
var disabledSubscriber = () => () => {
};
var resultTypeUnknown = { type: "unknown" };
var resultTypeComplete = { type: "complete" };
var resultTypeError = { type: "error" };
var emptySnapshotSingularUnknown = [void 0, resultTypeUnknown];
var emptySnapshotSingularComplete = [void 0, resultTypeComplete];
var emptySnapshotSingularErrorUnknown = [void 0, resultTypeError];
var emptySnapshotPluralUnknown = [emptyArray, resultTypeUnknown];
var emptySnapshotPluralComplete = [emptyArray, resultTypeComplete];
var emptySnapshotErrorUnknown = [emptyArray, resultTypeError];
function getDefaultSnapshot(singular) {
  return singular ? emptySnapshotSingularUnknown : emptySnapshotPluralUnknown;
}
function getSnapshot(singular, data, resultType, refetchFn, error) {
  if (singular && data === void 0) {
    switch (resultType) {
      case "error":
        if (error) {
          return [
            void 0,
            makeError(refetchFn, error)
          ];
        }
        return emptySnapshotSingularErrorUnknown;
      case "complete":
        return emptySnapshotSingularComplete;
      case "unknown":
        return emptySnapshotSingularUnknown;
    }
  }
  if (!singular && data.length === 0) {
    switch (resultType) {
      case "error":
        if (error) {
          return [
            emptyArray,
            makeError(refetchFn, error)
          ];
        }
        return emptySnapshotErrorUnknown;
      case "complete":
        return emptySnapshotPluralComplete;
      case "unknown":
        return emptySnapshotPluralUnknown;
    }
  }
  switch (resultType) {
    case "error":
      if (error) {
        return [data, makeError(refetchFn, error)];
      }
      return [
        data,
        makeError(refetchFn, {
          error: "app",
          id: "unknown",
          name: "unknown",
          details: "An unknown error occurred"
        })
      ];
    case "complete":
      return [data, resultTypeComplete];
    case "unknown":
      return [data, resultTypeUnknown];
  }
}
function makeError(refetch, error) {
  return {
    type: "error",
    refetch,
    error: error.error === "app" || error.error === "zero" ? {
      type: "app",
      queryName: error.name,
      details: error.details
    } : {
      type: "http",
      queryName: error.name,
      status: error.status,
      details: error.details
    }
  };
}
var ViewStore = class {
  #views = /* @__PURE__ */ new Map();
  constructor() {
    if (false) {
      allViews.set(this, this.#views);
    }
  }
  getView(zero, query, enabled, ttl) {
    const { format } = query;
    if (!enabled) {
      return {
        getSnapshot: () => getDefaultSnapshot(format.singular),
        subscribeReactInternals: disabledSubscriber,
        updateTTL: () => {
        },
        waitForComplete: () => Promise.resolve(),
        waitForNonEmpty: () => Promise.resolve(),
        complete: false,
        nonEmpty: false
      };
    }
    const hash = query.hash() + zero.clientID;
    let existing = this.#views.get(hash);
    if (!existing) {
      existing = new ViewWrapper(zero, query, format, ttl, (view) => {
        const currentView = this.#views.get(hash);
        if (currentView && currentView !== view) {
          return;
        }
        this.#views.delete(hash);
      });
      this.#views.set(hash, existing);
    } else {
      existing.updateTTL(ttl);
    }
    return existing;
  }
};
var viewStore = new ViewStore();
var ViewWrapper = class {
  #zero;
  #view;
  #onDematerialized;
  #query;
  #format;
  #snapshot;
  #reactInternals;
  #ttl;
  #complete = false;
  #completeResolver = resolver();
  #nonEmpty = false;
  #nonEmptyResolver = resolver();
  constructor(zero, query, format, ttl, onDematerialized) {
    this.#zero = zero;
    this.#query = query;
    this.#format = format;
    this.#ttl = ttl;
    this.#onDematerialized = onDematerialized;
    this.#snapshot = getDefaultSnapshot(format.singular);
    this.#reactInternals = /* @__PURE__ */ new Set();
    this.#materializeIfNeeded();
  }
  #onData = (snap, resultType, error) => {
    const data = snap === void 0 ? snap : deepClone(snap);
    this.#snapshot = getSnapshot(
      this.#format.singular,
      data,
      resultType,
      this.#refetch,
      error
    );
    if (resultType === "complete" || resultType === "error") {
      this.#complete = true;
      this.#completeResolver.resolve();
      this.#nonEmpty = true;
      this.#nonEmptyResolver.resolve();
    }
    if (this.#format.singular ? this.#snapshot[0] !== void 0 : this.#snapshot[0].length !== 0) {
      this.#nonEmpty = true;
      this.#nonEmptyResolver.resolve();
    }
    for (const internals of this.#reactInternals) {
      internals();
    }
  };
  /**
   * Called by the user to force a refetch of the query
   * in the case the query errored.
   */
  #refetch = () => {
    this.#view?.destroy();
    this.#view = void 0;
    this.#materializeIfNeeded();
  };
  #materializeIfNeeded = () => {
    if (this.#view) {
      return;
    }
    this.#view = this.#zero.materialize(this.#query, { ttl: this.#ttl });
    this.#view.addListener(this.#onData);
  };
  getSnapshot = () => this.#snapshot;
  subscribeReactInternals = (internals) => {
    this.#reactInternals.add(internals);
    this.#materializeIfNeeded();
    return () => {
      this.#reactInternals.delete(internals);
      if (this.#reactInternals.size === 0) {
        setTimeout(() => {
          if (this.#view === void 0) {
            return;
          }
          if (this.#reactInternals.size > 0) {
            return;
          }
          this.#view.destroy();
          this.#view = void 0;
          this.#complete = false;
          this.#completeResolver = resolver();
          this.#nonEmpty = false;
          this.#nonEmptyResolver = resolver();
          this.#onDematerialized(this);
        }, 10);
      }
    };
  };
  updateTTL(ttl) {
    this.#ttl = ttl;
    this.#view?.updateTTL(ttl);
  }
  get complete() {
    return this.#complete;
  }
  waitForComplete() {
    return this.#completeResolver.promise;
  }
  get nonEmpty() {
    return this.#nonEmpty;
  }
  waitForNonEmpty() {
    return this.#nonEmptyResolver.promise;
  }
};

// ../zero-react/src/use-zero-online.tsx
import { useSyncExternalStore as useSyncExternalStore2 } from "react";
function useZeroOnline() {
  const zero = useZero();
  return useSyncExternalStore2(
    zero.onOnline,
    () => zero.online,
    () => zero.online
  );
}
export {
  ZeroInspector,
  ZeroProvider,
  createUseZero,
  useQuery,
  useSuspenseQuery,
  useZero,
  useZeroOnline
};
//# sourceMappingURL=react.js.map
