import {
  DataSpaceSelector,
  PanelContainer
} from "./chunk-34BKYKWL.mjs";

// src/panels/edge/InvocationTracePanel/InvocationTraceContainer.tsx
import * as Array from "effect/Array";
import * as Match from "effect/Match";
import * as Option from "effect/Option";
import * as Schema from "effect/Schema";
import React5, { useCallback as useCallback2, useMemo as useMemo5, useState as useState2 } from "react";
import { Filter as Filter2 } from "@dxos/echo";
import { Format } from "@dxos/echo/internal";
import { TraceEvent } from "@dxos/functions-runtime";
import { DXN } from "@dxos/keys";
import { useQuery as useQuery2 } from "@dxos/react-client/echo";
import { Toolbar } from "@dxos/react-ui";
import { SyntaxHighlighter as SyntaxHighlighter2 } from "@dxos/react-ui-syntax-highlighter";
import { Tabs } from "@dxos/react-ui-tabs";
import { mx as mx2 } from "@dxos/ui-theme";

// src/panels/edge/InvocationTracePanel/ExceptionPanel.tsx
import React, { useMemo } from "react";
import { Callout } from "@dxos/react-ui";
import { mx } from "@dxos/ui-theme";
var ExceptionPanel = ({ objects }) => {
  const errorLogs = useMemo(() => {
    if (!objects?.length) {
      return [];
    }
    return objects.flatMap((event) => event.logs.filter((log) => log.level === "error").map((log) => ({
      ...log,
      eventId: event.id
    }))).sort((a, b) => a.timestamp - b.timestamp);
  }, [
    objects
  ]);
  if (errorLogs.length === 0) {
    return /* @__PURE__ */ React.createElement("div", {
      role: "none",
      className: mx("flex is-full items-center justify-center m-4")
    }, /* @__PURE__ */ React.createElement(Callout.Root, null, /* @__PURE__ */ React.createElement(Callout.Title, null, "No exceptions.")));
  }
  return /* @__PURE__ */ React.createElement("div", {
    className: mx("p-1 overflow-auto")
  }, errorLogs.map((log, index) => {
    const context = log.context;
    const time = new Date(log.timestamp).toLocaleString();
    const errorInfo = context?.err || {};
    const errorName = errorInfo._id || "Error";
    const errorMessage = errorInfo.message || log.message;
    const stack = context?.stack;
    return /* @__PURE__ */ React.createElement("div", {
      key: `log-${index}`,
      className: "mb-2 border border-red-200 dark:border-red-900 rounded overflow-hidden"
    }, /* @__PURE__ */ React.createElement("div", {
      className: "p-2"
    }, /* @__PURE__ */ React.createElement("div", {
      className: "flex justify-between items-start"
    }, /* @__PURE__ */ React.createElement("div", {
      className: "font-medium"
    }, errorName), /* @__PURE__ */ React.createElement("div", {
      className: "text-xs font-mono opacity-80"
    }, time)), /* @__PURE__ */ React.createElement("div", {
      className: "mt-1 text-xs font-mono whitespace-pre-wrap"
    }, errorMessage)), stack && /* @__PURE__ */ React.createElement("pre", {
      className: "p-3 text-xs bg-neutral-50 dark:bg-neutral-900 overflow-auto"
    }, stack));
  }));
};

// src/panels/edge/InvocationTracePanel/ExecutionGraphPanel.tsx
import React2 from "react";
import { Timeline, useExecutionGraph } from "@dxos/react-ui-components";
var ExecutionGraphPanel = ({ queue }) => {
  const { branches, commits } = useExecutionGraph(queue);
  return /* @__PURE__ */ React2.createElement("div", {
    className: "flex flex-col bs-full"
  }, /* @__PURE__ */ React2.createElement(Timeline, {
    branches,
    commits
  }));
};

// src/panels/edge/InvocationTracePanel/hooks.ts
import { useCallback, useEffect, useMemo as useMemo2, useState } from "react";
import { Filter, Obj } from "@dxos/echo";
import { Function, Script, getUserFunctionIdInMetadata } from "@dxos/functions";
import { InvocationOutcome } from "@dxos/functions-runtime";
import { createInvocationSpans } from "@dxos/functions-runtime";
import { useQuery, useQueue } from "@dxos/react-client/echo";

// src/panels/edge/InvocationTracePanel/utils.ts
var formatDuration = (duration) => {
  return `${(duration / 1e3).toFixed(2)}`;
};
var getUuidFromDxn = (dxn) => {
  if (!dxn) {
    return void 0;
  }
  const dxnString = dxn.toString();
  const dxnParts = dxnString.split(":");
  return dxnParts.at(-1);
};

// src/panels/edge/InvocationTracePanel/hooks.ts
var useFunctionNameResolver = ({ db }) => {
  const functions = useQuery(db, Filter.type(Function.Function));
  return useCallback((invocationTargetId) => {
    if (!invocationTargetId) {
      return void 0;
    }
    const uuidPart = getUuidFromDxn(invocationTargetId);
    const matchingFunction = functions.find((fn) => getUserFunctionIdInMetadata(Obj.getMeta(fn)) === uuidPart);
    return matchingFunction?.name;
  }, [
    functions
  ]);
};
var useInvocationTargetsForScript = (target) => {
  const db = Obj.instanceOf(Script.Script, target) ? Obj.getDatabase(target) : void 0;
  const functions = useQuery(db, Filter.type(Function.Function));
  return useMemo2(() => {
    if (!Obj.instanceOf(Script.Script, target)) {
      return void 0;
    }
    return new Set(functions.filter((fn) => fn.source?.target?.id === target.id).map((fn) => getUserFunctionIdInMetadata(Obj.getMeta(fn))));
  }, [
    functions,
    target
  ]);
};
var useInvocationSpans = ({ queueDxn, target }) => {
  const functionsForScript = useInvocationTargetsForScript(target);
  const invocationsQueue = useQueue(queueDxn, {
    pollInterval: 1e3
  });
  const invocationSpans = useMemo2(() => createInvocationSpans(invocationsQueue?.objects), [
    invocationsQueue?.objects
  ]);
  const scopedInvocationSpans = useMemo2(() => {
    if (functionsForScript) {
      return invocationSpans.filter((span) => {
        if (!span.invocationTarget) {
          return false;
        }
        const targetId = span.invocationTarget.dxn;
        const uuidPart = getUuidFromDxn(targetId);
        return uuidPart ? functionsForScript?.has(uuidPart) : false;
      });
    } else if (target) {
      return invocationSpans.filter((span) => span.invocationTarget?.dxn.toString() === Obj.getDXN(target).toString());
    }
    return invocationSpans;
  }, [
    functionsForScript,
    target,
    invocationSpans
  ]);
  const [_, update] = useState({});
  useEffect(() => {
    if (scopedInvocationSpans.some((span) => span.outcome === InvocationOutcome.PENDING)) {
      const interval = setInterval(() => update({}), 1e3);
      return () => clearInterval(interval);
    }
  }, [
    scopedInvocationSpans
  ]);
  return scopedInvocationSpans;
};

// src/panels/edge/InvocationTracePanel/LogPanel.tsx
import React3, { useMemo as useMemo3 } from "react";
var LogPanel = ({ objects }) => {
  const rows = useMemo3(() => {
    if (!objects?.length) {
      return [];
    }
    return objects.flatMap((event) => event.logs.map((log, idx) => ({
      id: `${event.id}-${idx}`,
      time: new Date(log.timestamp),
      level: log.level,
      message: log.message,
      context: safeStringify(log.context)
    })));
  }, [
    objects
  ]);
  return /* @__PURE__ */ React3.createElement("div", {
    className: "flex grow min-bs-0 min-is-0 is-full bs-full overflow-auto"
  }, /* @__PURE__ */ React3.createElement("table", {
    className: "table-fixed min-w-full text-xs border-collapse"
  }, /* @__PURE__ */ React3.createElement("thead", {
    className: "sticky top-0 z-10"
  }, /* @__PURE__ */ React3.createElement("tr", null, /* @__PURE__ */ React3.createElement("th", {
    className: "text-left px-2 py-1 w-40 border border-separator"
  }, "Started"), /* @__PURE__ */ React3.createElement("th", {
    className: "text-left px-2 py-1 w-24 border border-separator"
  }, "Level"), /* @__PURE__ */ React3.createElement("th", {
    className: "text-left px-2 py-1 w-80 border border-separator"
  }, "Message"), /* @__PURE__ */ React3.createElement("th", {
    className: "text-left px-2 py-1 border border-separator"
  }, "Context"))), /* @__PURE__ */ React3.createElement("tbody", null, rows.map((row) => {
    const level = String(row.level).toUpperCase();
    return /* @__PURE__ */ React3.createElement("tr", {
      key: row.id,
      className: "align-top"
    }, /* @__PURE__ */ React3.createElement("td", {
      className: "px-2 py-1 whitespace-nowrap border border-separator"
    }, row.time.toLocaleString()), /* @__PURE__ */ React3.createElement("td", {
      className: "px-2 py-1 font-mono border border-separator"
    }, level), /* @__PURE__ */ React3.createElement("td", {
      className: "px-2 py-1 truncate max-w-[20rem] border border-separator"
    }, row.message), /* @__PURE__ */ React3.createElement("td", {
      className: "px-2 py-1 font-mono text-[10px] whitespace-pre-wrap break-words border border-separator"
    }, row.context));
  }))));
};
var safeStringify = (value2) => {
  try {
    if (value2 == null) {
      return "";
    }
    const seen = /* @__PURE__ */ new WeakSet();
    return JSON.stringify(value2, (key, val) => {
      if (typeof val === "object" && val !== null) {
        if (seen.has(val)) {
          return "[Circular]";
        }
        seen.add(val);
      }
      return val;
    }, 2);
  } catch {
    return "[Unserializable]";
  }
};

// src/panels/edge/InvocationTracePanel/RawDataPanel.tsx
import React4, { useMemo as useMemo4 } from "react";
import { SyntaxHighlighter, createElement } from "@dxos/react-ui-syntax-highlighter";
var RawDataPanel = ({ classNames, span, objects }) => {
  const combinedData = useMemo4(() => {
    return {
      span,
      traceEvents: objects ?? []
    };
  }, [
    span,
    objects
  ]);
  const rowRenderer = ({ rows, stylesheet, useInlineStyles }) => {
    return rows.map((row, index) => {
      return createElement({
        node: row,
        stylesheet,
        style: {},
        useInlineStyles,
        key: index
      });
    });
  };
  return /* @__PURE__ */ React4.createElement(SyntaxHighlighter, {
    language: "json",
    className: classNames,
    renderer: rowRenderer
  }, JSON.stringify(combinedData, null, 2));
};

// src/panels/edge/InvocationTracePanel/InvocationTraceContainer.tsx
var InvocationTraceContainer = ({ db, queueDxn, detailAxis = "inline", showSpaceSelector = false, target }) => {
  const resolver = useFunctionNameResolver({
    db
  });
  const invocationSpans = useInvocationSpans({
    queueDxn,
    target
  });
  const [selectedId, setSelectedId] = useState2();
  const selectedInvocation = useMemo5(() => {
    if (!selectedId) {
      return void 0;
    }
    return invocationSpans.find((span) => selectedId === span.id);
  }, [
    selectedId,
    invocationSpans
  ]);
  const properties = useMemo5(() => {
    function* generateProperties() {
      if (target === void 0) {
        yield {
          name: "target",
          title: "Target",
          format: Format.TypeFormat.String,
          size: 200
        };
      }
      yield* [
        {
          name: "time",
          title: "Started",
          format: Format.TypeFormat.DateTime,
          sort: "desc",
          size: 194
        },
        {
          name: "status",
          title: "Status",
          format: Format.TypeFormat.SingleSelect,
          size: 110,
          config: {
            options: [
              {
                id: "pending",
                title: "Pending",
                color: "blue"
              },
              {
                id: "success",
                title: "Success",
                color: "emerald"
              },
              {
                id: "failure",
                title: "Failure",
                color: "red"
              },
              {
                id: "unknown",
                title: "Unknown",
                color: "neutral"
              }
            ]
          }
        },
        {
          name: "duration",
          title: "Duration",
          format: Format.TypeFormat.Duration,
          size: 110
        },
        {
          name: "queue",
          title: "Queue",
          format: Format.TypeFormat.String,
          // TODO(burdon): Add formatter.
          // formatter: (value: string) => value.split(':').pop(),
          size: 400
        }
      ];
    }
    return [
      ...generateProperties()
    ];
  }, [
    target
  ]);
  const rows = useMemo5(() => {
    return invocationSpans.map((invocation) => {
      const status = invocation.outcome;
      const targetDxn = invocation.invocationTarget?.dxn ?? (invocation.invocationTarget && "/" in invocation.invocationTarget ? DXN.parse(invocation.invocationTarget["/"]) : void 0);
      return {
        id: invocation.id,
        target: resolver(targetDxn),
        // TODO(burdon): Change to timestamp?
        time: new Date(invocation.timestamp),
        duration: formatDuration(invocation.duration),
        status,
        queue: invocation.invocationTraceQueue?.dxn?.toString() ?? (invocation.invocationTraceQueue && "/" in invocation.invocationTraceQueue ? invocation.invocationTraceQueue["/"] : "unknown"),
        _original: invocation
      };
    });
  }, [
    invocationSpans,
    resolver
  ]);
  const handleRowClick = useCallback2((row) => {
    if (!row) {
      return;
    }
    setSelectedId(row.id);
  }, []);
  const gridLayout = useMemo5(() => {
    if (selectedInvocation) {
      switch (detailAxis) {
        case "inline":
          return "grid grid-cols-[1fr_30rem]";
        case "block":
          return "grid grid-rows-[1fr_2fr]";
      }
    }
    return "grid grid-cols-1";
  }, [
    selectedInvocation,
    detailAxis
  ]);
  const features = useMemo5(() => ({
    selection: {
      enabled: true,
      mode: "single"
    }
  }), []);
  return /* @__PURE__ */ React5.createElement(PanelContainer, {
    toolbar: showSpaceSelector ? /* @__PURE__ */ React5.createElement(Toolbar.Root, {
      classNames: "border-be border-subduedSeparator"
    }, /* @__PURE__ */ React5.createElement(DataSpaceSelector, null)) : void 0
  }, /* @__PURE__ */ React5.createElement("div", {
    className: mx2("bs-full", gridLayout)
  }, /* @__PURE__ */ React5.createElement("div", {
    className: "overflow-auto"
  }, /* @__PURE__ */ React5.createElement("table", {
    className: "table-fixed min-w-full text-xs border-collapse"
  }, /* @__PURE__ */ React5.createElement("thead", {
    className: "sticky top-0 z-10 bg-background"
  }, /* @__PURE__ */ React5.createElement("tr", null, /* @__PURE__ */ React5.createElement("th", null, "ID"), /* @__PURE__ */ React5.createElement("th", null, "Target"), /* @__PURE__ */ React5.createElement("th", null, "Started"), /* @__PURE__ */ React5.createElement("th", null, "Duration"), /* @__PURE__ */ React5.createElement("th", null, "Status"), /* @__PURE__ */ React5.createElement("th", null, "Queue"))), /* @__PURE__ */ React5.createElement("tbody", null, rows.toSorted((a, b) => b.time.getTime() - a.time.getTime()).slice(0, 50).map((row) => /* @__PURE__ */ React5.createElement("tr", {
    key: row.id,
    role: "button",
    tabIndex: 0,
    "aria-selected": selectedId === row.id,
    onClick: () => handleRowClick(row),
    onKeyDown: (e) => {
      if (e.key === "Enter" || e.key === " ") {
        e.preventDefault();
        handleRowClick(row);
      }
    },
    className: "cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
  }, /* @__PURE__ */ React5.createElement("td", {
    className: "px-2 py-1 whitespace-nowrap border border-separator"
  }, row.id), /* @__PURE__ */ React5.createElement("td", {
    className: "px-2 py-1 whitespace-nowrap border border-separator"
  }, row.target), /* @__PURE__ */ React5.createElement("td", {
    className: "px-2 py-1 whitespace-nowrap border border-separator"
  }, row.time.toLocaleString()), /* @__PURE__ */ React5.createElement("td", {
    className: "px-2 py-1 whitespace-nowrap border border-separator"
  }, row.duration), /* @__PURE__ */ React5.createElement("td", {
    className: "px-2 py-1 whitespace-nowrap border border-separator"
  }, row.status), /* @__PURE__ */ React5.createElement("td", {
    className: "px-2 py-1 whitespace-nowrap border border-separator"
  }, row.queue)))))), selectedInvocation && /* @__PURE__ */ React5.createElement(Selected, {
    span: selectedInvocation
  })));
};
var Selected = ({ span }) => {
  const [activeTab, setActiveTab] = useState2("input");
  const queue = span.invocationTraceQueue?.target;
  const objects = useQuery2(queue, Filter2.everything());
  const contents = Array.head(objects).pipe(Option.getOrUndefined, Match.value, Match.not(Match.defined, () => "unknown"), Match.when(Schema.is(TraceEvent), () => "logs"), Match.orElse(() => "execution-graph"));
  const isLogQueue = "logs" === contents || objects.length === 0;
  return /* @__PURE__ */ React5.createElement("div", {
    className: "grid grid-cols-1 grid-rows-[min-content_1fr] bs-full min-bs-0 border-separator"
  }, /* @__PURE__ */ React5.createElement(Tabs.Root, {
    classNames: 'grid grid-rows-[min-content_1fr] min-bs-0 [&>[role="tabpanel"]]:min-bs-0 [&>[role="tabpanel"][data-state="active"]]:grid border-bs border-separator',
    orientation: "horizontal",
    value: activeTab,
    onValueChange: setActiveTab
  }, /* @__PURE__ */ React5.createElement(Tabs.Tablist, {
    classNames: "border-be border-separator"
  }, /* @__PURE__ */ React5.createElement(Tabs.Tab, {
    value: "input"
  }, "Input"), isLogQueue && /* @__PURE__ */ React5.createElement(Tabs.Tab, {
    value: "logs"
  }, "Logs"), isLogQueue && /* @__PURE__ */ React5.createElement(Tabs.Tab, {
    value: "errors"
  }, "Error logs"), isLogQueue && /* @__PURE__ */ React5.createElement(Tabs.Tab, {
    value: "raw"
  }, "Raw"), span.error && /* @__PURE__ */ React5.createElement(Tabs.Tab, {
    value: "failure"
  }, "Failure"), contents === "execution-graph" && /* @__PURE__ */ React5.createElement(Tabs.Tab, {
    value: "execution-graph"
  }, "Execution Graph")), /* @__PURE__ */ React5.createElement(Tabs.Tabpanel, {
    value: "input"
  }, /* @__PURE__ */ React5.createElement(SyntaxHighlighter2, {
    language: "json"
  }, JSON.stringify(span.input, null, 2))), isLogQueue && /* @__PURE__ */ React5.createElement(Tabs.Tabpanel, {
    value: "logs"
  }, /* @__PURE__ */ React5.createElement(LogPanel, {
    objects
  })), isLogQueue && /* @__PURE__ */ React5.createElement(Tabs.Tabpanel, {
    value: "errors"
  }, /* @__PURE__ */ React5.createElement(ExceptionPanel, {
    objects
  })), isLogQueue && /* @__PURE__ */ React5.createElement(Tabs.Tabpanel, {
    value: "raw",
    classNames: "min-bs-0 min-is-0 is-full overflow-auto"
  }, /* @__PURE__ */ React5.createElement(RawDataPanel, {
    classNames: "text-xs",
    span,
    objects
  })), span.error && /* @__PURE__ */ React5.createElement(Tabs.Tabpanel, {
    value: "failure"
  }, /* @__PURE__ */ React5.createElement(SpanErrorPanel, {
    exception: span.error
  })), contents === "execution-graph" && /* @__PURE__ */ React5.createElement(Tabs.Tabpanel, {
    value: "execution-graph"
  }, /* @__PURE__ */ React5.createElement(ExecutionGraphPanel, {
    queue
  }))));
};
var SpanErrorPanel = ({ exception }) => {
  return /* @__PURE__ */ React5.createElement("div", {
    className: "text-xs whitespace-pre-wrap m-4"
  }, /* @__PURE__ */ React5.createElement("div", null, "Code: ", exception.name), /* @__PURE__ */ React5.createElement("div", null, "Message: ", exception.message), /* @__PURE__ */ React5.createElement("div", null), /* @__PURE__ */ React5.createElement("div", null, "Stack: ", exception.stack), /* @__PURE__ */ React5.createElement("div", null), exception.cause && /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement("div", null, "Caused by:"), /* @__PURE__ */ React5.createElement(SpanErrorPanel, {
    exception: exception.cause
  })));
};
var InvocationTraceContainer_default = InvocationTraceContainer;

export {
  InvocationTraceContainer,
  InvocationTraceContainer_default
};
//# sourceMappingURL=chunk-6J2WZH3G.mjs.map
