diff --git a/frontend/packages/components/src/actors/actor-build.tsx b/frontend/packages/components/src/actors/actor-build.tsx
deleted file mode 100644
index 8416f0dd4e..0000000000
--- a/frontend/packages/components/src/actors/actor-build.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import { Dd, DiscreteCopyButton, Dl, Dt, Flex } from "@rivet-gg/components";
-import { formatISO } from "date-fns";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import { useCallback } from "react";
-import { type Actor, type ActorAtom, actorBuildsAtom } from "./actor-context";
-import { ActorTags } from "./actor-tags";
-
-const buildIdSelector = (a: Actor) => a.runtime?.build;
-
-interface ActorBuildProps {
- actor: ActorAtom;
-}
-
-export function ActorBuild({ actor }: ActorBuildProps) {
- const buildId = useAtomValue(selectAtom(actor, buildIdSelector));
-
- const data = useAtomValue(
- selectAtom(
- actorBuildsAtom,
- useCallback(
- (builds) => {
- return builds.find((build) => build.id === buildId);
- },
- [buildId],
- ),
- ),
- );
-
- if (!data) {
- return null;
- }
-
- return (
-
-
-
Build
-
-
-
- - ID
- -
-
- {data.id}
-
-
- - Created
- -
-
- {formatISO(data.createdAt)}
-
-
- - Tags
- -
-
-
-
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-config-tab.tsx b/frontend/packages/components/src/actors/actor-config-tab.tsx
deleted file mode 100644
index 2a05406888..0000000000
--- a/frontend/packages/components/src/actors/actor-config-tab.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Button, DocsSheet, ScrollArea } from "@rivet-gg/components";
-import { Icon, faBooks } from "@rivet-gg/icons";
-import type { ActorAtom } from "./actor-context";
-import { ActorGeneral } from "./actor-general";
-import { ActorNetwork } from "./actor-network";
-import { ActorRuntime } from "./actor-runtime";
-
-interface ActorConfigTabProps {
- actor: ActorAtom;
-}
-
-export function ActorConfigTab(props: ActorConfigTabProps) {
- return (
-
-
-
- }
- >
- Documentation
-
-
-
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-connections-tab.tsx b/frontend/packages/components/src/actors/actor-connections-tab.tsx
deleted file mode 100644
index 42be046a7c..0000000000
--- a/frontend/packages/components/src/actors/actor-connections-tab.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { LiveBadge, ScrollArea } from "@rivet-gg/components";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import type { Actor, ActorAtom } from "./actor-context";
-import { ActorObjectInspector } from "./console/actor-inspector";
-import {
- useActorConnections,
- useActorWorkerStatus,
-} from "./worker/actor-worker-context";
-
-const selector = (a: Actor) => a.destroyedAt;
-
-interface ActorConnectionsTabProps {
- actor: ActorAtom;
-}
-
-export function ActorConnectionsTab({ actor }: ActorConnectionsTabProps) {
- const destroyedAt = useAtomValue(selectAtom(actor, selector));
- const status = useActorWorkerStatus();
-
- const connections = useActorConnections();
-
- if (destroyedAt) {
- return (
-
- Connections Preview is unavailable for inactive Actors.
-
- );
- }
-
- if (status.type === "error") {
- return (
-
- Connections Preview is currently unavailable.
-
- See console/logs for more details.
-
- );
- }
-
- if (status.type !== "ready") {
- return (
-
- Loading connections...
-
- );
- }
-
- return (
-
-
-
-
-
-
[c.id, c]))}
- expandPaths={["$"]}
- />
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-context.tsx b/frontend/packages/components/src/actors/actor-context.tsx
deleted file mode 100644
index 7aab38c8fa..0000000000
--- a/frontend/packages/components/src/actors/actor-context.tsx
+++ /dev/null
@@ -1,437 +0,0 @@
-import type { Rivet } from "@rivet-gg/api";
-import { isAfter, isBefore } from "date-fns";
-import { type Atom, atom } from "jotai";
-import { atomFamily, splitAtom } from "jotai/utils";
-import { toRecord } from "../lib/utils";
-import { FilterOp, type FilterValue } from "../ui/filters";
-import { ACTOR_FRAMEWORK_TAG_VALUE } from "./actor-tags";
-
-export enum ActorFeature {
- Logs = "logs",
- Config = "config",
- Connections = "connections",
- State = "state",
- Console = "console",
- Runtime = "runtime",
- Metrics = "metrics",
- InspectReconnectNotification = "inspect_reconnect_notification",
-}
-
-export type Actor = Omit<
- Rivet.actor.Actor,
- "createdAt" | "runtime" | "lifecycle" | "network" | "resources"
-> & {
- status: "unknown" | "starting" | "running" | "stopped" | "crashed";
-
- lifecycle?: Rivet.actor.Lifecycle;
- endpoint?: string;
- logs: LogsAtom;
- metrics: MetricsAtom;
- network?: Rivet.actor.Network | null;
- resources?: Rivet.actor.Resources | null;
- runtime?: Rivet.actor.Runtime | null;
- destroy?: DestroyActorAtom;
- destroyTs?: Date;
- createdAt?: Date;
- features?: ActorFeature[];
-};
-
-export type Logs = {
- id: string;
- level: "error" | "info";
- timestamp: Date;
- line: string;
- message: string;
- properties: Record;
-}[];
-
-export type Metrics = Record;
-
-export type Build = Rivet.actor.Build;
-export type DestroyActor = {
- isDestroying: boolean;
- destroy: () => Promise;
-};
-
-export type ActorAtom = Atom;
-export type LogsAtom = Atom<{
- logs: Logs;
- // query status
- status: string;
-}>;
-export type MetricsAtom = Atom<{
- metrics: Metrics;
- updatedAt: number;
- // query status
- status: string;
-}>;
-export type BuildAtom = Atom;
-export type DestroyActorAtom = Atom;
-
-export type CreateActor = {
- create: (values: {
- endpoint: string;
- id: string;
- tags: Record;
- region?: string;
- params?: Record;
- }) => Promise;
- isCreating: boolean;
- endpoint: string | null;
-};
-
-export type Region = Rivet.actor.Region;
-
-// global atoms
-export const currentActorIdAtom = atom(undefined);
-
-export const currentActorQueryAtom = atom<{
- isLoading: boolean;
- error: string | null;
-}>({
- isLoading: false,
- error: null,
-});
-export const actorsQueryAtom = atom<{
- isLoading: boolean;
- error: string | null;
-}>({
- isLoading: false,
- error: null,
-});
-export const actorsAtom = atom([]);
-export const actorFiltersAtom = atom<{
- tags: FilterValue;
- region: FilterValue;
- createdAt: FilterValue;
- destroyedAt: FilterValue;
- status: FilterValue;
- devMode: FilterValue;
-}>({
- tags: undefined,
- region: undefined,
- createdAt: undefined,
- destroyedAt: undefined,
- status: undefined,
- devMode: undefined,
-});
-export const actorsPaginationAtom = atom({
- hasNextPage: false,
- isFetchingNextPage: false,
- fetchNextPage: () => {},
-});
-
-export const actorRegionsAtom = atom([
- {
- id: "default",
- name: "Default",
- },
-]);
-
-export const actorBuildsAtom = atom([]);
-
-export const actorEnvironmentAtom = atom<{
- projectNameId: string;
- environmentNameId: string;
-} | null>(null);
-
-export const actorMetricsTimeWindowAtom = atom(15 * 60 * 1000); // Default to 15 minutes
-
-export const actorsInternalFilterAtom = atom<{
- fn: (actor: Actor) => boolean;
-}>();
-
-// derived atoms
-
-export const currentActorRegionAtom = atom((get) => {
- const actorAtom = get(currentActorAtom);
- if (!actorAtom) {
- return undefined;
- }
- const regions = get(actorRegionsAtom);
- const actor = get(actorAtom);
- return regions.find((region) => region.id === actor.region);
-});
-export const filteredActorsAtom = atom((get) => {
- const filters = get(actorFiltersAtom);
- const actors = get(actorsAtom);
-
- const isActorInternal = get(actorsInternalFilterAtom)?.fn;
-
- return actors.filter((actor) => {
- const satisfiesFilters = Object.entries(filters).every(
- ([key, filter]) => {
- if (filter === undefined) {
- return true;
- }
- if (key === "tags") {
- const filterTags = filter.value.map((tag) =>
- tag.split("="),
- );
- const tags = toRecord(actor.tags);
-
- if (filter.operator === FilterOp.NOT_EQUAL) {
- return Object.entries(tags).every(
- ([tagKey, tagValue]) => {
- return filterTags.every(
- ([filterKey, filterValue]) => {
- if (filterKey === tagKey) {
- if (filterValue === "*") {
- return false;
- }
- return tagValue !== filterValue;
- }
- return true;
- },
- );
- },
- );
- }
-
- return Object.entries(tags).some(([tagKey, tagValue]) => {
- return filterTags.some(([filterKey, filterValue]) => {
- if (filterKey === tagKey) {
- if (filterValue === "*") {
- return true;
- }
- return tagValue === filterValue;
- }
- return false;
- });
- });
- }
-
- if (key === "region") {
- if (filter.operator === FilterOp.NOT_EQUAL) {
- return !filter.value.includes(actor.region);
- }
-
- return filter.value.includes(actor.region);
- }
-
- if (key === "createdAt") {
- if (actor.createdAt === undefined) {
- return false;
- }
- const createdAt = new Date(actor.createdAt);
-
- if (filter.operator === FilterOp.AFTER) {
- return isAfter(createdAt, +filter.value[0]);
- }
- if (filter.operator === FilterOp.BEFORE) {
- return isBefore(createdAt, +filter.value[0]);
- }
- if (filter.operator === FilterOp.BETWEEN) {
- return (
- isAfter(createdAt, +filter.value[0]) &&
- isBefore(createdAt, +filter.value[1])
- );
- }
- return false;
- }
-
- if (key === "destroyedAt") {
- if (actor.destroyTs === undefined) {
- return false;
- }
- const destroyedAt = new Date(actor.destroyTs);
-
- if (filter.operator === FilterOp.AFTER) {
- return isAfter(destroyedAt, +filter.value[0]);
- }
- if (filter.operator === FilterOp.BEFORE) {
- return isBefore(destroyedAt, +filter.value[0]);
- }
- if (filter.operator === FilterOp.BETWEEN) {
- return (
- isAfter(destroyedAt, +filter.value[0]) &&
- isBefore(destroyedAt, +filter.value[1])
- );
- }
- return false;
- }
-
- if (key === "status") {
- if (filter.operator === FilterOp.NOT_EQUAL) {
- return !filter.value.includes(actor.status);
- }
-
- return filter.value.includes(actor.status);
- }
-
- return true;
- },
- );
-
- const isInternal =
- toRecord(actor.tags).owner === "rivet" ||
- (isActorInternal?.(actor) ?? false);
-
- return (
- satisfiesFilters && ((isInternal && filters.devMode) || !isInternal)
- );
- });
-});
-export const actorsAtomsAtom = splitAtom(
- filteredActorsAtom,
- (actor) => actor.id,
-);
-export const actorsCountAtom = atom((get) => get(actorsAtom).length);
-export const filteredActorsCountAtom = atom(
- (get) => get(filteredActorsAtom).length,
-);
-
-export const currentActorAtom = atom((get) => {
- const actorId = get(currentActorIdAtom);
- return get(actorsAtomsAtom).find((actor) => get(actor).id === actorId);
-});
-
-export const isCurrentActorAtom = atomFamily((actor: ActorAtom) =>
- atom((get) => {
- const actorId = get(currentActorIdAtom);
- return get(actor).id === actorId;
- }),
-);
-
-export const actorFiltersCountAtom = atom((get) => {
- const filters = get(actorFiltersAtom);
- return Object.values(filters).filter((value) => value !== undefined).length;
-});
-
-// tags created by the user, not from the server
-export const actorCustomTagValues = atom([]);
-export const actorCustomTagKeys = atom([]);
-
-const actorCustomTagsAtom = atom<{ keys: string[]; values: string[] }>(
- (get) => {
- const keys = get(actorCustomTagKeys);
- const values = get(actorCustomTagValues);
-
- return { keys, values };
- },
- // @ts-expect-error
- (get, set, value: { key: string; value: string }) => {
- set(actorCustomTagKeys, (keys) => {
- const newKeys = [...keys];
- const index = newKeys.indexOf(value.key);
- if (index === -1) {
- newKeys.push(value.key);
- }
- return newKeys;
- });
- set(actorCustomTagValues, (values) => {
- const newValues = [...values];
- const index = newValues.indexOf(value.value);
- if (index === -1) {
- newValues.push(value.value);
- }
- return newValues;
- });
- },
-);
-
-export const createActorAtom = atom({
- endpoint: null,
- isCreating: false,
- create: async () => {},
-});
-
-export const actorManagerEndpointAtom = atom((get) => {
- return get(createActorAtom)?.endpoint ?? null;
-});
-
-export const actorTagsAtom = atom((get) => {
- const actorTags = get(actorsAtom).flatMap((actor) =>
- Object.entries(toRecord(actor.tags)).map(([key, value]) => ({
- key,
- value: value as string,
- })),
- );
-
- const keys = new Set();
- const values = new Set();
-
- for (const { key, value } of actorTags) {
- keys.add(key);
- values.add(value);
- }
-
- const customTags = get(actorCustomTagsAtom);
-
- for (const key of customTags.keys) {
- keys.add(key);
- }
-
- for (const value of customTags.values) {
- values.add(value);
- }
-
- const allTags = [];
-
- for (const key of keys) {
- for (const value of values) {
- allTags.push({ key, value });
- }
- }
-
- return allTags;
-});
-
-export const actorTagValuesAtom = atom((get) => {
- const tags = get(actorTagsAtom);
- const values = new Set();
- for (const tag of tags) {
- values.add(tag.value);
- }
- return [...values];
-});
-
-export const actorTagKeysAtom = atom((get) => {
- const tags = get(actorTagsAtom);
- const keys = new Set();
- for (const tag of tags) {
- keys.add(tag.key);
- }
- return [...keys];
-});
-
-export const actorBuildsCountAtom = atom((get) => {
- return get(actorBuildsAtom).length;
-});
-
-const commonActorFeatures = [
- ActorFeature.Logs,
- ActorFeature.Config,
- ActorFeature.Runtime,
- // ActorFeature.Metrics,
- // ActorFeature.InspectReconnectNotification,
-];
-
-export const currentActorFeaturesAtom = atom((get) => {
- const atom = get(currentActorAtom);
- if (!atom) {
- return [];
- }
-
- const actor = get(atom);
-
- // actors from hub
- if (!actor.features) {
- const tags = toRecord(actor.tags);
- if (tags.framework === ACTOR_FRAMEWORK_TAG_VALUE) {
- if (tags.name === "manager") {
- return commonActorFeatures;
- }
- return [
- ...commonActorFeatures,
- // ActorFeature.Connections,
- // ActorFeature.State,
- // ActorFeature.Console,
- // ActorFeature.InspectReconnectNotification,
- ];
- }
- return [...commonActorFeatures, ActorFeature.Metrics];
- }
-
- return actor.features;
-});
diff --git a/frontend/packages/components/src/actors/actor-cpu-stats.tsx b/frontend/packages/components/src/actors/actor-cpu-stats.tsx
deleted file mode 100644
index 2cf694f339..0000000000
--- a/frontend/packages/components/src/actors/actor-cpu-stats.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-import { format } from "date-fns";
-import { useId } from "react";
-import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
-import { timing } from "../lib/timing";
-import {
- type ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "../ui/chart";
-
-interface ActorCpuStatsProps {
- interval?: number;
- cpu: number[];
- metricsAt: number;
- syncId?: string;
- isRunning?: boolean;
-}
-
-const chartConfig = {
- value: {
- color: "hsl(var(--chart-1))",
- label: "CPU Usage",
- },
-} satisfies ChartConfig;
-
-export function ActorCpuStats({
- interval = 15,
- cpu,
- metricsAt,
- syncId,
- isRunning = true,
-}: ActorCpuStatsProps) {
- // Filter out trailing zeros in the last 15 seconds only if actor is still running
- let filteredCpu = [...cpu];
- if (isRunning) {
- const secondsToCheck = 15;
- const pointsToCheck = Math.ceil(secondsToCheck / interval);
-
- // Find the last non-zero value and cut off any zeros after it
- for (
- let i = filteredCpu.length - 1;
- i >= Math.max(0, filteredCpu.length - pointsToCheck);
- i--
- ) {
- if (filteredCpu[i] === 0) {
- filteredCpu = filteredCpu.slice(0, i);
- } else {
- break;
- }
- }
- }
-
- const data = filteredCpu.map((value, i) => {
- let cpuPercent = 0;
-
- // Calculate CPU percentage using delta time between ticks
- if (i > 0) {
- const currentCpuTime = value;
- const previousCpuTime = filteredCpu[i - 1];
- const deltaTime = interval; // seconds between measurements
-
- // CPU percentage = (cpu_time_delta / time_delta) * 100
- // This gives us the percentage of CPU time used in the interval
- if (currentCpuTime >= previousCpuTime) {
- cpuPercent = Math.min(
- ((currentCpuTime - previousCpuTime) / deltaTime) * 100,
- 100,
- );
- }
- }
-
- return {
- x: `${(filteredCpu.length - i) * -interval}`,
- value: cpuPercent / 100, // Convert to 0-1 range for chart
- config: {
- label: new Date(
- metricsAt -
- (filteredCpu.length - i) * timing.seconds(interval),
- ),
- },
- };
- });
-
- const id = useId();
-
- const fillId = `fill-${id}`;
- return (
-
-
-
-
- `${value * 100}%`}
- />
- {
- return format(label, "HH:mm:ss");
- }}
- valueFormatter={(value) => {
- if (typeof value !== "number") {
- return "n/a";
- }
- return `${(value * 100).toFixed(2)}%`;
- }}
- />
- }
- />
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-details-settings-button.tsx b/frontend/packages/components/src/actors/actor-details-settings-button.tsx
deleted file mode 100644
index 9e4684bd40..0000000000
--- a/frontend/packages/components/src/actors/actor-details-settings-button.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import {
- Button,
- DropdownMenu,
- DropdownMenuCheckboxItem,
- DropdownMenuContent,
- DropdownMenuTrigger,
- WithTooltip,
-} from "@rivet-gg/components";
-import { Icon, faCog } from "@rivet-gg/icons";
-import { useActorDetailsSettings } from "./actor-details-settings";
-
-export function ActorDetailsSettingsButton() {
- const [settings, setSettings] = useActorDetailsSettings();
-
- return (
-
-
-
-
- }
- content="Settings"
- />
-
- {
- setSettings((old) => ({
- ...old,
- showTimestamps: value,
- }));
- }}
- >
- Show timestamps
-
- {
- setSettings((old) => ({
- ...old,
- autoFollowLogs: value,
- }));
- }}
- >
- Auto follow logs when scrolled to bottom
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-details-settings.tsx b/frontend/packages/components/src/actors/actor-details-settings.tsx
deleted file mode 100644
index 622ce862e4..0000000000
--- a/frontend/packages/components/src/actors/actor-details-settings.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import {
- type Dispatch,
- type ReactNode,
- type SetStateAction,
- createContext,
- useContext,
-} from "react";
-import { useLocalStorage } from "usehooks-ts";
-
-export interface Settings {
- showTimestamps: boolean;
- autoFollowLogs: boolean;
-}
-
-export const ActorDetailsSettingsContext = createContext<
- [Settings, Dispatch>, unknown]
->([{ showTimestamps: false, autoFollowLogs: true }, () => {}, {}]);
-
-export const useActorDetailsSettings = () => {
- const value = useContext(ActorDetailsSettingsContext);
- return value;
-};
-
-interface ActorDetailsSettingsProviderProps {
- children: ReactNode;
-}
-
-export const ActorDetailsSettingsProvider = ({
- children,
-}: ActorDetailsSettingsProviderProps) => {
- const localStorage = useLocalStorage("actor-details-settings", {
- showTimestamps: false,
- autoFollowLogs: true,
- });
-
- return (
-
- {children}
-
- );
-};
diff --git a/frontend/packages/components/src/actors/actor-download-logs-button.tsx b/frontend/packages/components/src/actors/actor-download-logs-button.tsx
deleted file mode 100644
index a261529c32..0000000000
--- a/frontend/packages/components/src/actors/actor-download-logs-button.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { Button, WithTooltip } from "@rivet-gg/components";
-import { Icon, faSave } from "@rivet-gg/icons";
-import { useAtomValue } from "jotai";
-import type { ActorAtom } from "./actor-context";
-import type { LogsTypeFilter } from "./actor-logs";
-
-interface ActorDownloadLogsButtonProps {
- actor: ActorAtom;
- typeFilter?: LogsTypeFilter;
- filter?: string;
- onExportLogs?: (
- actorId: string,
- typeFilter?: string,
- filter?: string,
- ) => Promise;
- isExporting?: boolean;
-}
-
-export function ActorDownloadLogsButton({
- actor,
- typeFilter,
- filter,
- onExportLogs,
- isExporting = false,
-}: ActorDownloadLogsButtonProps) {
- const actorData = useAtomValue(actor);
-
- const handleDownload = async () => {
- if (!onExportLogs) {
- console.warn("No export handler provided");
- return;
- }
-
- try {
- await onExportLogs(actorData.id, typeFilter, filter);
- } catch (error) {
- console.error("Failed to export logs:", error);
- }
- };
-
- return (
-
-
-
- }
- />
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-editable-state.tsx b/frontend/packages/components/src/actors/actor-editable-state.tsx
deleted file mode 100644
index e54e474548..0000000000
--- a/frontend/packages/components/src/actors/actor-editable-state.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { Badge, Button, WithTooltip } from "@rivet-gg/components";
-import {
- type CodeMirrorRef,
- EditorView,
- JsonCode,
-} from "@rivet-gg/components/code-mirror";
-import { Icon, faRotateLeft, faSave } from "@rivet-gg/icons";
-import { AnimatePresence, motion } from "framer-motion";
-import { useMemo, useRef, useState } from "react";
-import { ActorStateChangeIndicator } from "./actor-state-change-indicator";
-import type { ContainerState } from "./worker/actor-worker-container";
-import { useActorWorker } from "./worker/actor-worker-context";
-
-const isValidJson = (json: string | null): json is string => {
- if (!json) return false;
- try {
- JSON.parse(json);
- return true;
- } catch {
- return false;
- }
-};
-
-interface ActorEditableStateProps {
- state: ContainerState["state"];
-}
-
-export function ActorEditableState({ state }: ActorEditableStateProps) {
- const container = useActorWorker();
- const [isEditing, setIsEditing] = useState(false);
- const [value, setValue] = useState(null);
-
- const ref = useRef(null);
-
- const formatted = useMemo(() => {
- return JSON.stringify(state.value || "{}", null, 2);
- }, [state.value]);
-
- const isValid = isValidJson(value) ? JSON.parse(value) : false;
-
- return (
- <>
-
-
-
-
- {isEditing ? (
-
-
- Modified
-
-
- }
- content="State has been modified and not saved."
- />
- ) : null}
-
-
{
- container.setState(value || "");
- setIsEditing(false);
- setValue(null);
- }}
- >
-
-
- }
- />
- {
- setValue(null);
- setIsEditing(false);
- }}
- >
-
-
- }
- />
-
-
-
- {
- setValue(value);
- setIsEditing(true);
- }}
- />
-
- >
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-general.tsx b/frontend/packages/components/src/actors/actor-general.tsx
deleted file mode 100644
index b9269fb39e..0000000000
--- a/frontend/packages/components/src/actors/actor-general.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { Dd, DiscreteCopyButton, Dl, Dt, Flex, cn } from "@rivet-gg/components";
-import { formatISO } from "date-fns";
-import equal from "fast-deep-equal";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import type { Actor, ActorAtom } from "./actor-context";
-import { ActorRegion } from "./actor-region";
-import { ActorTags } from "./actor-tags";
-
-const selector = (a: Actor) => ({
- id: a.id,
- tags: a.tags,
- createdAt: a.createdAt,
- destroyedAt: a.destroyedAt,
- region: a.region,
-});
-
-export interface ActorGeneralProps {
- actor: ActorAtom;
-}
-
-export function ActorGeneral({ actor }: ActorGeneralProps) {
- const { id, tags, createdAt, destroyedAt, region } = useAtomValue(
- selectAtom(actor, selector, equal),
- );
-
- return (
-
-
General
-
-
- - Region
- -
-
-
- - ID
- -
-
- {id}
-
-
- - Tags
- -
-
-
-
-
- - Created
- -
-
- {createdAt ? formatISO(createdAt) : "n/a"}
-
-
- - Destroyed
- -
-
- {destroyedAt ? formatISO(destroyedAt) : "n/a"}
-
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-logs-tab.tsx b/frontend/packages/components/src/actors/actor-logs-tab.tsx
deleted file mode 100644
index 7925e94cc0..0000000000
--- a/frontend/packages/components/src/actors/actor-logs-tab.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import { LogsView, ToggleGroup, ToggleGroupItem } from "@rivet-gg/components";
-import { startTransition, useState } from "react";
-import type { ActorAtom } from "./actor-context";
-import { ActorDetailsSettingsButton } from "./actor-details-settings-button";
-import { ActorDownloadLogsButton } from "./actor-download-logs-button";
-import { ActorLogs, type LogsTypeFilter } from "./actor-logs";
-
-interface ActorLogsTabProps {
- actor: ActorAtom;
- onExportLogs?: (
- actorId: string,
- typeFilter?: string,
- filter?: string,
- ) => Promise;
- isExporting?: boolean;
-}
-
-export function ActorLogsTab({
- actor,
- onExportLogs,
- isExporting,
-}: ActorLogsTabProps) {
- const [search, setSearch] = useState("");
- const [logsFilter, setLogsFilter] = useState("all");
-
- return (
-
-
-
-
-
- startTransition(() => setSearch(e.target.value))
- }
- />
-
-
{
- if (!value) {
- setLogsFilter("all");
- } else {
- setLogsFilter(value as LogsTypeFilter);
- }
- }}
- className="gap-0 text-xs p-2 border-r"
- >
-
- all
-
-
- output
-
-
- errors
-
-
-
-
-
-
-
-
- );
-}
-
-ActorLogsTab.Skeleton = () => {
- return (
-
-
-
- );
-};
diff --git a/frontend/packages/components/src/actors/actor-logs.tsx b/frontend/packages/components/src/actors/actor-logs.tsx
deleted file mode 100644
index 11427cab70..0000000000
--- a/frontend/packages/components/src/actors/actor-logs.tsx
+++ /dev/null
@@ -1,219 +0,0 @@
-import { ShimmerLine, VirtualScrollArea } from "@rivet-gg/components";
-import type { Virtualizer } from "@tanstack/react-virtual";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import { memo, useCallback, useEffect, useRef } from "react";
-import { useResizeObserver } from "usehooks-ts";
-import type { Actor, ActorAtom, Logs } from "./actor-context";
-import { useActorDetailsSettings } from "./actor-details-settings";
-import { ActorConsoleMessage } from "./console/actor-console-message";
-
-export type LogsTypeFilter = "all" | "output" | "errors";
-
-const selector = (a: Actor) => a.logs;
-
-interface ActorLogsProps {
- actor: ActorAtom;
- typeFilter?: LogsTypeFilter;
- filter?: string;
-}
-
-export const ActorLogs = memo(
- ({ typeFilter, actor, filter }: ActorLogsProps) => {
- const [settings] = useActorDetailsSettings();
- const follow = useRef(true);
- const shouldFollow = () => settings.autoFollowLogs && follow.current;
-
- const viewport = useRef(null);
- const virtualizer = useRef>(null);
- // Detect if the container has resized (i.e, console was opened)
- useResizeObserver({
- ref: viewport,
- onResize: () => {
- if (shouldFollow()) {
- // https://github.com/TanStack/virtual/issues/537
- requestAnimationFrame(() => {
- virtualizer.current?.scrollToIndex(combined.length, {
- align: "end",
- });
- });
- }
- },
- });
-
- const logsAtom = useAtomValue(selectAtom(actor, selector));
-
- const { logs, status } = useAtomValue(logsAtom);
-
- const combined = filterLogs({
- typeFilter: typeFilter ?? "all",
- filter: filter ?? "",
- logs,
- });
-
- // Scroll to the bottom when new logs are added
- // biome-ignore lint/correctness/useExhaustiveDependencies: run this effect only when the length of the logs changes
- useEffect(() => {
- if (!shouldFollow()) {
- return () => {};
- }
- // https://github.com/TanStack/virtual/issues/537
- const rafId = requestAnimationFrame(() => {
- virtualizer.current?.scrollToIndex(
- virtualizer.current.options.count - 1,
- {
- align: "end",
- },
- );
- });
-
- return () => {
- cancelAnimationFrame(rafId);
- };
- }, [combined.length]);
-
- // Detect if the user has scrolled all the way to the bottom
- const handleChange = useCallback(
- (instance: Virtualizer, sync: boolean) => {
- if (sync) {
- return;
- }
-
- follow.current =
- !instance.isScrolling &&
- instance.range?.endIndex === instance.options.count - 1;
- },
- [],
- );
-
- // if (isStdOutLoading || isStdErrLoading) {
- // return (
- //
- //
- // Loading logs...
- //
- //
- // );
- // }
-
- // const status = getActorStatus({ createdAt, startedAt, destroyedAt });
-
- if (status === "starting" && combined.length === 0) {
- return (
-
-
- [SYSTEM]: Actor is starting...
-
-
- );
- }
-
- if (status === "pending") {
- return (
- <>
-
-
- >
- );
- }
-
- if (combined.length === 0) {
- // if (!isStdOutSuccess || !isStdErrSuccess) {
- // return (
- //
- //
- // [SYSTEM]: Couldn't find the logs. Please try again
- // later.
- //
- //
- // );
- // }
- return (
-
-
- [SYSTEM]: No logs found. Logs are retained for 3 days.
-
-
- );
- }
-
- return (
- <>
-
- ({
- ...combined[index],
- children:
- combined[index].message || combined[index].line,
- variant: combined[index].level,
- timestamp: settings.showTimestamps
- ? combined[index].timestamp
- : undefined,
- })}
- onChange={handleChange}
- count={combined.length}
- estimateSize={() => 26}
- row={ActorConsoleMessage}
- />
- >
- );
- },
-);
-
-interface ScrollerProps {
- virtualizer: React.MutableRefObject | null>;
-}
-
-function Scroller({ virtualizer }: ScrollerProps) {
- // biome-ignore lint/correctness/useExhaustiveDependencies: scroll on mount, no need to run this effect again
- useEffect(() => {
- // https://github.com/TanStack/virtual/issues/537
- virtualizer.current?.scrollToIndex(
- virtualizer.current.options.count - 1,
- {
- align: "end",
- },
- );
- }, []);
-
- return null;
-}
-
-export function filterLogs({
- typeFilter,
- filter,
- logs,
-}: { typeFilter: LogsTypeFilter; filter: string; logs: Logs }) {
- const output = logs?.filter((log) => {
- if (typeFilter === "errors") {
- return log.level === "error";
- }
- if (typeFilter === "output") {
- return log.level !== "error";
- }
- return true;
- });
-
- // Search
- const filtered =
- filter && filter.trim() !== ""
- ? output.filter((log) => log.message.includes(filter))
- : output;
-
- const sorted = filtered.toSorted(
- (a, b) =>
- new Date(a.timestamp).valueOf() - new Date(b.timestamp).valueOf(),
- );
-
- return sorted;
-}
diff --git a/frontend/packages/components/src/actors/actor-memory-stats.tsx b/frontend/packages/components/src/actors/actor-memory-stats.tsx
deleted file mode 100644
index f2af24d75d..0000000000
--- a/frontend/packages/components/src/actors/actor-memory-stats.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import { format } from "date-fns";
-import { filesize } from "filesize";
-import { useId } from "react";
-import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
-import { timing } from "../lib/timing";
-import {
- type ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "../ui/chart";
-
-interface ActorMemoryStatsProps {
- metricsAt: number;
- memory: number[];
- allocatedMemory?: number;
- syncId?: string;
- interval?: number;
- isRunning?: boolean;
-}
-
-const chartConfig = {
- value: {
- color: "hsl(var(--chart-1))",
- label: "Memory Usage",
- },
-} satisfies ChartConfig;
-
-export function ActorMemoryStats({
- interval = 15,
- memory,
- allocatedMemory,
- metricsAt,
- syncId,
- isRunning = true,
-}: ActorMemoryStatsProps) {
- // Filter out trailing zeros in the last 15 seconds only if actor is still running
- let filteredMemory = [...memory];
- if (isRunning) {
- const secondsToCheck = 15;
- const pointsToCheck = Math.ceil(secondsToCheck / interval);
-
- // Find the last non-zero value and cut off any zeros after it
- for (
- let i = filteredMemory.length - 1;
- i >= Math.max(0, filteredMemory.length - pointsToCheck);
- i--
- ) {
- if (filteredMemory[i] === 0) {
- filteredMemory = filteredMemory.slice(0, i);
- } else {
- break;
- }
- }
- }
-
- const data = filteredMemory.map((value, i) => ({
- x: `${(filteredMemory.length - i) * -interval}`,
- value,
- config: {
- label: new Date(
- metricsAt -
- (filteredMemory.length - i) * timing.seconds(interval),
- ),
- },
- }));
-
- const max = allocatedMemory || Math.max(...filteredMemory);
-
- const id = useId();
-
- const fillId = `fill-${id}`;
- return (
-
-
-
-
-
- `${Math.ceil((value / max) * 100)}%`
- }
- />
- {
- return format(label, "HH:mm:ss");
- }}
- valueFormatter={(value) => {
- if (typeof value !== "number") {
- return "n/a";
- }
- return `${filesize(value)} (${Math.round((value / max) * 100).toFixed(2)}%)`;
- }}
- />
- }
- />
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-metrics-tab.tsx b/frontend/packages/components/src/actors/actor-metrics-tab.tsx
deleted file mode 100644
index edc11a6618..0000000000
--- a/frontend/packages/components/src/actors/actor-metrics-tab.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Button, ScrollArea } from "@rivet-gg/components";
-import { Icon, faBooks } from "@rivet-gg/icons";
-import { ActorMetrics } from "./actor-metrics";
-import type { ActorAtom } from "./actor-context";
-
-interface ActorMetricsTabProps {
- actor: ActorAtom;
-}
-
-export function ActorMetricsTab(props: ActorMetricsTabProps) {
- return (
-
-
- }
- >
- Documentation
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/frontend/packages/components/src/actors/actor-metrics.tsx b/frontend/packages/components/src/actors/actor-metrics.tsx
deleted file mode 100644
index 947d1166dc..0000000000
--- a/frontend/packages/components/src/actors/actor-metrics.tsx
+++ /dev/null
@@ -1,704 +0,0 @@
-import { useAtomValue, useSetAtom } from "jotai";
-import { selectAtom } from "jotai/utils";
-import equal from "fast-deep-equal";
-import { useState, useMemo } from "react";
-import type { Actor, ActorAtom } from "./actor-context";
-import { ActorCpuStats } from "./actor-cpu-stats";
-import { ActorMemoryStats } from "./actor-memory-stats";
-import { Dd, Dl, Dt } from "../ui/typography";
-import { Button } from "../ui/button";
-import { Flex } from "../ui/flex";
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
-import { actorMetricsTimeWindowAtom, actorEnvironmentAtom } from "./actor-context";
-import { useQuery } from "@tanstack/react-query";
-import { actorMetricsQueryOptions } from "@/domains/project/queries/actors/query-options";
-
-const selector = (a: Actor) => ({
- metrics: a.metrics,
- status: a.status,
- resources: a.resources,
- id: a.id,
-});
-
-const timeWindowOptions = [
- { label: "5 minutes", value: "5m", milliseconds: 5 * 60 * 1000 },
- { label: "15 minutes", value: "15m", milliseconds: 15 * 60 * 1000 },
- { label: "30 minutes", value: "30m", milliseconds: 30 * 60 * 1000 },
- { label: "1 hour", value: "1h", milliseconds: 60 * 60 * 1000 },
- { label: "3 hours", value: "3h", milliseconds: 3 * 60 * 60 * 1000 },
- { label: "6 hours", value: "6h", milliseconds: 6 * 60 * 60 * 1000 },
- { label: "12 hours", value: "12h", milliseconds: 12 * 60 * 60 * 1000 },
- { label: "24 hours", value: "24h", milliseconds: 24 * 60 * 60 * 1000 },
- { label: "2 days", value: "2d", milliseconds: 2 * 24 * 60 * 60 * 1000 },
-];
-
-export interface ActorMetricsProps {
- actor: ActorAtom;
-}
-
-export function ActorMetrics({ actor }: ActorMetricsProps) {
- const { metrics, status, resources, id } = useAtomValue(
- selectAtom(actor, selector, equal),
- );
- const defaultMetricsData = useAtomValue(metrics);
- const [showAdvanced, setShowAdvanced] = useState(false);
-
- const timeWindowMs = useAtomValue(actorMetricsTimeWindowAtom);
- const setTimeWindowMs = useSetAtom(actorMetricsTimeWindowAtom);
- const environment = useAtomValue(actorEnvironmentAtom);
-
- const currentTimeWindow = timeWindowOptions.find(option => option.milliseconds === timeWindowMs) || timeWindowOptions[1];
- const [timeWindow, setTimeWindow] = useState(currentTimeWindow.value);
-
- const isActorRunning = status === "running";
-
- // Create a query for time window-specific metrics
- const { data: customMetricsData, status: customMetricsStatus } = useQuery({
- ...actorMetricsQueryOptions(
- {
- projectNameId: environment?.projectNameId || "",
- environmentNameId: environment?.environmentNameId || "",
- actorId: id,
- timeWindowMs: timeWindowMs,
- },
- { refetchInterval: 5000 }
- ),
- enabled: !!environment && !!id,
- });
-
- // Use custom metrics if available, otherwise fall back to default
- const metricsData = customMetricsData ? {
- metrics: customMetricsData.metrics,
- rawData: customMetricsData.rawData,
- interval: customMetricsData.interval,
- status: customMetricsStatus,
- updatedAt: Date.now(),
- } : defaultMetricsData;
-
- const handleTimeWindowChange = (value: string) => {
- setTimeWindow(value);
- const selectedOption = timeWindowOptions.find(option => option.value === value);
- if (selectedOption) {
- setTimeWindowMs(selectedOption.milliseconds);
- }
- };
-
- const formatBytes = (bytes: number | null | undefined) => {
- if (!isActorRunning || bytes === null || bytes === undefined)
- return "n/a";
- const mb = bytes / 1024 / 1024;
- if (mb < 1024) {
- return `${mb.toFixed(1)} MB`;
- }
- return `${(mb / 1024).toFixed(1)} GB`;
- };
-
- const formatCpuUsage = (cpu: number | null | undefined) => {
- if (!isActorRunning || cpu === null || cpu === undefined) return "n/a";
- return `${(cpu * 100).toFixed(2)}%`;
- };
-
- const formatNumber = (value: number | null | undefined) => {
- if (!isActorRunning || value === null || value === undefined)
- return "n/a";
- return value.toLocaleString();
- };
-
- const formatTimestamp = (timestamp: number | null | undefined) => {
- if (!isActorRunning || timestamp === null || timestamp === undefined)
- return "n/a";
- return new Date(timestamp * 1000).toLocaleString();
- };
-
- // Calculate CPU percentage using time series data points
- const cpuPercentage = useMemo(() => {
- if (!isActorRunning) {
- return "Stopped";
- }
-
- const data = metricsData;
- if (!data || !data.rawData || !data.interval) {
- return "n/a";
- }
-
- const cpuValues = data.rawData.cpu_usage_seconds_total;
- if (!cpuValues || cpuValues.length < 2) {
- return "n/a";
- }
-
- // Find the last valid CPU rate from the most recent data points
- let cpuRate = 0;
- for (let i = cpuValues.length - 1; i > 0; i--) {
- const currentCpu = cpuValues[i];
- const previousCpu = cpuValues[i - 1];
-
- if (
- currentCpu !== 0 &&
- previousCpu !== 0 &&
- currentCpu >= previousCpu
- ) {
- const cpuDelta = currentCpu - previousCpu;
- const timeDelta = data.interval / 1000; // Convert ms to seconds
-
- // Rate calculation: CPU seconds used per second of real time
- // This gives the fraction of available CPU used (0-1)
- cpuRate = (cpuDelta / timeDelta) * 100;
- break;
- }
- }
-
- return `${Math.min(cpuRate, 100).toFixed(2)}%`;
- }, [metricsData, isActorRunning]);
-
- const calculateMemoryPercentage = (
- usage: number | null | undefined,
- ) => {
- if (
- !isActorRunning ||
- usage === null ||
- usage === undefined ||
- !resources ||
- !resources.memory ||
- resources.memory === 0
- ) {
- return null;
- }
- // Convert usage from bytes to MB and compare with resources.memory (which is in MB)
- const usageMB = usage / (1024 * 1024);
- return (usageMB / resources.memory) * 100;
- };
-
- const isLoading = metricsData.status === "pending";
- const hasError = metricsData.status === "error";
- const data = metricsData.metrics || {};
-
- if (isLoading) {
- return (
-
- );
- }
-
- if (hasError) {
- return (
-
-
Metrics
-
- Error loading metrics
-
-
- );
- }
-
- const memoryPercentage = calculateMemoryPercentage(
- data.memory_usage_bytes,
- );
-
- return (
-
-
-
Container Metrics
-
-
-
- {/* Main Metrics */}
-
-
-
-
- CPU Usage
-
-
- {cpuPercentage}
- {metricsData.rawData?.cpu_usage_seconds_total &&
- metricsData.rawData.cpu_usage_seconds_total.length > 0 ? (
-
- ) : null}
-
-
-
-
- Memory Usage
-
-
-
- {formatBytes(data.memory_usage_bytes)}
- {memoryPercentage !== null && (
-
- ({memoryPercentage.toFixed(1)}%)
-
- )}
-
- {metricsData.rawData?.memory_usage_bytes &&
- metricsData.rawData.memory_usage_bytes.length > 0 ? (
-
- ) : null}
-
-
-
-
-
- {/* Advanced Metrics */}
- {false && (
-
- {/* CPU & Performance */}
-
-
CPU & Performance
-
- - CPU Load Average (10s)
- - {formatCpuUsage(data.cpu_load_average_10s)}
- - CPU Usage Seconds Total
- -
- {formatNumber(data.cpu_usage_seconds_total)}
-
- - CPU User Seconds Total
- - {formatNumber(data.cpu_user_seconds_total)}
- - CPU System Seconds Total
- -
- {formatNumber(data.cpu_system_seconds_total)}
-
- - CPU Schedstat Run Periods
- -
- {formatNumber(
- data.cpu_schedstat_run_periods_total,
- )}
-
- - CPU Schedstat Run Seconds
- -
- {formatNumber(
- data.cpu_schedstat_run_seconds_total,
- )}
-
- - CPU Schedstat Runqueue Seconds
- -
- {formatNumber(
- data.cpu_schedstat_runqueue_seconds_total,
- )}
-
-
-
-
- {/* Memory */}
-
-
Memory
-
- - Memory Usage
- - {formatBytes(data.memory_usage_bytes)}
- - Memory Working Set
- -
- {formatBytes(data.memory_working_set_bytes)}
-
- - Memory RSS
- - {formatBytes(data.memory_rss)}
- - Memory Cache
- - {formatBytes(data.memory_cache)}
- - Memory Swap
- - {formatBytes(data.memory_swap)}
- - Memory Max Usage
- - {formatBytes(data.memory_max_usage_bytes)}
- - Memory Mapped File
- - {formatBytes(data.memory_mapped_file)}
- - Memory Failcnt
- - {formatNumber(data.memory_failcnt)}
-
-
-
- {/* Memory Failures */}
-
-
Memory Failures
-
- - Page Fault (Container)
- -
- {formatNumber(
- data.memory_failures_pgfault_container,
- )}
-
- - Page Fault (Hierarchy)
- -
- {formatNumber(
- data.memory_failures_pgfault_hierarchy,
- )}
-
- - Major Page Fault (Container)
- -
- {formatNumber(
- data.memory_failures_pgmajfault_container,
- )}
-
- - Major Page Fault (Hierarchy)
- -
- {formatNumber(
- data.memory_failures_pgmajfault_hierarchy,
- )}
-
-
-
-
- {/* Resource Limits */}
-
-
Resource Limits
-
- - Memory Limit
- - {resources?.memory ? `${resources.memory} MB` : "n/a"}
- - CPU Limit
- - {resources?.cpu ? `${resources.cpu / 1000} cores` : "n/a"}
-
-
-
- {/* Processes & Threads */}
-
-
- Processes & Threads
-
-
- - Processes
- - {formatNumber(data.processes)}
- - Threads
- - {formatNumber(data.threads)}
- - Max Threads
- - {formatNumber(data.threads_max)}
- - Tasks Running
- - {formatNumber(data.tasks_state_running)}
- - Tasks Sleeping
- - {formatNumber(data.tasks_state_sleeping)}
- - Tasks Stopped
- - {formatNumber(data.tasks_state_stopped)}
- - Tasks IO Waiting
- - {formatNumber(data.tasks_state_iowaiting)}
- - Tasks Uninterruptible
- -
- {formatNumber(data.tasks_state_uninterruptible)}
-
-
-
-
- {/* Filesystem */}
-
-
Filesystem
-
- - Reads Bytes Total (sda)
- -
- {formatBytes(data.fs_reads_bytes_total_sda)}
-
- - Writes Bytes Total (sda)
- -
- {formatBytes(data.fs_writes_bytes_total_sda)}
-
-
-
-
- {/* Network - Receive */}
-
-
Network - Receive
-
- - Bytes Total (eth0)
- -
- {formatBytes(
- data.network_receive_bytes_total_eth0,
- )}
-
- - Bytes Total (eth1)
- -
- {formatBytes(
- data.network_receive_bytes_total_eth1,
- )}
-
- - Errors Total (eth0)
- -
- {formatNumber(
- data.network_receive_errors_total_eth0,
- )}
-
- - Errors Total (eth1)
- -
- {formatNumber(
- data.network_receive_errors_total_eth1,
- )}
-
- - Packets Dropped (eth0)
- -
- {formatNumber(
- data.network_receive_packets_dropped_total_eth0,
- )}
-
- - Packets Dropped (eth1)
- -
- {formatNumber(
- data.network_receive_packets_dropped_total_eth1,
- )}
-
- - Packets Total (eth0)
- -
- {formatNumber(
- data.network_receive_packets_total_eth0,
- )}
-
- - Packets Total (eth1)
- -
- {formatNumber(
- data.network_receive_packets_total_eth1,
- )}
-
-
-
-
- {/* Network - Transmit */}
-
-
Network - Transmit
-
- - Bytes Total (eth0)
- -
- {formatBytes(
- data.network_transmit_bytes_total_eth0,
- )}
-
- - Bytes Total (eth1)
- -
- {formatBytes(
- data.network_transmit_bytes_total_eth1,
- )}
-
- - Errors Total (eth0)
- -
- {formatNumber(
- data.network_transmit_errors_total_eth0,
- )}
-
- - Errors Total (eth1)
- -
- {formatNumber(
- data.network_transmit_errors_total_eth1,
- )}
-
- - Packets Dropped (eth0)
- -
- {formatNumber(
- data.network_transmit_packets_dropped_total_eth0,
- )}
-
- - Packets Dropped (eth1)
- -
- {formatNumber(
- data.network_transmit_packets_dropped_total_eth1,
- )}
-
- - Packets Total (eth0)
- -
- {formatNumber(
- data.network_transmit_packets_total_eth0,
- )}
-
- - Packets Total (eth1)
- -
- {formatNumber(
- data.network_transmit_packets_total_eth1,
- )}
-
-
-
-
- {/* TCP Connections */}
-
-
TCP Connections
-
- - Close
- -
- {formatNumber(data.network_tcp_usage_close)}
-
- - Close Wait
- -
- {formatNumber(
- data.network_tcp_usage_closewait,
- )}
-
- - Closing
- -
- {formatNumber(data.network_tcp_usage_closing)}
-
- - Established
- -
- {formatNumber(
- data.network_tcp_usage_established,
- )}
-
- - Fin Wait 1
- -
- {formatNumber(data.network_tcp_usage_finwait1)}
-
- - Fin Wait 2
- -
- {formatNumber(data.network_tcp_usage_finwait2)}
-
- - Last Ack
- -
- {formatNumber(data.network_tcp_usage_lastack)}
-
- - Listen
- -
- {formatNumber(data.network_tcp_usage_listen)}
-
- - Syn Recv
- -
- {formatNumber(data.network_tcp_usage_synrecv)}
-
- - Syn Sent
- -
- {formatNumber(data.network_tcp_usage_synsent)}
-
- - Time Wait
- -
- {formatNumber(data.network_tcp_usage_timewait)}
-
-
-
-
- {/* TCP6 Connections */}
-
-
TCP6 Connections
-
- - Close
- -
- {formatNumber(data.network_tcp6_usage_close)}
-
- - Close Wait
- -
- {formatNumber(
- data.network_tcp6_usage_closewait,
- )}
-
- - Closing
- -
- {formatNumber(data.network_tcp6_usage_closing)}
-
- - Established
- -
- {formatNumber(
- data.network_tcp6_usage_established,
- )}
-
- - Fin Wait 1
- -
- {formatNumber(data.network_tcp6_usage_finwait1)}
-
- - Fin Wait 2
- -
- {formatNumber(data.network_tcp6_usage_finwait2)}
-
- - Last Ack
- -
- {formatNumber(data.network_tcp6_usage_lastack)}
-
- - Listen
- -
- {formatNumber(data.network_tcp6_usage_listen)}
-
- - Syn Recv
- -
- {formatNumber(data.network_tcp6_usage_synrecv)}
-
- - Syn Sent
- -
- {formatNumber(data.network_tcp6_usage_synsent)}
-
- - Time Wait
- -
- {formatNumber(data.network_tcp6_usage_timewait)}
-
-
-
-
- {/* UDP Connections */}
-
-
UDP Connections
-
- - Dropped
- -
- {formatNumber(data.network_udp_usage_dropped)}
-
- - Listen
- -
- {formatNumber(data.network_udp_usage_listen)}
-
- - RX Queued
- -
- {formatNumber(data.network_udp_usage_rxqueued)}
-
- - TX Queued
- -
- {formatNumber(data.network_udp_usage_txqueued)}
-
-
-
-
- {/* UDP6 Connections */}
-
-
UDP6 Connections
-
- - Dropped
- -
- {formatNumber(data.network_udp6_usage_dropped)}
-
- - Listen
- -
- {formatNumber(data.network_udp6_usage_listen)}
-
- - RX Queued
- -
- {formatNumber(data.network_udp6_usage_rxqueued)}
-
- - TX Queued
- -
- {formatNumber(data.network_udp6_usage_txqueued)}
-
-
-
-
- {/* System */}
-
-
System
-
- - File Descriptors
- - {formatNumber(data.file_descriptors)}
- - Sockets
- - {formatNumber(data.sockets)}
- - Last Seen
- - {formatTimestamp(data.last_seen)}
- - Start Time
- - {formatTimestamp(data.start_time_seconds)}
-
-
-
- )}
-
- );
-}
\ No newline at end of file
diff --git a/frontend/packages/components/src/actors/actor-network.tsx b/frontend/packages/components/src/actors/actor-network.tsx
deleted file mode 100644
index 3bb466ad99..0000000000
--- a/frontend/packages/components/src/actors/actor-network.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import {
- Button,
- Dd,
- DiscreteCopyButton,
- Dl,
- DocsSheet,
- Dt,
- Flex,
- cn,
-} from "@rivet-gg/components";
-import { Icon, faBooks } from "@rivet-gg/icons";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import { Fragment } from "react";
-import type { Actor, ActorAtom } from "./actor-context";
-import { ActorObjectInspector } from "./console/actor-inspector";
-
-const selector = (a: Actor) => a.network?.ports;
-
-export interface ActorNetworkProps {
- actor: ActorAtom;
-}
-
-export function ActorNetwork({ actor }: ActorNetworkProps) {
- const ports = useAtomValue(selectAtom(actor, selector));
- if (!ports) {
- return null;
- }
-
- return (
-
-
-
Network
-
- }
- >
- Documentation
-
-
-
-
-
-
- - Ports
- -
- {Object.entries(ports).map(
- ([name, port], index) => (
-
-
- {name}
-
-
- - Protocol
- -
-
- {port.protocol}
-
-
- - Port
- -
-
- {port.port}
-
-
- - Hostname
- -
-
-
- {port.hostname}
-
-
-
- {port.url ? (
- <>
- - URL
- -
-
-
- {port.url}
-
-
-
- >
- ) : null}
-
- {port.routing?.host ? (
- <>
- - Host Routing
- -
-
-
-
-
- >
- ) : null}
-
-
- ),
- )}
-
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-not-found.tsx b/frontend/packages/components/src/actors/actor-not-found.tsx
deleted file mode 100644
index c1beda2464..0000000000
--- a/frontend/packages/components/src/actors/actor-not-found.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Icon, faQuestionSquare } from "@rivet-gg/icons";
-import { useAtomValue, useSetAtom } from "jotai";
-import { selectAtom } from "jotai/utils";
-import { useCallback } from "react";
-import { Button } from "../ui/button";
-import { FilterOp } from "../ui/filters";
-import {
- type ActorFeature,
- actorFiltersAtom,
- currentActorQueryAtom,
-} from "./actor-context";
-import { ActorTabs } from "./actors-actor-details";
-import { useActorsView } from "./actors-view-context-provider";
-import { ShimmerLine } from "../shimmer-line";
-
-export function ActorNotFound({
- features = [],
-}: { features?: ActorFeature[] }) {
- const { copy } = useActorsView();
-
- const setFilters = useSetAtom(actorFiltersAtom);
- const hasDevMode = useAtomValue(
- selectAtom(
- actorFiltersAtom,
- useCallback((filters) => filters.devMode, []),
- ),
- );
-
- const { isLoading } = useAtomValue(currentActorQueryAtom);
-
- return (
-
-
-
- {!isLoading ? (
- <>
-
-
- {copy.actorNotFound}
-
-
- {copy.actorNotFoundDescription}
-
- >
- ) : null}
-
- {!hasDevMode && !isLoading ? (
-
- ) : null}
- {isLoading ?
: null}
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-region.tsx b/frontend/packages/components/src/actors/actor-region.tsx
deleted file mode 100644
index 12b7eb3e2e..0000000000
--- a/frontend/packages/components/src/actors/actor-region.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { Flex, WithTooltip } from "@rivet-gg/components";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import { useCallback } from "react";
-import {
- REGION_LABEL,
- RegionIcon,
- getRegionKey,
-} from "../matchmaker/lobby-region";
-import { actorRegionsAtom } from "./actor-context";
-
-interface ActorRegionProps {
- regionId?: string;
- showLabel?: boolean | "abbreviated";
- className?: string;
-}
-
-export function ActorRegion({
- showLabel,
- regionId,
- className,
-}: ActorRegionProps) {
- const region = useAtomValue(
- selectAtom(
- actorRegionsAtom,
- useCallback(
- (regions) => regions.find((region) => region.id === regionId),
- [regionId],
- ),
- ),
- );
-
- const regionKey = getRegionKey(region?.id);
-
- if (showLabel) {
- return (
-
-
-
- {showLabel === "abbreviated"
- ? regionKey.toUpperCase()
- : (REGION_LABEL[regionKey] ?? REGION_LABEL.unknown)}
-
-
- );
- }
-
- return (
-
-
-
- }
- />
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-runtime.tsx b/frontend/packages/components/src/actors/actor-runtime.tsx
deleted file mode 100644
index e8c0f181a8..0000000000
--- a/frontend/packages/components/src/actors/actor-runtime.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import equal from "fast-deep-equal";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import { Suspense } from "react";
-import { formatDuration } from "../lib/formatter";
-import { toRecord } from "../lib/utils";
-import { Flex } from "../ui/flex";
-import { Skeleton } from "../ui/skeleton";
-import { Dd, Dl, Dt } from "../ui/typography";
-import { ActorBuild } from "./actor-build";
-import {
- type Actor,
- type ActorAtom,
- ActorFeature,
- currentActorFeaturesAtom,
-} from "./actor-context";
-import { ACTOR_FRAMEWORK_TAG_VALUE } from "./actor-tags";
-import { ActorObjectInspector } from "./console/actor-inspector";
-
-const selector = (a: Actor) => ({
- lifecycle: a.lifecycle,
- resources: a.resources,
- runtime: a.runtime,
- tags: a.tags,
-});
-
-export interface ActorRuntimeProps {
- actor: ActorAtom;
-}
-
-export function ActorRuntime({ actor }: ActorRuntimeProps) {
- const { lifecycle, resources, runtime, tags } = useAtomValue(
- selectAtom(actor, selector, equal),
- );
-
- const features = useAtomValue(currentActorFeaturesAtom);
-
- return (
- <>
- {features.includes(ActorFeature.Runtime) && lifecycle && runtime ? (
-
-
-
Runtime
-
-
-
- - Kill timeout
- -
- {formatDuration(lifecycle.killTimeout || 0, {
- show0Min: true,
- })}
-
- {toRecord(tags).framework !==
- ACTOR_FRAMEWORK_TAG_VALUE && resources ? (
- <>
- - Resources
- -
- {resources.cpu / 1000} CPU cores,{" "}
- {resources.memory} MB RAM
-
- >
- ) : null}
- - Arguments
- -
-
-
- - Environment
- -
-
-
-
- - Durable
- - {lifecycle.durable ? "Yes" : "No"}
-
-
-
- ) : null}
-
- }
- >
-
-
- >
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-state-change-indicator.tsx b/frontend/packages/components/src/actors/actor-state-change-indicator.tsx
deleted file mode 100644
index b5d1400d85..0000000000
--- a/frontend/packages/components/src/actors/actor-state-change-indicator.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Badge } from "@rivet-gg/components";
-import { motion } from "framer-motion";
-import { useEffect, useRef } from "react";
-
-const EMPTY_OBJECT = {};
-
-interface ActorStateChangeIndicatorProps {
- state: unknown | undefined;
-}
-
-export function ActorStateChangeIndicator({
- state,
-}: ActorStateChangeIndicatorProps) {
- const isMounted = useRef(false);
- const oldState = useRef();
-
- useEffect(() => {
- isMounted.current = true;
- }, []);
-
- useEffect(() => {
- oldState.current = state || EMPTY_OBJECT;
- }, [state]);
-
- const hasChanged = state !== oldState.current;
- const shouldUpdate = hasChanged && isMounted.current;
-
- return (
-
-
- State changed
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-state-tab.tsx b/frontend/packages/components/src/actors/actor-state-tab.tsx
deleted file mode 100644
index 634596a9b5..0000000000
--- a/frontend/packages/components/src/actors/actor-state-tab.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import type { Actor, ActorAtom } from "./actor-context";
-import { ActorEditableState } from "./actor-editable-state";
-import {
- useActorState,
- useActorWorkerStatus,
-} from "./worker/actor-worker-context";
-
-const selector = (a: Actor) => a.destroyedAt;
-
-interface ActorStateTabProps {
- actor: ActorAtom;
-}
-
-export function ActorStateTab({ actor }: ActorStateTabProps) {
- const destroyedAt = useAtomValue(selectAtom(actor, selector));
- const status = useActorWorkerStatus();
-
- const state = useActorState();
-
- if (destroyedAt) {
- return (
-
- State Preview is unavailable for inactive Actors.
-
- );
- }
-
- if (status.type === "error") {
- return (
-
- State Preview is currently unavailable.
-
- See console/logs for more details.
-
- );
- }
-
- if (status.type === "unsupported") {
- return (
-
- State Preview is not supported for this Actor.
-
- );
- }
-
- if (status.type !== "ready") {
- return (
-
- Loading state...
-
- );
- }
-
- return (
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-status-indicator.tsx b/frontend/packages/components/src/actors/actor-status-indicator.tsx
deleted file mode 100644
index 81f9ff34dc..0000000000
--- a/frontend/packages/components/src/actors/actor-status-indicator.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { Ping, cn } from "@rivet-gg/components";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import type { ComponentPropsWithRef } from "react";
-import type { Actor, ActorAtom } from "./actor-context";
-
-export type ActorStatus =
- | "starting"
- | "running"
- | "stopped"
- | "crashed"
- | "unknown";
-
-export function getActorStatus(
- actor: Pick,
-): ActorStatus {
- const { createdAt, startedAt, destroyedAt } = actor;
-
- if (createdAt && !startedAt && !destroyedAt) {
- return "starting";
- }
-
- if (createdAt && startedAt && !destroyedAt) {
- return "running";
- }
-
- if (createdAt && startedAt && destroyedAt) {
- return "stopped";
- }
-
- if (createdAt && !startedAt && destroyedAt) {
- return "crashed";
- }
-
- return "unknown";
-}
-
-interface AtomizedActorStatusIndicatorProps
- extends ComponentPropsWithRef<"span"> {
- actor: ActorAtom;
-}
-
-export const AtomizedActorStatusIndicator = ({
- actor,
- ...props
-}: AtomizedActorStatusIndicatorProps) => {
- const status = useAtomValue(selectAtom(actor, selector));
- return ;
-};
-
-const selector = ({ status }: Actor) => status;
-
-interface ActorStatusIndicatorProps extends ComponentPropsWithRef<"span"> {
- status: ReturnType;
-}
-
-export const ActorStatusIndicator = ({
- status,
- ...props
-}: ActorStatusIndicatorProps) => {
- if (status === "running") {
- return (
-
- );
- }
-
- return (
-
- );
-};
diff --git a/frontend/packages/components/src/actors/actor-status-label.tsx b/frontend/packages/components/src/actors/actor-status-label.tsx
deleted file mode 100644
index c6a16ddb7d..0000000000
--- a/frontend/packages/components/src/actors/actor-status-label.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import type { Actor, ActorAtom } from "./actor-context";
-import type { ActorStatus } from "./actor-status-indicator";
-
-export const ACTOR_STATUS_LABEL_MAP = {
- unknown: "Unknown",
- starting: "Starting",
- running: "Running",
- stopped: "Stopped",
- crashed: "Crashed",
-} satisfies Record;
-
-export const ActorStatusLabel = ({ status }: { status: ActorStatus }) => {
- return {ACTOR_STATUS_LABEL_MAP[status]};
-};
-
-const selector = (a: Actor) => a.status;
-
-export const AtomizedActorStatusLabel = ({
- actor,
-}: {
- actor: ActorAtom;
-}) => {
- const status = useAtomValue(selectAtom(actor, selector));
- return ;
-};
diff --git a/frontend/packages/components/src/actors/actor-status.tsx b/frontend/packages/components/src/actors/actor-status.tsx
deleted file mode 100644
index 0f587ed7cb..0000000000
--- a/frontend/packages/components/src/actors/actor-status.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { cn } from "@rivet-gg/components";
-import type { ActorAtom } from "./actor-context";
-import {
- ActorStatusIndicator,
- type ActorStatus as ActorStatusType,
- AtomizedActorStatusIndicator,
-} from "./actor-status-indicator";
-import {
- ActorStatusLabel,
- AtomizedActorStatusLabel,
-} from "./actor-status-label";
-
-interface ActorStatusProps {
- className?: string;
- actor: ActorAtom;
-}
-
-export const AtomizedActorStatus = ({
- className,
- ...props
-}: ActorStatusProps) => {
- return (
-
- );
-};
-
-export const ActorStatus = ({
- className,
- status,
-}: {
- className?: string;
- status: ActorStatusType;
-}) => {
- return (
-
- );
-};
diff --git a/frontend/packages/components/src/actors/actor-stop-button.tsx b/frontend/packages/components/src/actors/actor-stop-button.tsx
deleted file mode 100644
index cbc67ea89f..0000000000
--- a/frontend/packages/components/src/actors/actor-stop-button.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Button, WithTooltip } from "@rivet-gg/components";
-import { Icon, faXmark } from "@rivet-gg/icons";
-
-import equal from "fast-deep-equal";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import type { Actor, ActorAtom, DestroyActorAtom } from "./actor-context";
-
-const selector = (a: Actor) => ({
- destroyedAt: a.destroyedAt,
- destroy: a.destroy,
-});
-
-interface ActorStopButtonProps {
- actor: ActorAtom;
-}
-
-export function ActorStopButton({ actor }: ActorStopButtonProps) {
- const { destroy: destroyAtom, destroyedAt } = useAtomValue(
- selectAtom(actor, selector, equal),
- );
-
- if (destroyedAt || !destroyAtom) {
- return null;
- }
-
- return ;
-}
-
-function Content({ destroy: destroyAtom }: { destroy: DestroyActorAtom }) {
- const { destroy, isDestroying } = useAtomValue(destroyAtom);
- return (
-
-
-
- }
- content="Stop Actor"
- />
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-tags-select.tsx b/frontend/packages/components/src/actors/actor-tags-select.tsx
deleted file mode 100644
index 17eedbba3e..0000000000
--- a/frontend/packages/components/src/actors/actor-tags-select.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Combobox } from "@rivet-gg/components";
-import { useAtomValue } from "jotai";
-import { useMemo } from "react";
-import { actorTagsAtom } from "./actor-context";
-import { ActorTag } from "./actor-tags";
-
-interface ActorTagsSelectProps {
- value: Record;
- onValueChange: (value: Record) => void;
- showSelectedOptions?: number;
-}
-
-export function ActorTagsSelect({
- value,
- onValueChange,
- showSelectedOptions,
-}: ActorTagsSelectProps) {
- const data = useAtomValue(actorTagsAtom);
-
- const valArray = useMemo(() => Object.entries(value), [value]);
- const tags = useMemo(() => {
- // upsert custom tags to the list of tags
- const tags = [...data];
- for (const [key, value] of valArray) {
- const found = data.find(
- (tag) => tag.key === key && tag.value === value,
- );
-
- if (!found) {
- tags.push({ key, value });
- }
- }
- return tags;
- }, [valArray, data]);
-
- const val = useMemo(
- () =>
- valArray.map(([key, value]) => {
- return [key, value].join("=");
- }),
- [valArray],
- );
-
- const options = useMemo(
- () =>
- tags.map((tag) => {
- return {
- label: (
-
-
- {tag.key}={tag.value}
-
-
- ),
- value: [tag.key, tag.value].join("="),
- tag,
- };
- }),
- [tags],
- );
-
- const handleValueChange = (value: string[]) => {
- onValueChange(
- Object.fromEntries(
- value.map((v) => {
- // its safe to split by "=" because the value is a tag
- const [key, value] = v.split("=");
- return [key.trim(), value.trim()];
- }),
- ),
- );
- };
-
- const handleCreateOption = (option: string) => {
- const parts = option.split("=");
- if (parts.length !== 2) return;
-
- const [key, value] = parts.map((part) => part.trim());
- if (!key || !value) return;
-
- onValueChange(Object.fromEntries([...valArray, [key, value]]));
- };
-
- return (
- {
- const tagKey = option.tag.key.toLowerCase();
- const tagValue = option.tag.value.toLowerCase();
-
- if (search.includes("=")) {
- const [key, value] = search.split("=");
- return tagKey.includes(key) && tagValue.includes(value);
- }
- return tagKey.includes(search) || tagValue.includes(search);
- }}
- className="w-full min-w-[20rem]"
- />
- );
-}
diff --git a/frontend/packages/components/src/actors/actor-tags.tsx b/frontend/packages/components/src/actors/actor-tags.tsx
deleted file mode 100644
index 5a017d5cb0..0000000000
--- a/frontend/packages/components/src/actors/actor-tags.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-import {
- Button,
- DiscreteCopyButton,
- Slot,
- Slottable,
- WithTooltip,
- cn,
-} from "@rivet-gg/components";
-import { Icon, faTag } from "@rivet-gg/icons";
-import { type ReactNode, forwardRef, useState } from "react";
-
-const BUILT_IN_TAGS = {
- actors: ["framework", "framework-version"],
- builds: ["current"],
-};
-
-export const ACTOR_FRAMEWORK_TAG_VALUE = "rivetkit";
-
-export const ActorTag = forwardRef<
- HTMLSpanElement,
- { children: ReactNode; className?: string }
->(({ children, className, ...props }, ref) => (
-
-
- {children}
-
-));
-
-interface ActorTagsProps {
- tags?: unknown;
- excludeBuiltIn?: keyof typeof BUILT_IN_TAGS;
- className?: string;
- truncate?: boolean;
- copy?: boolean;
- max?: number;
- hoverable?: boolean;
-}
-
-export function ActorTags({
- tags = {},
- excludeBuiltIn = undefined,
- truncate = true,
- className,
- hoverable = true,
- max = Number.POSITIVE_INFINITY,
- copy = true,
-}: ActorTagsProps) {
- const withoutBuiltIn = Object.entries(tags ?? {}).filter(([key]) =>
- excludeBuiltIn ? !BUILT_IN_TAGS[excludeBuiltIn].includes(key) : true,
- );
-
- const [isTruncatedList, setTruncatedList] = useState(
- withoutBuiltIn.length > max,
- );
-
- const truncated = withoutBuiltIn.filter((_, index) =>
- isTruncatedList ? index < max : true,
- );
-
- const truncatedCount = withoutBuiltIn.length - truncated.length;
-
- return (
-
- {truncated.length > 0 ? (
- <>
- {truncated
- .sort(([a], [b]) => a.localeCompare(b))
- .map(([key, value]) => {
- let trigger = truncate ? (
-
-
- {key}={value}
-
-
- ) : (
-
-
- {key}={value}
-
-
- );
-
- trigger = copy ? (
-
-
- {trigger}
-
-
- ) : (
- trigger
- );
-
- return truncate && hoverable && !copy ? (
-
- ) : (
- trigger
- );
- })}
-
- {truncatedCount > 0 ? (
-
- ) : null}
- >
- ) : null}
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actors-actor-details.tsx b/frontend/packages/components/src/actors/actors-actor-details.tsx
deleted file mode 100644
index e45a7bc48e..0000000000
--- a/frontend/packages/components/src/actors/actors-actor-details.tsx
+++ /dev/null
@@ -1,240 +0,0 @@
-import {
- Flex,
- Tabs,
- TabsContent,
- TabsList,
- TabsTrigger,
- cn,
-} from "@rivet-gg/components";
-import { Icon, faQuestionSquare } from "@rivet-gg/icons";
-import { useAtomValue } from "jotai";
-import { type ReactNode, Suspense, memo } from "react";
-import { ActorConfigTab } from "./actor-config-tab";
-import { ActorConnectionsTab } from "./actor-connections-tab";
-import {
- type ActorAtom,
- ActorFeature,
- currentActorFeaturesAtom,
-} from "./actor-context";
-import { ActorDetailsSettingsProvider } from "./actor-details-settings";
-import { ActorLogsTab } from "./actor-logs-tab";
-import { ActorMetricsTab } from "./actor-metrics-tab";
-import { ActorStateTab } from "./actor-state-tab";
-import { AtomizedActorStatus } from "./actor-status";
-import { ActorStopButton } from "./actor-stop-button";
-import { ActorsSidebarToggleButton } from "./actors-sidebar-toggle-button";
-import { useActorsView } from "./actors-view-context-provider";
-import { ActorConsole } from "./console/actor-console";
-import { ActorWorkerContextProvider } from "./worker/actor-worker-context";
-
-interface ActorsActorDetailsProps {
- tab?: string;
- actor: ActorAtom;
- onTabChange?: (tab: string) => void;
- onExportLogs?: (
- actorId: string,
- typeFilter?: string,
- filter?: string,
- ) => Promise;
- isExportingLogs?: boolean;
-}
-
-export const ActorsActorDetails = memo(
- ({
- tab,
- onTabChange,
- actor,
- onExportLogs,
- isExportingLogs,
- }: ActorsActorDetailsProps) => {
- const actorFeatures = useAtomValue(currentActorFeaturesAtom);
- const supportsConsole = actorFeatures?.includes(ActorFeature.Console);
-
- return (
-
-
-
-
-
- {supportsConsole ?
: null}
-
-
-
- );
- },
-);
-
-export const ActorsActorEmptyDetails = ({
- features,
-}: {
- features: ActorFeature[];
-}) => {
- const { copy } = useActorsView();
- return (
-
- );
-};
-
-export function ActorTabs({
- tab,
- features,
- onTabChange,
- actor,
- className,
- disabled,
- children,
- onExportLogs,
- isExportingLogs,
-}: {
- disabled?: boolean;
- tab?: string;
- features: ActorFeature[];
- onTabChange?: (tab: string) => void;
- actor?: ActorAtom;
- className?: string;
- children?: ReactNode;
- onExportLogs?: (
- actorId: string,
- typeFilter?: string,
- filter?: string,
- ) => Promise;
- isExportingLogs?: boolean;
-}) {
- const supportsState = features?.includes(ActorFeature.State);
- const supportsLogs = features?.includes(ActorFeature.Logs);
- const supportsConnections = features?.includes(ActorFeature.Connections);
- const supportsConfig = features?.includes(ActorFeature.Config);
- const supportsMetrics = features?.includes(ActorFeature.Metrics);
-
- const defaultTab = supportsState ? "state" : "logs";
- const value = disabled ? undefined : tab || defaultTab;
-
- return (
-
-
-
-
-
- {supportsState ? (
-
- State
-
- ) : null}
- {supportsConnections ? (
-
- Connections
-
- ) : null}
- {supportsLogs ? (
-
- Logs
-
- ) : null}
- {supportsConfig ? (
-
- Config
-
- ) : null}
- {supportsMetrics ? (
-
- Metrics
-
- ) : null}
-
- {actor ? (
-
-
-
-
- ) : null}
-
-
- {actor ? (
- <>
- {supportsLogs ? (
-
- }>
-
-
-
- ) : null}
- {supportsConfig ? (
-
-
-
- ) : null}
- {supportsConnections ? (
-
-
-
- ) : null}
- {supportsState ? (
-
-
-
- ) : null}
- {supportsMetrics ? (
-
-
-
- ) : null}
- >
- ) : null}
- {children}
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actors-actor-not-found.tsx b/frontend/packages/components/src/actors/actors-actor-not-found.tsx
deleted file mode 100644
index 96fed3be76..0000000000
--- a/frontend/packages/components/src/actors/actors-actor-not-found.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-// import { isRivetError } from "@/lib/utils";
-// import { RivetError } from "@rivet-gg/api";
-import { Icon, faCircleExclamation } from "@rivet-gg/icons";
-import type { ErrorComponentProps } from "@tanstack/react-router";
-import { ActorsSidebarToggleButton } from "./actors-sidebar-toggle-button";
-
-export function ActorsActorError({ error }: ErrorComponentProps) {
- // if (isRivetError(error) || error instanceof RivetError) {
- // return (
- //
- //
- //
- //
- // Actor not found.
- //
- //
- // );
- // }
-
- return (
-
-
-
-
- Error occurred while fetching Actor.
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actors-layout-context.tsx b/frontend/packages/components/src/actors/actors-layout-context.tsx
deleted file mode 100644
index a40a84c760..0000000000
--- a/frontend/packages/components/src/actors/actors-layout-context.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import type { ReactNode } from "react";
-import { createContext, useContext, useMemo } from "react";
-import { assertNonNullable } from "../lib/utils";
-
-export interface ActorsLayoutContextValue {
- isFolded: boolean;
- setFolded: (value: boolean) => void;
-}
-
-export const ActorsLayoutContext =
- createContext(null);
-
-interface ActorsLayoutProviderProps extends ActorsLayoutContextValue {
- children: ReactNode;
-}
-
-export function ActorsLayoutContextProvider({
- children,
- isFolded,
- setFolded,
-}: ActorsLayoutProviderProps) {
- return (
- ({ isFolded, setFolded }),
- [isFolded, setFolded],
- )}
- >
- {children}
-
- );
-}
-
-export function useActorsLayout() {
- const context = useContext(ActorsLayoutContext);
- assertNonNullable(context);
- return context;
-}
diff --git a/frontend/packages/components/src/actors/actors-layout.tsx b/frontend/packages/components/src/actors/actors-layout.tsx
deleted file mode 100644
index df2a8d4b32..0000000000
--- a/frontend/packages/components/src/actors/actors-layout.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { type ReactNode, memo, useState } from "react";
-import { cn, ls } from "../lib/utils";
-import { ActorsLayoutContextProvider } from "./actors-layout-context";
-
-interface ActorsListPreviewProps {
- left: ReactNode;
- right: ReactNode;
- className?: string;
-}
-
-export const ActorsLayout = memo(
- ({ left, right, className }: ActorsListPreviewProps) => {
- const [folded, setFolded] = useState(() => ls.actorsList.getFolded());
-
- return (
-
-
-
- );
- },
-);
diff --git a/frontend/packages/components/src/actors/actors-list-panel.tsx b/frontend/packages/components/src/actors/actors-list-panel.tsx
deleted file mode 100644
index aea9d29000..0000000000
--- a/frontend/packages/components/src/actors/actors-list-panel.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { ActorsList } from "./actors-list";
-
-export function ActorsListPanel() {
- return ;
-}
diff --git a/frontend/packages/components/src/actors/actors-list-preview.tsx b/frontend/packages/components/src/actors/actors-list-preview.tsx
deleted file mode 100644
index d26a22acab..0000000000
--- a/frontend/packages/components/src/actors/actors-list-preview.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-import { Icon, faGripDotsVertical } from "@rivet-gg/icons";
-import {
- animate,
- motion,
- useMotionTemplate,
- useMotionValue,
- useMotionValueEvent,
- useTransform,
-} from "framer-motion";
-import {
- type ReactNode,
- Suspense,
- memo,
- useCallback,
- useLayoutEffect,
- useState,
-} from "react";
-import { cn, ls } from "../lib/utils";
-import { ActorsLayoutContextProvider } from "./actors-layout-context";
-import { ActorsListPanel } from "./actors-list-panel";
-
-const RIGHT_PANEL_MIN_WIDTH = 480;
-
-interface ActorsListPreviewProps {
- children: ReactNode;
-}
-
-export const ActorsListPreview = memo(
- ({ children }: ActorsListPreviewProps) => {
- const outerWidth = useMotionValue(0);
- const x = useMotionValue(0);
-
- const rightWidth = useMotionTemplate`calc(50% - ${x}px)`;
- const leftWidth = useMotionTemplate`calc(50% + ${x}px)`;
-
- const [folded, setFolded] = useState(() => ls.actorsList.getFolded());
- const [isDragging, setIsDragging] = useState(false);
-
- const [, setInitialized] = useState(false);
-
- useMotionValueEvent(x, "change", (value) => {
- ls.actorsList.set(value / outerWidth.get(), folded);
- });
-
- // biome-ignore lint/correctness/useExhaustiveDependencies: on first draw
- useLayoutEffect(() => {
- x.setCurrent((ls.actorsList.getWidth() || 0) * outerWidth.get());
- setInitialized(true);
- }, []);
-
- const relativeOffset = useTransform(
- [x, outerWidth],
- ([value, outerWidth]: number[]) => {
- const center = outerWidth / 2;
- const percent = (value + center) / outerWidth;
- // 0.5 is the center
- // 0 is the left
- // 1 is the right
- return percent;
- },
- );
-
- const opacity = useTransform(relativeOffset, [0, 0.1], [0, 1]);
- const pointerEvents = useTransform(opacity, () => {
- return opacity.get() > 0.5 ? "auto" : "none";
- });
-
- const toggle = useCallback(
- (newValue: boolean) => {
- setFolded(newValue);
- if (newValue) {
- animate(x, -outerWidth.get() / 2);
- } else {
- animate(x, 0);
- }
- },
- [outerWidth, x],
- );
-
- return (
-
- {
- if (ref) {
- const width = ref.getBoundingClientRect().width;
- outerWidth.set(width);
- }
- }}
- >
-
-
-
-
setIsDragging(true)}
- onDrag={(e, info) => {
- const rightPos = outerWidth.get() - info.point.x;
- setFolded(outerWidth.get() - rightPos < 470);
- }}
- onDoubleClick={() => {
- setFolded(!folded);
- if (folded) {
- animate(x, 0);
- } else {
- animate(x, -outerWidth.get() / 2);
- }
- }}
- onDragEnd={(e, info) => {
- if (folded) {
- animate(x, -outerWidth.get() / 2);
- } else {
- const leftPos = outerWidth.get() - info.point.x;
- if (leftPos < RIGHT_PANEL_MIN_WIDTH) {
- animate(
- x,
- outerWidth.get() / 2 -
- RIGHT_PANEL_MIN_WIDTH,
- );
- }
- }
- setIsDragging(false);
- }}
- dragMomentum={false}
- className="w-[1px] bg-border cursor-col-resize inset-y-0 z-20 relative flex items-center group"
- >
-
-
-
- {children}
-
-
-
- );
- },
-);
diff --git a/frontend/packages/components/src/actors/actors-list-row.tsx b/frontend/packages/components/src/actors/actors-list-row.tsx
deleted file mode 100644
index 6108b4df0c..0000000000
--- a/frontend/packages/components/src/actors/actors-list-row.tsx
+++ /dev/null
@@ -1,168 +0,0 @@
-import {
- Button,
- RelativeTime,
- SmallText,
- WithTooltip,
- cn,
- toRecord,
-} from "@rivet-gg/components";
-import { Icon, faTag, faTags } from "@rivet-gg/icons";
-import { Link } from "@tanstack/react-router";
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import { memo } from "react";
-import {
- type Actor,
- type ActorAtom,
- isCurrentActorAtom,
-} from "./actor-context";
-import { ActorRegion } from "./actor-region";
-import { AtomizedActorStatusIndicator } from "./actor-status-indicator";
-import { AtomizedActorStatusLabel } from "./actor-status-label";
-import { ActorTags } from "./actor-tags";
-
-interface ActorsListRowProps {
- className?: string;
- actor: ActorAtom;
-}
-
-const selector = (actor: Actor) => actor.id;
-
-export const ActorsListRow = memo(
- ({ className, actor }: ActorsListRowProps) => {
- const id = useAtomValue(selectAtom(actor, selector));
- const isCurrent = useAtomValue(isCurrentActorAtom(actor));
-
- return (
-
- );
- },
-);
-
-const regionSelector = (actor: Actor) => actor.region;
-
-function Region({ actor }: { actor: ActorAtom }) {
- const regionId = useAtomValue(selectAtom(actor, regionSelector));
-
- return (
-
- );
-}
-
-const tagsSelector = (actor: Actor) => toRecord(actor.tags);
-
-function Tags({ actor }: { actor: ActorAtom }) {
- const tags = useAtomValue(selectAtom(actor, tagsSelector));
-
- const tagCount = Object.keys(tags).length;
-
- return (
-
-
-
-
- {Object.keys(tags).length}{" "}
- {tagCount === 1 ? "tag" : "tags"}
-
-
- }
- content={
- <>
- Tags
-
- >
- }
- />
- );
-}
-
-const createdAtSelector = (actor: Actor) => actor.createdAt;
-
-function CreatedAt({ actor }: { actor: ActorAtom }) {
- const createdAt = useAtomValue(selectAtom(actor, createdAtSelector));
-
- return (
-
- {createdAt ? (
- }
- content={createdAt.toLocaleString()}
- />
- ) : (
- -
- )}
-
- );
-}
-
-const destroyedAtSelector = (actor: Actor) => actor.destroyedAt;
-function DestroyedAt({ actor }: { actor: ActorAtom }) {
- const destroyedAt = useAtomValue(selectAtom(actor, destroyedAtSelector));
-
- return (
-
- {destroyedAt ? (
- }
- content={new Date(destroyedAt).toLocaleString()}
- />
- ) : (
- -
- )}
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actors-list.tsx b/frontend/packages/components/src/actors/actors-list.tsx
deleted file mode 100644
index 2ca0e8242d..0000000000
--- a/frontend/packages/components/src/actors/actors-list.tsx
+++ /dev/null
@@ -1,489 +0,0 @@
-import {
- Button,
- Checkbox,
- CommandGroup,
- CommandItem,
- DocsSheet,
- FilterCreator,
- type FilterDefinitions,
- FilterOp,
- type OnFiltersChange,
- type OptionsProviderProps,
- ScrollArea,
- ShimmerLine,
- SmallText,
- cn,
- createFiltersPicker,
- createFiltersSchema,
-} from "@rivet-gg/components";
-import {
- Icon,
- faActors,
- faCalendarCircleMinus,
- faCalendarCirclePlus,
- faCalendarMinus,
- faCalendarPlus,
- faCode,
- faGlobe,
- faReact,
- faSignalBars,
- faTag,
- faTs,
-} from "@rivet-gg/icons";
-import { useNavigate, useSearch } from "@tanstack/react-router";
-import { useAtomValue, useSetAtom } from "jotai";
-import { useCallback, useMemo } from "react";
-import {
- actorFiltersAtom,
- actorFiltersCountAtom,
- actorRegionsAtom,
- actorTagsAtom,
- actorsAtomsAtom,
- actorsPaginationAtom,
- actorsQueryAtom,
- filteredActorsCountAtom,
-} from "./actor-context";
-import { ActorStatus } from "./actor-status";
-import type { ActorStatus as ActorStatusType } from "./actor-status-indicator";
-import { ActorTag } from "./actor-tags";
-import { ActorsListRow } from "./actors-list-row";
-import { useActorsView } from "./actors-view-context-provider";
-import { CreateActorButton } from "./create-actor-button";
-import { GoToActorButton } from "./go-to-actor-button";
-
-export function ActorsList() {
- return (
- <>
-
-
-
-
-
-
-
-
- Region
-
-
-
-
-
-
ID
-
Tags
-
-
- Created
-
-
-
-
-
-
-
- Destroyed
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
-
-function LoadingIndicator() {
- const state = useAtomValue(actorsQueryAtom);
- if (state.isLoading) {
- return ;
- }
- return null;
-}
-
-function List() {
- const actors = useAtomValue(actorsAtomsAtom);
- return (
- <>
- {actors.map((actor) => (
-
- ))}
- >
- );
-}
-
-function Pagination() {
- const { hasNextPage, isFetchingNextPage, fetchNextPage } =
- useAtomValue(actorsPaginationAtom);
-
- if (hasNextPage) {
- return (
-
-
-
- );
- }
-
- return ;
-}
-
-function EmptyState() {
- const count = useAtomValue(filteredActorsCountAtom);
- const filtersCount = useAtomValue(actorFiltersCountAtom);
- const setFilters = useSetAtom(actorFiltersAtom);
- const { copy } = useActorsView();
-
- return (
-
- {count === 0 ? (
- filtersCount === 0 ? (
-
-
-
- {copy.noActorsFound}
-
-
-
{" "}
-
- Use one of the quick start guides to get
- started.
-
-
-
- }
- >
- TypeScript
-
-
-
- }
- >
- React
-
-
-
-
-
- ) : (
- <>
-
- {copy.noActorsMatchFilter}
-
-
- >
- )
- ) : (
-
- {copy.noMoreActors}
-
- )}
-
- );
-}
-
-const FILTER_DEFINITIONS = {
- tags: {
- type: "select",
- label: "Tags",
- icon: faTag,
- options: TagsOptions,
- operators: {
- [FilterOp.EQUAL]: "is one of",
- [FilterOp.NOT_EQUAL]: "is not one of",
- },
- },
- createdAt: {
- type: "date",
- label: "Created",
- icon: faCalendarCirclePlus,
- },
- destroyedAt: {
- type: "date",
- label: "Destroyed",
- icon: faCalendarCircleMinus,
- },
- status: {
- type: "select",
- label: "Status",
- icon: faSignalBars,
- options: StatusOptions,
- display: ({ value }) => {
- if (value.length > 1) {
- return {value.length} statuses;
- }
- return (
-
- );
- },
- },
- region: {
- type: "select",
- label: "Region",
- icon: faGlobe,
- options: RegionOptions,
- display: ({ value }) => {
- if (value.length > 1) {
- return {value.length} regions;
- }
- const region = useAtomValue(actorRegionsAtom).find(
- (region) => region.id === value[0],
- );
- return {region?.name};
- },
- operators: {
- [FilterOp.EQUAL]: "is one of",
- [FilterOp.NOT_EQUAL]: "is not one of",
- },
- },
- devMode: {
- type: "boolean",
- label: "Show hidden actors",
- icon: faCode,
- },
-} satisfies FilterDefinitions;
-
-export const ActorsListFiltersSchema = createFiltersSchema(FILTER_DEFINITIONS);
-
-export const pickActorListFilters = createFiltersPicker(FILTER_DEFINITIONS);
-
-function Filters() {
- const navigate = useNavigate();
- const filters = useSearch({ strict: false });
-
- const onFiltersChange: OnFiltersChange = useCallback(
- (fnOrValue) => {
- if (typeof fnOrValue === "function") {
- navigate({
- search: ({ actorId, tab, ...filters }) => ({
- actorId,
- tab,
- ...Object.fromEntries(
- Object.entries(fnOrValue(filters)).filter(
- ([, filter]) => filter.value.length > 0,
- ),
- ),
- }),
- });
- } else {
- navigate({
- search: (value) => ({
- actorId: value.actorId,
- tab: value.tab,
- ...Object.fromEntries(
- Object.entries(fnOrValue).filter(
- ([, filter]) => filter.value.length > 0,
- ),
- ),
- }),
- });
- }
- },
- [navigate],
- );
-
- const { copy } = useActorsView();
-
- const filtersDefs = useMemo(() => {
- return {
- ...FILTER_DEFINITIONS,
- devMode: {
- ...FILTER_DEFINITIONS.devMode,
- hidden: true,
- label: copy.showHiddenActors,
- },
- };
- }, [copy.showHiddenActors]);
-
- return (
-
- );
-}
-
-function TagsOptions({ onSelect, value: filterValue }: OptionsProviderProps) {
- const tags = useAtomValue(actorTagsAtom);
-
- const values = filterValue.map((filter) => filter.split("="));
-
- return (
-
- {tags.map(({ key, value }) => {
- const isSelected = values.some(
- ([filterKey, filterValue]) =>
- filterKey === key && filterValue === value,
- );
- return (
- {
- if (isSelected) {
- onSelect(
- values
- .filter(
- ([filterKey, filterValue]) =>
- filterKey !== key ||
- filterValue !== value,
- )
- .map((pair) => pair.join("=")),
- { closeAfter: true },
- );
- return;
- }
- onSelect([...filterValue, `${key}=${value}`], {
- closeAfter: true,
- });
- }}
- >
-
-
-
- {key}={value}
-
-
-
- );
- })}
-
- );
-}
-
-function StatusOptions({ onSelect, value: filterValue }: OptionsProviderProps) {
- return (
-
- {["running", "starting", "crashed", "stopped"].map((key) => {
- const isSelected = filterValue.some((val) => val === key);
- return (
- {
- if (isSelected) {
- onSelect(
- filterValue.filter(
- (filterKey) => filterKey !== key,
- ),
- { closeAfter: true },
- );
- return;
- }
-
- onSelect([...filterValue, key], {
- closeAfter: true,
- });
- }}
- >
-
-
-
- );
- })}
-
- );
-}
-
-function RegionOptions({ onSelect, value: filterValue }: OptionsProviderProps) {
- const regions = useAtomValue(actorRegionsAtom);
- return (
-
- {regions.map(({ id, name }) => {
- const isSelected = filterValue.some((val) => val === id);
- return (
- {
- if (isSelected) {
- onSelect(
- filterValue.filter(
- (filterKey) => filterKey !== id,
- ),
- { closeAfter: true },
- );
- return;
- }
-
- onSelect([...filterValue, id], {
- closeAfter: true,
- });
- }}
- >
-
- {name}
-
- );
- })}
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actors-sidebar-toggle-button.tsx b/frontend/packages/components/src/actors/actors-sidebar-toggle-button.tsx
deleted file mode 100644
index d210653c7a..0000000000
--- a/frontend/packages/components/src/actors/actors-sidebar-toggle-button.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Button } from "@rivet-gg/components";
-import { Icon, faSidebar } from "@rivet-gg/icons";
-import { useActorsLayout } from "./actors-layout-context";
-
-export function ActorsSidebarToggleButton() {
- const { setFolded, isFolded } = useActorsLayout();
-
- if (!isFolded) {
- return null;
- }
- return (
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/actors-view-context-provider.tsx b/frontend/packages/components/src/actors/actors-view-context-provider.tsx
deleted file mode 100644
index d7a063f1ae..0000000000
--- a/frontend/packages/components/src/actors/actors-view-context-provider.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { createContext, useContext } from "react";
-
-const defaultValue = {
- copy: {
- createActor: "Create Actor",
- createActorUsingForm: "Create Actor using simple form",
- noActorsFound: "No Actors found",
- selectActor: (
- <>
- No Actor selected.
-
{" "}
-
- Select an Actor from the list to view its details.
-
- >
- ),
- goToActor: "Go to Actor",
- showActorList: "Show Actor List",
- showHiddenActors: "Show hidden Actors",
- actorId: "Actor ID",
- noActorsMatchFilter: "No Actors match the filters.",
- noMoreActors: "No more Actors to load.",
-
- createActorModal: {
- title: "Create Actor",
- description:
- "Choose a build to create an Actor from. Actor will be created using default settings.",
- },
-
- actorNotFound: "Actor not found",
- actorNotFoundDescription:
- "Change your filters to find the Actor you are looking for.",
-
- gettingStarted: {
- title: "Getting Started with Actors",
- description:
- "Use a quick start guide to start deploying Actors to your environment.",
- },
- },
- canCreate: true,
-};
-
-export const ActorsViewContext =
- createContext(defaultValue);
-
-export const useActorsView = () => {
- const context = useContext(ActorsViewContext);
- if (!context) {
- throw new Error(
- "useActorsView must be used within a ActorsViewContextProvider",
- );
- }
- return context;
-};
diff --git a/frontend/packages/components/src/actors/build-select.tsx b/frontend/packages/components/src/actors/build-select.tsx
deleted file mode 100644
index ac5ed0eecf..0000000000
--- a/frontend/packages/components/src/actors/build-select.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import { Badge, Combobox } from "@rivet-gg/components";
-import { useAtomValue } from "jotai";
-import { useMemo } from "react";
-import { actorBuildsAtom } from "./actor-context";
-
-interface BuildSelectProps {
- onValueChange: (value: string) => void;
- value: string;
- onlyCurrent?: boolean;
-}
-
-export function BuildSelect({
- onValueChange,
- value,
- onlyCurrent,
-}: BuildSelectProps) {
- const data = useAtomValue(actorBuildsAtom);
-
- const builds = useMemo(() => {
- let sorted = data.toSorted(
- (a, b) => b.createdAt.valueOf() - a.createdAt.valueOf(),
- );
-
- if (onlyCurrent) {
- sorted = sorted.filter((build) => build.tags.current);
- }
-
- const findLatest = (name: string) =>
- sorted.find((build) => build.tags.name === name);
- return sorted.map((build, index, array) => {
- return {
- label: (
-
-
-
- {build.tags.name || build.id.split("-")[0]}
-
- {findLatest(build.tags.name)?.id ===
- build.id ? (
-
- Latest
-
- ) : null}
-
-
- Created: {build.createdAt.toLocaleString()}
-
-
-
- ),
- value: build.id,
- build,
- };
- });
- }, [data, onlyCurrent]);
-
- return (
-
- option.build.name.includes(search) ||
- option.build.tags.name.includes(search)
- }
- className="w-full h-14"
- />
- );
-}
diff --git a/frontend/packages/components/src/actors/console/actor-console-input.tsx b/frontend/packages/components/src/actors/console/actor-console-input.tsx
deleted file mode 100644
index 58689575ef..0000000000
--- a/frontend/packages/components/src/actors/console/actor-console-input.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { Button, ScrollArea } from "@rivet-gg/components";
-import { useRef } from "react";
-import { useActorRpcs, useActorWorker } from "../worker/actor-worker-context";
-import { ActorConsoleMessage } from "./actor-console-message";
-import { ReplInput, type ReplInputRef, replaceCode } from "./repl-input";
-
-export function ActorConsoleInput() {
- const worker = useActorWorker();
- const rpcs = useActorRpcs();
- const ref = useRef(null);
-
- return (
-
-
-
- {
- worker.run(code);
- }}
- />
-
-
-
- {rpcs.map((rpc) => (
-
- ))}
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/console/actor-console-log-formatted.tsx b/frontend/packages/components/src/actors/console/actor-console-log-formatted.tsx
deleted file mode 100644
index 11545a63f5..0000000000
--- a/frontend/packages/components/src/actors/console/actor-console-log-formatted.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import type { FormattedCode } from "../worker/actor-worker-schema";
-
-export function ActorConsoleLogFormatted({ tokens }: FormattedCode) {
- return (
- <>
- {tokens.map((tokensLine, index) => (
-
- {tokensLine.map((token, index) => (
-
- {token.content}
-
- ))}
-
- ))}
- >
- );
-}
diff --git a/frontend/packages/components/src/actors/console/actor-console-log.tsx b/frontend/packages/components/src/actors/console/actor-console-log.tsx
deleted file mode 100644
index 38b97eb0cf..0000000000
--- a/frontend/packages/components/src/actors/console/actor-console-log.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { memo } from "react";
-import type { ReplCommand } from "../worker/actor-worker-container";
-import { ActorConsoleLogFormatted } from "./actor-console-log-formatted";
-import { ActorConsoleMessage } from "./actor-console-message";
-import { ActorObjectInspector } from "./actor-inspector";
-
-type ActorConsoleLogProps = ReplCommand & {
- showTimestmaps: boolean;
-};
-
-export const ActorConsoleLog = memo((props: ActorConsoleLogProps) => {
- return (
- <>
-
- {"formatted" in props && props.formatted ? (
-
- ) : null}
-
- {"error" in props ? (
-
- {props.error &&
- typeof props.error === "object" &&
- "toString" in props.error
- ? props.error.toString()
- : JSON.stringify(props.error)}
-
- ) : null}
- {props.logs?.map((log, index) => (
-
- {log.data?.map((element, key) => {
- if (typeof element === "string") {
- return (
-
- {element}
-
- );
- }
- return (
-
- );
- })}
-
- ))}
- {"result" in props ? (
-
- {typeof props.result === "string" ? (
- props.result
- ) : (
-
- )}
-
- ) : null}
- >
- );
-});
diff --git a/frontend/packages/components/src/actors/console/actor-console-logs.tsx b/frontend/packages/components/src/actors/console/actor-console-logs.tsx
deleted file mode 100644
index bd3593a6ba..0000000000
--- a/frontend/packages/components/src/actors/console/actor-console-logs.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { ScrollArea } from "@rivet-gg/components";
-import { useLayoutEffect, useRef } from "react";
-import { useActorDetailsSettings } from "../actor-details-settings";
-import { useActorReplCommands } from "../worker/actor-worker-context";
-import { ActorConsoleLog } from "./actor-console-log";
-
-export function ActorConsoleLogs() {
- const isScrolledToBottom = useRef(true);
- const ref = useRef(null);
- const commands = useActorReplCommands();
-
- const [settings] = useActorDetailsSettings();
-
- // biome-ignore lint/correctness/useExhaustiveDependencies: we want to run this effect on every commands change
- useLayoutEffect(() => {
- if (ref.current && isScrolledToBottom.current) {
- ref.current.scrollTop = ref.current.scrollHeight;
- }
- }, [commands]);
-
- return (
- {
- if (ref.current) {
- isScrolledToBottom.current =
- ref.current.scrollTop + ref.current.clientHeight >=
- ref.current.scrollHeight - 1;
- }
- },
- }}
- className="w-full flex-1"
- >
- {commands.map((log) => (
-
- ))}
-
- );
-}
diff --git a/frontend/packages/components/src/actors/console/actor-console-message.tsx b/frontend/packages/components/src/actors/console/actor-console-message.tsx
deleted file mode 100644
index eaba9b1e3d..0000000000
--- a/frontend/packages/components/src/actors/console/actor-console-message.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import { cn } from "@rivet-gg/components";
-import {
- Icon,
- faAngleLeft,
- faAngleRight,
- faExclamationCircle,
- faSpinnerThird,
- faWarning,
-} from "@rivet-gg/icons";
-import { format } from "date-fns";
-import { type ReactNode, forwardRef } from "react";
-
-interface ActorConsoleMessageProps {
- variant:
- | "input"
- | "input-pending"
- | "output"
- | "error"
- | "log"
- | "warn"
- | "info"
- | "debug";
- timestamp?: Date;
- className?: string;
- children: ReactNode;
-}
-
-export const ActorConsoleMessage = forwardRef<
- HTMLDivElement,
- ActorConsoleMessageProps
->(({ children, variant, timestamp, className, ...props }, ref) => {
- return (
-
-
-
-
-
- {timestamp
- ? format(timestamp, "LLL dd HH:mm:ss").toUpperCase()
- : null}
-
-
- {children}
-
-
- );
-});
-
-export const ConsoleMessageVariantIcon = ({
- variant,
- className,
-}: {
- variant: string;
- className?: string;
-}) => {
- if (variant === "input") {
- return ;
- }
- if (variant === "input-pending") {
- return (
-
- );
- }
- if (variant === "output") {
- return ;
- }
- if (variant === "error") {
- return (
-
- );
- }
- if (variant === "warn") {
- return ;
- }
- return ;
-};
-
-export const getConsoleMessageVariant = (variant: string) =>
- cn({
- "bg-red-950/30 border-red-800/40 text-red-400 z-10":
- variant === "error",
- "bg-yellow-500/10 border-yellow-800/40 text-yellow-200 z-10":
- variant === "warn",
- "bg-blue-950/30 border-blue-800/40 text-blue-400 z-10":
- variant === "debug",
- });
diff --git a/frontend/packages/components/src/actors/console/actor-console.tsx b/frontend/packages/components/src/actors/console/actor-console.tsx
deleted file mode 100644
index 681cbbf10d..0000000000
--- a/frontend/packages/components/src/actors/console/actor-console.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Button } from "@rivet-gg/components";
-import { Icon, faChevronDown } from "@rivet-gg/icons";
-import { AnimatePresence, motion } from "framer-motion";
-import { useState } from "react";
-import { useActorWorkerStatus } from "../worker/actor-worker-context";
-import { ActorWorkerStatus } from "../worker/actor-worker-status";
-import { ActorConsoleInput } from "./actor-console-input";
-import { ActorConsoleLogs } from "./actor-console-logs";
-
-export function ActorConsole() {
- const [isOpen, setOpen] = useState(false);
-
- const status = useActorWorkerStatus();
-
- const isBlocked = status.type !== "ready";
-
- return (
-
-
-
- {isOpen && !isBlocked ? (
-
-
-
-
- ) : null}
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/console/actor-inspector.tsx b/frontend/packages/components/src/actors/console/actor-inspector.tsx
deleted file mode 100644
index 87949e7a13..0000000000
--- a/frontend/packages/components/src/actors/console/actor-inspector.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { cn } from "@rivet-gg/components";
-import type { ComponentProps } from "react";
-import { Inspector, type ObjectInspector, chromeDark } from "react-inspector";
-
-const INSPECTOR_THEME = {
- ...chromeDark,
- BASE_BACKGROUND_COLOR: "transparent",
-};
-
-export function ActorObjectInspector(
- props: ComponentProps,
-) {
- return (
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/console/repl-input.tsx b/frontend/packages/components/src/actors/console/repl-input.tsx
deleted file mode 100644
index fc7ef1e372..0000000000
--- a/frontend/packages/components/src/actors/console/repl-input.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import {
- CodeMirror,
- type CodeMirrorRef,
- type CompletionContext,
- EditorView,
- External,
- defaultKeymap,
- javascript,
- javascriptLanguage,
- keymap,
-} from "@rivet-gg/components/code-mirror";
-import { forwardRef } from "react";
-
-export const replaceCode = (editor: EditorView, code: string) => {
- return editor.dispatch({
- changes: {
- from: 0,
- to: editor.state.doc.length,
- insert: code,
- },
- selection: { anchor: code.length },
- scrollIntoView: true,
- annotations: [External.of(true)],
- });
-};
-
-const deleteBgTheme = EditorView.theme({
- ".cm-content": { padding: 0 },
-});
-
-export type ReplInputRef = CodeMirrorRef;
-
-interface ReplInputProps {
- className: string;
- rpcs: string[];
- onRun: (code: string) => void;
-}
-
-export const ReplInput = forwardRef(
- ({ rpcs, onRun, className }, ref) => {
- const rivetKeymap = keymap.of([
- {
- key: "Enter",
- run: (editor) => {
- onRun(editor?.state.doc.toString());
- editor.dispatch({
- changes: {
- from: 0,
- to: editor.state.doc.length,
- insert: "",
- },
- annotations: [External.of(true)],
- });
- return true;
- },
- },
- ...defaultKeymap,
- ]);
-
- const replAutocomplete = javascriptLanguage.data.of({
- autocomplete: (context: CompletionContext) => {
- const word = context.matchBefore(/^actor\.\w*/);
- if (!word || (word?.from === word?.to && !context.explicit))
- return null;
- return {
- from: word.from,
- to: word.to,
- boost: 99,
- options: rpcs.map((rpc) => ({
- label: `actor.${rpc}(/* args */)`,
- apply: `actor.${rpc}(`,
- validFor: /^actor\.\w*$/,
- info: `Call "${rpc}" RPC on Actor`,
- })),
- };
- },
- });
-
- return (
-
- );
- },
-);
diff --git a/frontend/packages/components/src/actors/create-actor-button.tsx b/frontend/packages/components/src/actors/create-actor-button.tsx
deleted file mode 100644
index 3748de59bb..0000000000
--- a/frontend/packages/components/src/actors/create-actor-button.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { Button, type ButtonProps, WithTooltip } from "@rivet-gg/components";
-import { Icon, faPlus } from "@rivet-gg/icons";
-import { useNavigate } from "@tanstack/react-router";
-import { useAtomValue } from "jotai";
-import {
- actorBuildsCountAtom,
- actorManagerEndpointAtom,
-} from "./actor-context";
-import { useActorsView } from "./actors-view-context-provider";
-
-export function CreateActorButton(props: ButtonProps) {
- const navigate = useNavigate();
- const builds = useAtomValue(actorBuildsCountAtom);
- const endpoint = useAtomValue(actorManagerEndpointAtom);
-
- const { copy, canCreate: contextAllowActorsCreation } = useActorsView();
-
- const canCreate = builds > 0 && contextAllowActorsCreation && endpoint;
-
- if (!contextAllowActorsCreation) {
- return null;
- }
-
- const content = (
-
-
-
- );
-
- if (canCreate) {
- return content;
- }
-
- return (
-
- );
-}
diff --git a/frontend/packages/components/src/actors/current-environment-version-title.tsx b/frontend/packages/components/src/actors/current-environment-version-title.tsx
deleted file mode 100644
index e38d15afec..0000000000
--- a/frontend/packages/components/src/actors/current-environment-version-title.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { useSuspenseQuery } from "@tanstack/react-query";
-import {
- projectEnvironmentQueryOptions,
- projectVersionQueryOptions,
-} from "../queries";
-import { EnvironmentVersionTitle } from "./environment-version-title";
-
-interface CurrentEnvironmentVersionTitleProps {
- environmentId: string;
- projectId: string;
-}
-
-export function CurrentEnvironmentVersionTitle({
- environmentId,
- projectId,
-}: CurrentEnvironmentVersionTitleProps) {
- const {
- data: { namespace: environment },
- } = useSuspenseQuery(
- projectEnvironmentQueryOptions({ projectId, environmentId }),
- );
-
- const { data: version } = useSuspenseQuery(
- projectVersionQueryOptions({
- projectId,
- versionId: environment.versionId,
- }),
- );
-
- return (
-
- );
-}
diff --git a/frontend/packages/components/src/actors/dialogs/create-actor-dialog.tsx b/frontend/packages/components/src/actors/dialogs/create-actor-dialog.tsx
deleted file mode 100644
index 9dc8b0745f..0000000000
--- a/frontend/packages/components/src/actors/dialogs/create-actor-dialog.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { useAtomValue } from "jotai";
-import {
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
-} from "../../ui/dialog";
-import { Flex } from "../../ui/flex";
-import { createActorAtom } from "../actor-context";
-import { useActorsView } from "../actors-view-context-provider";
-import * as ActorCreateForm from "../form/actor-create-form";
-import type { DialogContentProps } from "../hooks";
-
-interface ContentProps extends DialogContentProps {}
-
-export default function CreateActorDialog({ onClose }: ContentProps) {
- const { endpoint, create } = useAtomValue(createActorAtom);
-
- const { copy } = useActorsView();
-
- return (
- <>
- {
- if (!endpoint) {
- throw new Error("No endpoint");
- }
- await create({
- endpoint,
- id: values.buildId,
- tags: Object.fromEntries(
- values.tags.map((tag) => [tag.key, tag.value]),
- ),
- params: values.parameters
- ? JSON.parse(values.parameters)
- : undefined,
- region: values.regionId,
- });
- onClose?.();
- }}
- defaultValues={{ buildId: "", regionId: "" }}
- >
-
- {copy.createActorModal.title}
-
- {copy.createActorModal.description}
-
-
-
-
-
-
- {/* */}
-
-
-
- Create
-
-
-
- >
- );
-}
diff --git a/frontend/packages/components/src/actors/dialogs/go-to-actor-dialog.tsx b/frontend/packages/components/src/actors/dialogs/go-to-actor-dialog.tsx
deleted file mode 100644
index 3baf32d185..0000000000
--- a/frontend/packages/components/src/actors/dialogs/go-to-actor-dialog.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Button } from "../../ui/button";
-import { DialogFooter, DialogHeader, DialogTitle } from "../../ui/dialog";
-import { useActorsView } from "../actors-view-context-provider";
-import * as GoToActorForm from "../form/go-to-actor-form";
-import type { DialogContentProps } from "../hooks";
-
-interface ContentProps extends DialogContentProps {
- onSubmit?: (actorId: string) => void;
-}
-
-export default function GoToActorDialogContent({
- onClose,
- onSubmit,
-}: ContentProps) {
- const { copy } = useActorsView();
- return (
- {
- onSubmit?.(actorId);
- }}
- >
-
- {copy.goToActor}
-
-
-
-
- Go
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/dynamic-servers-feature-card.tsx b/frontend/packages/components/src/actors/dynamic-servers-feature-card.tsx
deleted file mode 100644
index 7df263034b..0000000000
--- a/frontend/packages/components/src/actors/dynamic-servers-feature-card.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import {
- Card,
- CardContent,
- CardHeader,
- CardTitle,
- Link,
- Text,
-} from "@rivet-gg/components";
-import { Link as RouterLink } from "@tanstack/react-router";
-
-export function DynamicServersFeatureCard() {
- return (
-
-
- Legacy Lobbies and Environments
-
-
-
- Dynamic servers and builds are the new way to manage your
- project servers. However, if you're looking for lobbies and
- namespaces, you can switch back to the old interface in the{" "}
-
-
- User Settings
-
-
- .
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/environment-select.tsx b/frontend/packages/components/src/actors/environment-select.tsx
deleted file mode 100644
index 2261946dbc..0000000000
--- a/frontend/packages/components/src/actors/environment-select.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { projectEnvironmentsQueryOptions } from "@/domains/project/queries";
-import {
- Flex,
- Select,
- SelectContent,
- SelectItem,
- SelectSeparator,
- SelectTrigger,
- SelectValue,
-} from "@rivet-gg/components";
-import { Icon, faCirclePlus } from "@rivet-gg/icons";
-import { useSuspenseQuery } from "@tanstack/react-query";
-import { type ComponentProps, useCallback } from "react";
-
-interface EnvironmentSelectProps extends ComponentProps {
- projectId: string;
- showCreateEnvironment?: boolean;
- onCreateClick?: () => void;
- variant?: ComponentProps["variant"];
-}
-
-export function EnvironmentSelect({
- showCreateEnvironment,
- onCreateClick,
- onValueChange,
- projectId,
- variant,
- ...props
-}: EnvironmentSelectProps) {
- const { data } = useSuspenseQuery(
- projectEnvironmentsQueryOptions(projectId),
- );
-
- const handleValueChange = useCallback(
- (value: string) => {
- if (value === "create") {
- onCreateClick?.();
- return;
- }
- onValueChange?.(value);
- },
- [onCreateClick, onValueChange],
- );
-
- return (
-
- );
-}
diff --git a/frontend/packages/components/src/actors/environment-version-title.tsx b/frontend/packages/components/src/actors/environment-version-title.tsx
deleted file mode 100644
index 0777d81cda..0000000000
--- a/frontend/packages/components/src/actors/environment-version-title.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Badge, Flex } from "@rivet-gg/components";
-
-interface EnvironmentVersionTitleProps {
- environment: string;
- version: string;
-}
-
-export function EnvironmentVersionTitle({
- environment,
- version,
-}: EnvironmentVersionTitleProps) {
- return (
-
- {environment}
- {version}
-
- );
-}
diff --git a/frontend/packages/components/src/actors/form/actor-create-form.tsx b/frontend/packages/components/src/actors/form/actor-create-form.tsx
deleted file mode 100644
index bf92b99b46..0000000000
--- a/frontend/packages/components/src/actors/form/actor-create-form.tsx
+++ /dev/null
@@ -1,150 +0,0 @@
-import {
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
- Label,
- createSchemaForm,
-} from "@rivet-gg/components";
-import { JsonCode } from "@rivet-gg/components/code-mirror";
-import { type UseFormReturn, useFormContext } from "react-hook-form";
-import z from "zod";
-
-import { useAtomValue, useSetAtom } from "jotai";
-import {
- actorCustomTagKeys,
- actorCustomTagValues,
- actorTagKeysAtom,
- actorTagValuesAtom,
-} from "../actor-context";
-import { BuildSelect } from "../build-select";
-import { RegionSelect } from "../region-select";
-import {
- Tags as TagsInput,
- formSchema as tagsFormSchema,
-} from "./build-tags-form";
-
-const jsonValid = z.custom((value) => {
- try {
- JSON.parse(value);
- return true;
- } catch {
- return false;
- }
-});
-
-export const formSchema = z.object({
- buildId: z.string().nonempty("Build is required"),
- regionId: z.string(),
- parameters: jsonValid.optional(),
- tags: tagsFormSchema.shape.tags,
-});
-
-export type FormValues = z.infer;
-export type SubmitHandler = (
- values: FormValues,
- form: UseFormReturn,
-) => Promise;
-
-const { Form, Submit } = createSchemaForm(formSchema);
-export { Form, Submit };
-
-export const Build = () => {
- const { control } = useFormContext();
- return (
- (
-
- Build
-
-
-
-
-
- )}
- />
- );
-};
-
-export const Region = () => {
- const { control } = useFormContext();
-
- return (
- (
-
- Region
-
-
-
-
-
- )}
- />
- );
-};
-
-export const Parameters = () => {
- const { control } = useFormContext();
- return (
- (
-
- Parameters
-
-
-
-
-
- )}
- />
- );
-};
-
-export const Tags = () => {
- const setValues = useSetAtom(actorCustomTagValues);
- const setKeys = useSetAtom(actorCustomTagKeys);
-
- const keys = useAtomValue(actorTagKeysAtom);
- const values = useAtomValue(actorTagValuesAtom);
-
- return (
-
-
- ({
- label: key,
- value: key,
- }))}
- values={values.map((value) => ({
- label: value,
- value: value,
- }))}
- onCreateKeyOption={(value) => {
- setKeys((old) =>
- Array.from(new Set([...old, value]).values()),
- );
- }}
- onCreateValueOption={(value) => {
- setValues((old) =>
- Array.from(new Set([...old, value]).values()),
- );
- }}
- />
-
- );
-};
diff --git a/frontend/packages/components/src/actors/form/build-tags-form.tsx b/frontend/packages/components/src/actors/form/build-tags-form.tsx
deleted file mode 100644
index b431baf7cd..0000000000
--- a/frontend/packages/components/src/actors/form/build-tags-form.tsx
+++ /dev/null
@@ -1,160 +0,0 @@
-import { Icon, faTrash } from "@rivet-gg/icons";
-import {
- type UseFormReturn,
- useFieldArray,
- useFormContext,
-} from "react-hook-form";
-import z from "zod";
-import { createSchemaForm } from "../../lib/create-schema-form";
-import { Button } from "../../ui/button";
-import { Combobox, type ComboboxOption as Option } from "../../ui/combobox";
-import {
- FormControl,
- FormFieldContext,
- FormItem,
- FormLabel,
- FormMessage,
-} from "../../ui/form";
-import { Text } from "../../ui/typography";
-
-export const formSchema = z.object({
- tags: z
- .array(
- z.object({
- key: z.string().min(1),
- value: z.string(),
- }),
- )
- .superRefine((tags, ctx) => {
- const tagsSet = new Set();
- for (const [idx, tag] of [...tags].reverse().entries()) {
- if (tagsSet.has(tag.key)) {
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- path: [idx, "key"],
- message: "Tag keys must be unique",
- });
- }
- tagsSet.add(tag.key);
- }
- }),
-});
-
-export type FormValues = z.infer;
-export type SubmitHandler = (
- values: FormValues,
- form: UseFormReturn,
-) => Promise;
-
-const { Form, Submit } = createSchemaForm(formSchema);
-export { Form, Submit };
-
-export const Tags = ({
- onCreateKeyOption,
- onCreateValueOption,
- keys,
- values,
-}: {
- onCreateKeyOption: (option: string) => void;
- onCreateValueOption: (option: string) => void;
- keys: Option[];
- values: Option[];
-}) => {
- const { control, setValue, watch } = useFormContext();
- const { fields, append, remove } = useFieldArray({
- name: "tags",
- control,
- });
-
- return (
- <>
- {fields.length === 0 ? (
-
- There's no tags added.
-
- ) : null}
- {fields.map((field, index) => (
-
-
-
- Key
-
- {
- setValue(`tags.${index}.key`, value, {
- shouldDirty: true,
- shouldTouch: true,
- shouldValidate: true,
- });
- }}
- allowCreate
- onCreateOption={onCreateKeyOption}
- />
-
-
-
-
-
-
-
- Value
-
- {
- setValue(`tags.${index}.value`, value, {
- shouldDirty: true,
- shouldTouch: true,
- shouldValidate: true,
- });
- }}
- allowCreate
- onCreateOption={onCreateValueOption}
- />
-
-
-
-
-
-
- ))}
-
- >
- );
-};
diff --git a/frontend/packages/components/src/actors/form/go-to-actor-form.tsx b/frontend/packages/components/src/actors/form/go-to-actor-form.tsx
deleted file mode 100644
index 4b7acdf0d2..0000000000
--- a/frontend/packages/components/src/actors/form/go-to-actor-form.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { type UseFormReturn, useFormContext } from "react-hook-form";
-import z from "zod";
-import { createSchemaForm } from "../../lib/create-schema-form";
-import {
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "../../ui/form";
-import { Input } from "../../ui/input";
-import { useActorsView } from "../actors-view-context-provider";
-
-export const formSchema = z.object({
- actorId: z.string().nonempty("Actor ID is required").uuid(),
-});
-
-export type FormValues = z.infer;
-export type SubmitHandler = (
- values: FormValues,
- form: UseFormReturn,
-) => Promise;
-
-const { Form, Submit } = createSchemaForm(formSchema);
-export { Form, Submit };
-
-export const ActorId = () => {
- const { control } = useFormContext();
- const { copy } = useActorsView();
- return (
- (
-
- {copy.actorId}
-
-
-
-
-
- )}
- />
- );
-};
diff --git a/frontend/packages/components/src/actors/get-started.tsx b/frontend/packages/components/src/actors/get-started.tsx
deleted file mode 100644
index 8039cb19f9..0000000000
--- a/frontend/packages/components/src/actors/get-started.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { Icon, faActors, faFunction, faServer } from "@rivet-gg/icons";
-import { motion } from "framer-motion";
-import type { ComponentProps } from "react";
-import { DocsSheet } from "../docs-sheet";
-import { cn } from "../lib/utils";
-import { Button } from "../ui/button";
-
-export function ActorsResources() {
- return (
- <>
-
-
-
-
-
- >
- );
-}
-
-const linkVariants = {
- hidden: {
- opacity: 0,
- },
- show: {
- opacity: 1,
- },
-};
-
-interface ExampleLinkProps {
- title: string;
- description?: string;
- icon: ComponentProps["icon"];
- href: string;
- size?: "sm" | "md" | "lg";
-}
-
-function ExampleLink({
- title,
- description,
- icon,
- href,
- size = "lg",
-}: ExampleLinkProps) {
- return (
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/getting-started.tsx b/frontend/packages/components/src/actors/getting-started.tsx
deleted file mode 100644
index e64f8ac502..0000000000
--- a/frontend/packages/components/src/actors/getting-started.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Icon, faActors } from "@rivet-gg/icons";
-import { useActorsView } from "./actors-view-context-provider";
-import { ActorsResources } from "./get-started";
-
-export function GettingStarted() {
- const { copy } = useActorsView();
- return (
-
-
-
-
- {copy.gettingStarted.title}
-
-
- {copy.gettingStarted.description}
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/go-to-actor-button.tsx b/frontend/packages/components/src/actors/go-to-actor-button.tsx
deleted file mode 100644
index b38d67f676..0000000000
--- a/frontend/packages/components/src/actors/go-to-actor-button.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Button, type ButtonProps } from "@rivet-gg/components";
-import { Icon, faMagnifyingGlass } from "@rivet-gg/icons";
-import { useNavigate } from "@tanstack/react-router";
-import { useActorsView } from "./actors-view-context-provider";
-
-export function GoToActorButton(props: ButtonProps) {
- const navigate = useNavigate();
- const { copy } = useActorsView();
- return (
-
- );
-}
diff --git a/frontend/packages/components/src/actors/group-project-select.tsx b/frontend/packages/components/src/actors/group-project-select.tsx
deleted file mode 100644
index d1f7fa6c86..0000000000
--- a/frontend/packages/components/src/actors/group-project-select.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { groupProjectsQueryOptions } from "@/domains/project/queries";
-import {
- Flex,
- Select,
- SelectContent,
- SelectItem,
- SelectSeparator,
- SelectTrigger,
- SelectValue,
-} from "@rivet-gg/components";
-import { Icon, faCirclePlus } from "@rivet-gg/icons";
-import { useSuspenseQuery } from "@tanstack/react-query";
-import { type ComponentProps, useCallback } from "react";
-
-interface GroupProjectSelectProps extends ComponentProps {
- groupId: string;
- showCreateProject?: boolean;
- onCreateClick?: () => void;
- variant?: ComponentProps["variant"];
-}
-
-export function GroupProjectSelect({
- groupId,
- showCreateProject,
- onCreateClick,
- onValueChange,
- variant,
- ...props
-}: GroupProjectSelectProps) {
- const { data } = useSuspenseQuery(groupProjectsQueryOptions(groupId));
-
- const handleValueChange = useCallback(
- (value: string) => {
- if (value === "create") {
- onCreateClick?.();
- return;
- }
- onValueChange?.(value);
- },
- [onCreateClick, onValueChange],
- );
-
- return (
-
- );
-}
diff --git a/frontend/packages/components/src/actors/hooks/index.ts b/frontend/packages/components/src/actors/hooks/index.ts
deleted file mode 100644
index 14c4d0400f..0000000000
--- a/frontend/packages/components/src/actors/hooks/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./use-dialog";
diff --git a/frontend/packages/components/src/actors/hooks/use-dialog.tsx b/frontend/packages/components/src/actors/hooks/use-dialog.tsx
deleted file mode 100644
index 77373da6c4..0000000000
--- a/frontend/packages/components/src/actors/hooks/use-dialog.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-"use client";
-import {
- type ComponentProps,
- type ComponentType,
- lazy,
- useCallback,
- useMemo,
- useState,
-} from "react";
-import { Dialog, DialogContent, type DialogProps } from "../../ui/dialog";
-
-export interface DialogContentProps {
- onClose?: () => void;
-}
-
-interface DialogConfig {
- autoFocus?: boolean;
-}
-
-export const createDialogHook = <
- // biome-ignore lint/suspicious/noExplicitAny: we don't know the type of the component, so we use any
- Component extends Promise<{ default: ComponentType }>,
->(
- component: Component,
- opts: DialogConfig = {},
-) => {
- const DialogImpl = ({
- dialogProps,
- ...props
- }: ComponentProps["default"]> & {
- dialogProps?: DialogProps;
- }) => {
- // biome-ignore lint/correctness/useExhaustiveDependencies: component here is a static value, won't change over time
- const Content = useMemo(() => lazy(() => component), []);
-
- return (
-
- );
- };
-
- const useHook = (props: ComponentProps["default"]>) => {
- const [isOpen, setIsOpen] = useState(() => false);
-
- const close = useCallback(() => {
- setIsOpen(false);
- }, []);
-
- const open = useCallback(() => {
- setIsOpen(true);
- }, []);
-
- const handleOpenChange = useCallback((open: boolean) => {
- setIsOpen(open);
- }, []);
-
- return {
- open,
- close,
- dialog: (
-
- ),
- };
- };
-
- useHook.Dialog = DialogImpl;
-
- return useHook;
-};
-
-export const createDataDialogHook = <
- const DataPropKeys extends string[],
- // biome-ignore lint/suspicious/noExplicitAny: we don't know the type of the component, so we use any
- Component extends Promise<{ default: ComponentType }>,
->(
- _: DataPropKeys,
- component: Component,
- opts: DialogConfig = {},
-) => {
- return (
- props: Omit<
- ComponentProps["default"]>,
- DataPropKeys[number]
- >,
- ) => {
- const [isOpen, setIsOpen] = useState(false);
- const [data, setData] =
- useState<
- Pick<
- ComponentProps["default"]>,
- DataPropKeys[number]
- >
- >();
-
- const close = useCallback(() => {
- setIsOpen(false);
- }, []);
-
- const open = useCallback(
- (
- data: Pick<
- ComponentProps["default"]>,
- DataPropKeys[number]
- >,
- ) => {
- setIsOpen(true);
- setData(data);
- },
- [],
- );
-
- // biome-ignore lint/correctness/useExhaustiveDependencies: component here is a static value, won't change over time
- const Content = useMemo(() => lazy(() => component), []);
-
- return {
- open,
- dialog: (
-
- ),
- };
- };
-};
-
-export function useDialog() {}
-
-useDialog.GoToActor = createDialogHook(import("../dialogs/go-to-actor-dialog"));
-
-useDialog.Feedback = createDialogHook(import("../../dialogs/feedback-dialog"));
-useDialog.CreateActor = createDialogHook(
- import("../dialogs/create-actor-dialog"),
-);
diff --git a/frontend/packages/components/src/actors/index.tsx b/frontend/packages/components/src/actors/index.tsx
deleted file mode 100644
index dd5baf45d2..0000000000
--- a/frontend/packages/components/src/actors/index.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-export * from "./getting-started";
-export * from "./actors-list-preview";
-export * from "./actor-tags";
-export * from "./actor-context";
-export * from "./actors-actor-details";
-export * from "./hooks/index";
-export { getActorStatus } from "./actor-status-indicator";
-export * from "./actors-layout";
-export * from "./actors-layout-context";
-export * from "./console/actor-console-message";
-export * from "./actor-region";
-export * from "./console/actor-inspector";
-export * from "./actor-status-indicator";
-export * from "./actor-status-label";
-export * from "./actors-view-context-provider";
-export * from "./actor-not-found";
-export { ActorsListFiltersSchema, pickActorListFilters } from "./actors-list";
diff --git a/frontend/packages/components/src/actors/matchmaker-lobby-config-settings-card.tsx b/frontend/packages/components/src/actors/matchmaker-lobby-config-settings-card.tsx
deleted file mode 100644
index c6112dea98..0000000000
--- a/frontend/packages/components/src/actors/matchmaker-lobby-config-settings-card.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import * as MatchmakerLobbyConfigForm from "@/domains/project/forms/matchmaker-lobby-config-form";
-import {
- Card,
- CardContent,
- CardFooter,
- CardHeader,
- CardTitle,
- Flex,
-} from "@rivet-gg/components";
-import { useSuspenseQuery } from "@tanstack/react-query";
-import { useMatchmakerLobbyConfigFormHandler } from "../hooks/use-matchmaker-lobby-config-form-handler";
-import { projectEnvironmentQueryOptions } from "../queries";
-
-interface MatchMakerLobbyConfigSettingsCardProps {
- projectId: string;
- environmentId: string;
-}
-
-export function MatchMakerLobbyConfigSettingsCard({
- environmentId,
- projectId,
-}: MatchMakerLobbyConfigSettingsCardProps) {
- const { data } = useSuspenseQuery(
- projectEnvironmentQueryOptions({ projectId, environmentId }),
- );
-
- const handleSubmit = useMatchmakerLobbyConfigFormHandler({
- environmentId,
- projectId,
- });
-
- return (
-
-
-
- Config
-
-
-
-
-
-
-
-
-
- Save
-
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/project-builds-table-actions.tsx b/frontend/packages/components/src/actors/project-builds-table-actions.tsx
deleted file mode 100644
index 47dcea2344..0000000000
--- a/frontend/packages/components/src/actors/project-builds-table-actions.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import {
- Button,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@rivet-gg/components";
-import { Icon, faEllipsisH } from "@rivet-gg/icons";
-import { useNavigate } from "@tanstack/react-router";
-
-interface ProjectBuildsTableActionsProps {
- buildId: string;
-}
-
-export function ProjectBuildsTableActions({
- buildId,
-}: ProjectBuildsTableActionsProps) {
- const navigate = useNavigate();
- return (
-
-
-
-
-
- {
- navigate({
- to: ".",
- search: { modal: "edit-tags", buildId },
- });
- }}
- >
- Edit tags
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/project-environments-table-actions.tsx b/frontend/packages/components/src/actors/project-environments-table-actions.tsx
deleted file mode 100644
index b6722a6274..0000000000
--- a/frontend/packages/components/src/actors/project-environments-table-actions.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import {
- Button,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@rivet-gg/components";
-import { Icon, faEllipsisH } from "@rivet-gg/icons";
-
-export function ProjectEnvironmentsTableActions() {
- return (
-
-
-
-
-
- Manage
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/project-logo-settings-card.tsx b/frontend/packages/components/src/actors/project-logo-settings-card.tsx
deleted file mode 100644
index e4ede8afde..0000000000
--- a/frontend/packages/components/src/actors/project-logo-settings-card.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import * as GroupImageForm from "@/domains/project/forms/project-logo-form";
-import {
- Card,
- CardContent,
- CardFooter,
- CardHeader,
- CardTitle,
-} from "@rivet-gg/components";
-import { useProjectLogoUploadMutation } from "../queries";
-
-interface ProjectLogoSettingsCardProps {
- projectId: string;
-}
-
-export function ProjectLogoSettingsCard({
- projectId,
-}: ProjectLogoSettingsCardProps) {
- const { mutateAsync } = useProjectLogoUploadMutation(projectId);
- return (
- {
- try {
- await mutateAsync({ file: values.logo });
- } catch {
- form.setError("logo", {
- type: "manual",
- message: "An error occurred while uploading the image",
- });
- }
- }}
- defaultValues={{ logo: undefined }}
- >
-
-
- Project Logo
-
-
-
-
-
- Save
-
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/project-select.tsx b/frontend/packages/components/src/actors/project-select.tsx
deleted file mode 100644
index 1438de3313..0000000000
--- a/frontend/packages/components/src/actors/project-select.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import { GroupAvatar } from "@/domains/group/components/group-avatar";
-import { projectsByGroupQueryOptions } from "@/domains/project/queries";
-import {
- Flex,
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectLabel,
- SelectSeparator,
- SelectTrigger,
- SelectValue,
-} from "@rivet-gg/components";
-import { Icon, faCirclePlus } from "@rivet-gg/icons";
-import { useSuspenseQuery } from "@tanstack/react-query";
-import { type ComponentProps, Fragment, useCallback } from "react";
-
-interface ProjectSelectProps extends ComponentProps {
- showCreateProject?: boolean;
- onCreateClick?: () => void;
-}
-
-export function ProjectSelect({
- showCreateProject,
- onCreateClick,
- onValueChange,
- ...props
-}: ProjectSelectProps) {
- const { data } = useSuspenseQuery(projectsByGroupQueryOptions());
-
- const handleValueChange = useCallback(
- (value: string) => {
- if (value === "create") {
- onCreateClick?.();
- return;
- }
- onValueChange?.(value);
- },
- [onCreateClick, onValueChange],
- );
-
- return (
-
- );
-}
diff --git a/frontend/packages/components/src/actors/project-table-actions.tsx b/frontend/packages/components/src/actors/project-table-actions.tsx
deleted file mode 100644
index bd462c5d44..0000000000
--- a/frontend/packages/components/src/actors/project-table-actions.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import {
- Button,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@rivet-gg/components";
-import { Icon, faEllipsisH } from "@rivet-gg/icons";
-
-export function ProjectTableActions() {
- return (
-
-
-
-
-
- Manage
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/project-tile.tsx b/frontend/packages/components/src/actors/project-tile.tsx
deleted file mode 100644
index 0938f22eaa..0000000000
--- a/frontend/packages/components/src/actors/project-tile.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import type { Rivet } from "@rivet-gg/api";
-import { AssetImage, Flex, Text } from "@rivet-gg/components";
-import { BillingPlanBadge } from "./billing/billing-plan-badge";
-
-interface ProjectTileProps
- extends Pick<
- Rivet.game.GameSummary,
- "gameId" | "displayName" | "logoUrl"
- > {}
-
-export function ProjectTile({
- gameId: projectId,
- displayName,
- logoUrl,
-}: ProjectTileProps) {
- return (
-
-
- {displayName}
-
-
- );
-}
diff --git a/frontend/packages/components/src/actors/region-select.tsx b/frontend/packages/components/src/actors/region-select.tsx
deleted file mode 100644
index d88d187105..0000000000
--- a/frontend/packages/components/src/actors/region-select.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Combobox } from "@rivet-gg/components";
-import { useAtomValue } from "jotai";
-import { actorRegionsAtom } from "./actor-context";
-import { ActorRegion } from "./actor-region";
-
-interface RegionSelectProps {
- onValueChange: (value: string) => void;
- value: string;
-}
-
-export function RegionSelect({ onValueChange, value }: RegionSelectProps) {
- const data = useAtomValue(actorRegionsAtom);
-
- const regions = [
- {
- label: "Automatic (Recommended)",
- value: "",
- region: { id: "", name: "Automatic (Recommended)" },
- },
- ...data.map((region) => {
- return {
- label: ,
- value: region.id,
- region,
- };
- }),
- ];
-
- return (
- {
- const search = searchMixed.toLowerCase();
- return (
- option.region.id.includes(search) ||
- option.region.name.includes(search)
- );
- }}
- className="w-full"
- />
- );
-}
diff --git a/frontend/packages/components/src/actors/worker/actor-repl.worker.ts b/frontend/packages/components/src/actors/worker/actor-repl.worker.ts
deleted file mode 100644
index 98753fabf2..0000000000
--- a/frontend/packages/components/src/actors/worker/actor-repl.worker.ts
+++ /dev/null
@@ -1,354 +0,0 @@
-import {
- type InspectData,
- type ToClient,
- ToClientSchema,
- type ToServer,
-} from "actor-core/inspector/protocol/actor";
-import type { Request, ResponseOk } from "actor-core/protocol/http";
-import { fromJs } from "esast-util-from-js";
-import { toJs } from "estree-util-to-js";
-import {
- createHighlighterCore,
- createOnigurumaEngine,
- type HighlighterCore,
-} from "shiki";
-import { endWithSlash } from "../../lib/utils";
-import {
- MessageSchema,
- type ReplErrorCode,
- type Response,
- ResponseSchema,
-} from "./actor-worker-schema";
-
-class ReplError extends Error {
- constructor(
- public readonly code: ReplErrorCode,
- message: string,
- ) {
- super(message);
- }
-
- static unsupported() {
- return new ReplError("unsupported", "Actor unsupported");
- }
-}
-
-export let highlighter: HighlighterCore | undefined;
-
-async function formatCode(code: string) {
- highlighter ??= await createHighlighterCore({
- themes: [import("shiki/themes/github-dark-default.mjs")],
- langs: [import("@shikijs/langs/typescript")],
- engine: createOnigurumaEngine(import("shiki/wasm")),
- });
-
- return highlighter.codeToTokens(code, {
- lang: "typescript",
- theme: "github-dark-default",
- });
-}
-
-const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
-
-async function evaluateCode(code: string, args: Record) {
- const argsString = Object.keys(args);
- const argValues = Object.values(args);
-
- let jsCode: ReturnType;
- try {
- const program = fromJs(code, {
- module: true,
- allowAwaitOutsideFunction: true,
- allowReturnOutsideFunction: true,
- });
-
- const lastStatement = program.body[program.body.length - 1];
- if (lastStatement.type === "ExpressionStatement") {
- program.body[program.body.length - 1] = {
- type: "ReturnStatement",
- argument: lastStatement.expression,
- };
- }
-
- jsCode = toJs(program);
- } catch (e) {
- throw new ReplError("syntax", "Syntax error");
- }
-
- return new Function(
- "window",
- ...argsString,
- `"use strict";
- return (async () => {
- ${jsCode.value}
- })()
- `,
- )({}, ...argValues);
-}
-
-const createConsole = (id: string) => {
- return new Proxy(
- { ...console },
- {
- get(target, prop) {
- return (...args: unknown[]) => {
- respond({
- type: "log",
- id,
- data: {
- method: prop as "log" | "warn" | "error",
- data: args,
- timestamp: new Date().toISOString(),
- },
- });
- return Reflect.get(target, prop)(...args);
- };
- },
- },
- );
-};
-
-let init: null | ({ ws: WebSocket; url: URL } & InspectData) = null;
-
-async function connect(endpoint: string, opts?: { token?: string }) {
- const url = new URL("inspect", endWithSlash(endpoint));
-
- if (opts?.token) {
- url.searchParams.set("token", opts.token);
- }
-
- const ws = new WebSocket(url);
-
- await waitForOpen(ws);
-
- ws.send(
- JSON.stringify({
- type: "info",
- } satisfies ToServer),
- );
-
- const { type: _, ...info } = await waitForMessage(ws, "info");
- init = { ...info, ws, url: new URL(endpoint) };
-
- ws.addEventListener("message", (event) => {
- try {
- const data = ToClientSchema.parse(JSON.parse(event.data));
-
- if (data.type === "info") {
- return respond({
- type: "inspect",
- data: {
- ...data,
- },
- });
- }
- if (data.type === "error") {
- return respond({
- type: "error",
- data: data.message,
- });
- }
- } catch (error) {
- console.warn("Malformed message", event.data, error);
- return;
- }
- });
-
- ws.addEventListener("close", () => {
- respond({
- type: "lost-connection",
- });
- setTimeout(() => {
- connect(endpoint, opts);
- }, 500);
- });
-
- respond({
- type: "ready",
- data: {
- ...info,
- },
- });
-}
-
-addEventListener("message", async (event) => {
- const { success, error, data } = MessageSchema.safeParse(event.data);
-
- if (!success) {
- console.error("Malformed message", event.data, error);
- return;
- }
-
- if (data.type === "init") {
- if (init) {
- respond({
- type: "error",
- data: new Error("Actor already initialized"),
- });
- return;
- }
-
- try {
- await Promise.race([
- connect(data.endpoint, data.token ? { token: data.token } : {}),
- wait(5000).then(() => {
- throw new Error("Timeout");
- }),
- ]);
-
- return;
- } catch (e) {
- return respond({
- type: "error",
- data: e,
- });
- }
- }
-
- if (data.type === "code") {
- const actor = init;
- if (!actor) {
- respond({
- type: "error",
- data: new Error("Actor not initialized"),
- });
- return;
- }
-
- try {
- const formatted = await formatCode(data.data);
- respond({
- type: "formatted",
- id: data.id,
- data: formatted,
- });
-
- const createRpc =
- (rpc: string) =>
- async (...args: unknown[]) => {
- const url = new URL(
- `rpc/${rpc}`,
- endWithSlash(actor.url.href),
- );
- const response = await fetch(url, {
- method: "POST",
- body: JSON.stringify({
- a: args,
- } satisfies Request),
- });
-
- if (!response.ok) {
- throw new Error("RPC failed");
- }
-
- const data = (await response.json()) as ResponseOk;
- return data.o;
- };
-
- const exposedActor = Object.fromEntries(
- init?.rpcs.map((rpc) => [rpc, createRpc(rpc)]) ?? [],
- );
-
- const evaluated = await evaluateCode(data.data, {
- console: createConsole(data.id),
- wait,
- actor: exposedActor,
- });
- return respond({
- type: "result",
- id: data.id,
- data: evaluated,
- });
- } catch (e) {
- return respond({
- type: "error",
- id: data.id,
- data: e,
- });
- }
- }
-
- if (data.type === "set-state") {
- const actor = init;
- if (!actor) {
- respond({
- type: "error",
- data: new Error("Actor not initialized"),
- });
- return;
- }
-
- try {
- const state = JSON.parse(data.data);
- actor.ws.send(
- JSON.stringify({
- type: "setState",
- state,
- } satisfies ToServer),
- );
- } catch (e) {
- return respond({
- type: "error",
- data: e,
- });
- }
- }
-});
-
-function respond(msg: Response) {
- return postMessage(ResponseSchema.parse(msg));
-}
-
-function waitForOpen(ws: WebSocket) {
- const { promise, resolve, reject } = Promise.withResolvers();
- ws.addEventListener("open", () => {
- resolve(undefined);
- });
- ws.addEventListener("error", (event) => {
- reject();
- });
- ws.addEventListener("close", (event) => {
- reject();
- });
-
- return Promise.race([
- promise,
- wait(5000).then(() => {
- throw new Error("Timeout");
- }),
- ]);
-}
-
-function waitForMessage(
- ws: WebSocket,
- type: T,
-): Promise> {
- const { promise, resolve, reject } =
- Promise.withResolvers>();
-
- function onMessage(event: MessageEvent) {
- try {
- const data = ToClientSchema.parse(JSON.parse(event.data));
-
- if (data.type === type) {
- resolve(data as Extract);
- ws.removeEventListener("message", onMessage);
- }
- } catch (e) {
- console.error(e);
- }
- }
-
- ws.addEventListener("message", onMessage);
- ws.addEventListener("error", (event) => {
- ws.removeEventListener("message", onMessage);
- reject();
- });
-
- return Promise.race([
- promise,
- wait(5000).then(() => {
- throw new Error("Timeout");
- }),
- ]);
-}
diff --git a/frontend/packages/components/src/actors/worker/actor-worker-container.ts b/frontend/packages/components/src/actors/worker/actor-worker-container.ts
deleted file mode 100644
index 7352e8e6bc..0000000000
--- a/frontend/packages/components/src/actors/worker/actor-worker-container.ts
+++ /dev/null
@@ -1,330 +0,0 @@
-import { CancelledError } from "@tanstack/react-query";
-import { toast } from "sonner";
-import { ls } from "../../lib/utils";
-import ActorWorker from "./actor-repl.worker?worker";
-import {
- type CodeMessage,
- type FormattedCode,
- type InitMessage,
- type InspectData,
- type Log,
- ResponseSchema,
- type SetStateMessage,
-} from "./actor-worker-schema";
-
-export type ReplCommand = {
- logs: Log[];
- code: string;
- key: string;
- inputTimestamp?: string;
- outputTimestamp?: string;
-} & (
- | { status: "pending" }
- | { status: "formatted"; formatted: FormattedCode }
- | { status: "success"; formatted: FormattedCode; result: unknown }
- | { status: "error"; formatted: FormattedCode | undefined; error: unknown }
-);
-
-export type ContainerStatus =
- | { type: "ready" }
- | { type: "error"; error: unknown }
- | { type: "pending" }
- | { type: "unsupported"; error: unknown }
- | { type: "unknown" };
-
-export type ContainerState = {
- status: ContainerStatus;
- commands: ReplCommand[];
-} & InspectData;
-
-export class ActorWorkerContainer {
- #state: ContainerState = {
- status: { type: "unknown" },
- commands: [],
- rpcs: [],
- state: { enabled: false, value: undefined },
- connections: [],
- };
-
- #meta: {
- actorId: string;
- } | null = null;
-
- #opts: {
- notifyOnReconnect?: boolean;
- } | null = null;
-
- #listeners: (() => void)[] = [];
- #worker: Worker | undefined;
-
- //
- async init({
- actorId,
- endpoint,
- signal,
- notifyOnReconnect,
- }: {
- actorId: string;
- endpoint: string;
- signal: AbortSignal;
- notifyOnReconnect?: boolean;
- }) {
- this.terminate();
-
- this.#meta = { actorId };
- this.#opts = { notifyOnReconnect };
- this.#state.status = { type: "pending" };
- this.#update();
- try {
- signal.throwIfAborted();
-
- // FIXME(RVT-4553)
- // if (actor.resources.cpu !== 125 || actor.resources.memory !== 128) {
- // throw new Error("Unsupported actor resources");
- // }
-
- // If we reached this point, the actor is supported
- // check if we still operate on the same actor
- if (this.#meta.actorId !== actorId) {
- // if not, we don't need to do anything
- return null;
- }
-
- const worker = new ActorWorker({ name: `actor-${actorId}` });
- signal.throwIfAborted();
- // now worker needs to check if the actor is supported
- this.#setupWorker(worker, { actorId, endpoint });
- signal.throwIfAborted();
- return worker;
- } catch (e) {
- console.log(e);
- // If we reached this point, the actor is not supported
- // check if we still operate on the same actor
- if (e instanceof DOMException && e.name === "AbortError") {
- return null;
- }
-
- if (e instanceof CancelledError) {
- this.#worker?.terminate();
- this.#worker = undefined;
- return null;
- }
-
- this.#worker?.terminate();
- this.#worker = undefined;
- this.#state.status = { type: "unsupported", error: e };
- this.#update();
- }
- return null;
- }
-
- terminate() {
- this.#worker?.terminate();
- this.#worker = undefined;
- this.#state.commands = [];
- this.#state.status = { type: "unknown" };
- this.#state.rpcs = [];
- this.#state.state = {
- enabled: false,
- value: undefined,
- };
- this.#meta = null;
- this.#opts = null;
- this.#state.connections = [];
- this.#update();
- }
-
- #setupWorker(worker: Worker, data: Omit) {
- this.#worker = worker;
- this.#worker.addEventListener("message", (event) => {
- try {
- this.#handleMessage(event);
- } catch (e) {
- console.error(e);
- this.#state.status = { type: "error", error: e };
- this.#update();
- }
- });
-
- this.#worker.addEventListener("error", (error) => {
- console.log(error, error.message, error.error);
- });
-
- this.#worker.postMessage({
- type: "init",
- ...data,
- token: ls.get("rivet-token")?.token,
- } satisfies InitMessage);
- }
-
- run(data: string) {
- const key = Date.now().toString();
- this.#state.commands = [
- ...this.#state.commands,
- { status: "pending", code: data, key, logs: [] },
- ];
-
- this.#worker?.postMessage({
- type: "code",
- data,
- id: key,
- } satisfies CodeMessage);
- this.#update();
- }
-
- setState(data: string) {
- this.#worker?.postMessage({
- type: "set-state",
- data,
- } satisfies SetStateMessage);
- this.#state.state = {
- ...this.#state.state,
- value: JSON.parse(data || "{}"),
- };
- this.#update();
- }
-
- getCommands() {
- return this.#state.commands;
- }
-
- getStatus() {
- return this.#state.status;
- }
-
- getRpcs() {
- return this.#state.rpcs;
- }
-
- getState() {
- return this.#state.state;
- }
-
- getConnections() {
- return this.#state.connections;
- }
-
- subscribe(cb: () => void) {
- this.#listeners.push(cb);
- return () => {
- this.#listeners = this.#listeners.filter(
- (listener) => listener !== cb,
- );
- };
- }
-
- #handleMessage(event: MessageEvent) {
- const { success, data: msg } = ResponseSchema.safeParse(event.data);
-
- if (!success) {
- return;
- }
-
- if (msg.type === "formatted") {
- const command = this.#state.commands.find(
- (command) => command.key === msg.id,
- );
- if (command) {
- const newCommand = {
- inputTimestamp: new Date().toISOString(),
- ...command,
- status: "formatted",
- formatted: msg.data,
- } satisfies ReplCommand;
- Object.assign(command, newCommand);
- this.#state.commands = [...this.#state.commands];
- this.#update();
- }
- }
-
- if (msg.type === "result") {
- const command = this.#state.commands.find(
- (command) => command.key === msg.id,
- );
- if (command) {
- const newCommand = {
- outputTimestamp: new Date().toISOString(),
- ...command,
- status: "success",
- result: msg.data,
- };
- Object.assign(command, newCommand);
- this.#state.commands = [...this.#state.commands];
- this.#update();
- }
- }
-
- if (msg.type === "log") {
- const command = this.#state.commands.find(
- (command) => command.key === msg.id,
- );
- if (command) {
- const newCommand = {
- ...command,
- logs: [...command.logs, msg.data],
- };
- Object.assign(command, newCommand);
- this.#state.commands = [...this.#state.commands];
- this.#update();
- }
- }
-
- if (msg.type === "error") {
- if (!msg.id) {
- this.#state.status = { type: "error", error: msg.data };
- console.error("Actor Worker Error", msg.data);
- this.#update();
- return;
- }
-
- const command = this.#state.commands.find(
- (command) => command.key === msg.id,
- );
- if (command) {
- const newCommand = {
- outputTimestamp: new Date().toISOString(),
- ...command,
- status: "error",
- error: msg.data,
- };
- Object.assign(command, newCommand);
- this.#state.commands = [...this.#state.commands];
- this.#update();
- }
- }
-
- if (msg.type === "ready") {
- if (this.#opts?.notifyOnReconnect) {
- toast.success("Connected to Actor", {
- id: "ac-ws-reconnect",
- });
- }
- this.#state.status = { type: "ready" };
- }
-
- if (msg.type === "inspect" || msg.type === "ready") {
- this.#state.rpcs = [...msg.data.rpcs];
- this.#state.state = {
- ...msg.data.state,
- value: msg.data.state.value || {},
- };
- this.#state.connections = [...msg.data.connections];
- this.#update();
- }
-
- if (msg.type === "lost-connection") {
- this.#state.status = { type: "pending" };
-
- if (this.#opts?.notifyOnReconnect) {
- toast.loading("Reconnecting...", { id: "ac-ws-reconnect" });
- }
- this.#update();
- }
- }
-
- #update() {
- for (const listener of this.#listeners) {
- listener();
- }
- }
-}
diff --git a/frontend/packages/components/src/actors/worker/actor-worker-context.tsx b/frontend/packages/components/src/actors/worker/actor-worker-context.tsx
deleted file mode 100644
index 0d731caae6..0000000000
--- a/frontend/packages/components/src/actors/worker/actor-worker-context.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-import { useAtomValue } from "jotai";
-import { selectAtom } from "jotai/utils";
-import {
- type ReactNode,
- createContext,
- useCallback,
- useContext,
- useEffect,
- useState,
- useSyncExternalStore,
-} from "react";
-import { toast } from "sonner";
-import { assertNonNullable } from "../../lib/utils";
-import { type Actor, type ActorAtom, ActorFeature } from "../actor-context";
-import { ActorWorkerContainer } from "./actor-worker-container";
-
-export const ActorWorkerContext = createContext(
- null,
-);
-
-export const useActorWorker = () => {
- const value = useContext(ActorWorkerContext);
- assertNonNullable(value);
- return value;
-};
-
-const selector = (a: Actor) => ({
- actorId: a.id,
- endpoint: a.endpoint,
- enabled:
- !a.destroyedAt &&
- a.endpoint !== null &&
- a.startedAt !== null &&
- a.features?.includes(ActorFeature.Console),
-});
-
-interface ActorWorkerContextProviderProps {
- actor: ActorAtom;
- children: ReactNode;
- notifyOnReconnect?: boolean;
-}
-
-// FIXME: rewrite with jotai
-export const ActorWorkerContextProvider = ({
- children,
- actor,
- notifyOnReconnect,
-}: ActorWorkerContextProviderProps) => {
- const { actorId, endpoint, enabled } = useAtomValue(
- selectAtom(actor, selector),
- );
-
- const [container] = useState(
- () => new ActorWorkerContainer(),
- );
-
- // biome-ignore lint/correctness/useExhaustiveDependencies: we want to create worker on each of those props change
- useEffect(() => {
- const ctrl = new AbortController();
-
- if (enabled && endpoint) {
- container.init({
- actorId,
- endpoint,
- notifyOnReconnect,
- signal: ctrl.signal,
- });
- } else {
- toast.dismiss("ac-ws-reconnect");
- }
-
- return () => {
- ctrl.abort();
- container.terminate();
- };
- }, [actorId, endpoint, enabled]);
-
- return (
-
- {children}
-
- );
-};
-
-export function useActorReplCommands() {
- const container = useActorWorker();
- return useSyncExternalStore(
- useCallback(
- (cb) => {
- return container.subscribe(cb);
- },
- [container],
- ),
- useCallback(() => {
- return container.getCommands();
- }, [container]),
- );
-}
-
-export function useActorWorkerStatus() {
- const container = useActorWorker();
- return useSyncExternalStore(
- useCallback(
- (cb) => {
- return container.subscribe(cb);
- },
- [container],
- ),
- useCallback(() => {
- return container.getStatus();
- }, [container]),
- );
-}
-
-export function useActorRpcs() {
- const container = useActorWorker();
- return useSyncExternalStore(
- useCallback(
- (cb) => {
- return container.subscribe(cb);
- },
- [container],
- ),
- useCallback(() => {
- return container.getRpcs();
- }, [container]),
- );
-}
-
-export function useActorState() {
- const container = useActorWorker();
- return useSyncExternalStore(
- useCallback(
- (cb) => {
- return container.subscribe(cb);
- },
- [container],
- ),
- useCallback(() => {
- return container.getState();
- }, [container]),
- );
-}
-
-export function useActorConnections() {
- const container = useActorWorker();
- return useSyncExternalStore(
- useCallback(
- (cb) => {
- return container.subscribe(cb);
- },
- [container],
- ),
- useCallback(() => {
- return container.getConnections();
- }, [container]),
- );
-}
diff --git a/frontend/packages/components/src/actors/worker/actor-worker-schema.ts b/frontend/packages/components/src/actors/worker/actor-worker-schema.ts
deleted file mode 100644
index 755bb57b59..0000000000
--- a/frontend/packages/components/src/actors/worker/actor-worker-schema.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import { InspectDataSchema } from "actor-core/inspector/protocol/actor";
-import { z } from "zod";
-
-export type ReplErrorCode =
- | "unsupported"
- | "runtime_error"
- | "timeout"
- | "syntax";
-
-const CodeMessageSchema = z.object({
- type: z.literal("code"),
- data: z.string(),
- id: z.string(),
-});
-const InitMessageSchema = z.object({
- type: z.literal("init"),
- endpoint: z.string(),
- actorId: z.string(),
- token: z.string().optional(),
-});
-
-const SetStateMessageSchema = z.object({
- type: z.literal("set-state"),
- data: z.string(),
-});
-
-export const MessageSchema = z.discriminatedUnion("type", [
- CodeMessageSchema,
- InitMessageSchema,
- SetStateMessageSchema,
-]);
-
-export const FormattedCodeSchema = z
- .object({
- fg: z.string().optional(),
- tokens: z.array(
- z.array(
- z.object({
- content: z.string(),
- color: z.string().optional(),
- }),
- ),
- ),
- })
- .catch((ctx) => ctx.input);
-
-export const LogSchema = z.object({
- method: z.union([z.literal("log"), z.literal("warn"), z.literal("error")]),
- data: z.array(z.any()).optional(),
- timestamp: z.string().optional(),
-});
-
-export const ResponseSchema = z.discriminatedUnion("type", [
- z.object({
- type: z.literal("error"),
- id: z.string().optional(),
- data: z.any(),
- }),
- z.object({
- type: z.literal("formatted"),
- id: z.string(),
- data: FormattedCodeSchema,
- }),
- z.object({
- type: z.literal("result"),
- id: z.string(),
- data: z.any().optional(),
- }),
- z.object({
- type: z.literal("log"),
- id: z.string(),
- data: LogSchema,
- }),
- z.object({
- type: z.literal("ready"),
- data: InspectDataSchema,
- }),
- z.object({
- type: z.literal("inspect"),
- data: InspectDataSchema,
- }),
- z.object({
- type: z.literal("lost-connection"),
- }),
-]);
-
-export type Response = z.infer;
-export type Message = z.infer;
-export type FormattedCode = z.infer;
-export type Log = z.infer;
-export type InitMessage = z.infer;
-export type CodeMessage = z.infer;
-export type InspectData = z.infer;
-export type SetStateMessage = z.infer;
diff --git a/frontend/packages/components/src/actors/worker/actor-worker-status.tsx b/frontend/packages/components/src/actors/worker/actor-worker-status.tsx
deleted file mode 100644
index 7838351392..0000000000
--- a/frontend/packages/components/src/actors/worker/actor-worker-status.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Icon, faExclamationTriangle, faSpinner } from "@rivet-gg/icons";
-import { AnimatePresence, motion } from "framer-motion";
-import type { ContainerStatus } from "./actor-worker-container";
-
-interface ActorWorkerStatusProps {
- status: ContainerStatus["type"];
-}
-
-export function ActorWorkerStatus({ status }: ActorWorkerStatusProps) {
- return (
-
- {status === "pending" ? (
-
-
- Connecting to Actor...
-
- ) : null}
- {status === "error" ? (
-
-
- Couldn't connect to Actor.
-
- ) : null}
- {status === "unsupported" ? (
-
-
- Console is not supported for this Actor.
-
- ) : null}
-
- );
-}
diff --git a/frontend/src/app/actors.tsx b/frontend/src/app/actors.tsx
index aae11f8c78..e67302a8dd 100644
--- a/frontend/src/app/actors.tsx
+++ b/frontend/src/app/actors.tsx
@@ -1,7 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import { useNavigate, useSearch } from "@tanstack/react-router";
import {
- ActorFeature,
type ActorId,
ActorNotFound,
ActorsActorDetails,
@@ -13,17 +12,7 @@ import {
export function Actors({ actorId }: { actorId: string | undefined }) {
return (
- {actorId ? (
-
- ) : (
-
- )}
+ {actorId ? : }
);
}
@@ -37,16 +26,7 @@ function Actor() {
);
if (!data || isError) {
- return (
-
- );
+ return ;
}
return (
diff --git a/frontend/src/app/data-providers/default-data-provider.tsx b/frontend/src/app/data-providers/default-data-provider.tsx
index 0b3c0a25f5..34cb877ed3 100644
--- a/frontend/src/app/data-providers/default-data-provider.tsx
+++ b/frontend/src/app/data-providers/default-data-provider.tsx
@@ -1,25 +1,13 @@
+import type { Rivet } from "@rivetkit/engine-api-full";
import {
infiniteQueryOptions,
type MutationOptions,
mutationOptions,
type QueryKey,
queryOptions,
- UseInfiniteQueryOptions,
} from "@tanstack/react-query";
-import type {
- ActorId,
- ActorLogEntry,
- CreateActor as InspectorCreateActor,
-} from "rivetkit/inspector";
import { z } from "zod";
-import {
- type Actor,
- type ActorMetrics,
- type Build,
- type CrashPolicy,
- getActorStatus,
- type Region,
-} from "@/components/actors";
+import { type ActorId, getActorStatus } from "@/components/actors";
import { queryClient } from "@/queries/global";
export const ActorQueryOptionsSchema = z
@@ -53,20 +41,7 @@ export type ActorQueryOptions = z.infer;
export const RECORDS_PER_PAGE = 10;
-type PaginatedResponse = {
- pagination: { cursor?: string };
-} & Record;
-
-type PaginatedActorResponse = PaginatedResponse;
-type PaginatedBuildsResponse = PaginatedResponse;
-type PaginatedRegionsResponse = PaginatedResponse;
-
-type CreateActor = Omit & {
- runnerNameSelector: string;
- key: string;
- crashPolicy: CrashPolicy;
- datacenter?: string;
-};
+type CreateActor = Omit;
const defaultContext = {
endpoint: "",
@@ -82,23 +57,10 @@ const defaultContext = {
refetchInterval: 2000,
queryFn: async () => {
throw new Error("Not implemented");
- return {} as PaginatedActorResponse;
- },
- getNextPageParam: (lastPage) => {
- if (lastPage.pagination.cursor) {
- return lastPage.pagination.cursor;
- }
-
- if (
- !lastPage ||
- lastPage.actors.length === 0 ||
- lastPage.actors.length < RECORDS_PER_PAGE
- ) {
- return undefined;
- }
-
- return lastPage.actors[lastPage.actors.length - 1].id;
+ // biome-ignore lint/correctness/noUnreachable: stub
+ return {} as Rivet.ActorsListResponse;
},
+ getNextPageParam: (lastPage) => lastPage.pagination.cursor,
});
},
@@ -110,13 +72,21 @@ const defaultContext = {
refetchInterval: 2000,
queryFn: async () => {
throw new Error("Not implemented");
- return {} as PaginatedBuildsResponse;
+ // biome-ignore lint/correctness/noUnreachable: stub
+ return {} as Rivet.ActorsListNamesResponse;
},
getNextPageParam: () => {
return undefined;
},
select: (data) => {
- return data.pages.flatMap((page) => page.builds);
+ return data.pages.flatMap((page) =>
+ Array.from(
+ Object.entries(page.names).map(([id, name]) => ({
+ id,
+ name,
+ })),
+ ),
+ );
},
});
},
@@ -126,7 +96,7 @@ const defaultContext = {
...this.buildsQueryOptions(),
select: (data) => {
return data.pages.reduce((acc, page) => {
- return acc + page.builds.length;
+ return acc + Object.keys(page.names).length;
}, 0);
},
});
@@ -139,7 +109,7 @@ const defaultContext = {
refetchInterval: 5000,
select: (data) => {
return data.pages.flatMap((page) =>
- page.actors.map((actor) => actor.id),
+ page.actors.map((actor) => actor.actorId),
);
},
});
@@ -150,7 +120,7 @@ const defaultContext = {
...this.actorsQueryOptions(opts),
select: (data) => {
return data.pages.flatMap((page) =>
- page.actors.map((actor) => actor.id),
+ page.actors.map((actor) => actor.actorId),
).length;
},
});
@@ -160,24 +130,17 @@ const defaultContext = {
actorQueryOptions(actorId: ActorId) {
return queryOptions({
queryFn: async () => {
- return {} as Actor;
+ return {} as Rivet.Actor;
},
queryKey: ["actor", actorId] as QueryKey,
});
},
- actorRegionQueryOptions(actorId: ActorId) {
- return queryOptions({
- ...this.actorQueryOptions(actorId),
- select: (data) => data.region,
- });
- },
-
actorDestroyedAtQueryOptions(actorId: ActorId) {
return queryOptions({
...this.actorQueryOptions(actorId),
select: (data) =>
- data.destroyedAt ? new Date(data.destroyedAt) : null,
+ data.destroyTs ? new Date(data.destroyTs) : null,
});
},
@@ -191,58 +154,39 @@ const defaultContext = {
actorStatusAdditionalInfoQueryOptions(actorId: ActorId) {
return queryOptions({
...this.actorQueryOptions(actorId),
- select: ({ rescheduleAt }) => ({
- rescheduleAt,
+ select: ({ rescheduleTs }) => ({
+ rescheduleTs,
}),
});
},
- actorFeaturesQueryOptions(actorId: ActorId) {
- return queryOptions({
- ...this.actorQueryOptions(actorId),
- select: (data) => data.features ?? [],
- });
- },
-
actorGeneralQueryOptions(actorId: ActorId) {
return queryOptions({
...this.actorQueryOptions(actorId),
select: (data) => ({
- tags: data.tags,
keys: data.key,
- createdAt: data.createdAt ? new Date(data.createdAt) : null,
- destroyedAt: data.destroyedAt
- ? new Date(data.destroyedAt)
- : null,
- connectableAt: data.connectableAt
- ? new Date(data.connectableAt)
+ createTs: data.createTs ? new Date(data.createTs) : null,
+ destroyTs: data.destroyTs ? new Date(data.destroyTs) : null,
+ connectableTs: data.connectableTs
+ ? new Date(data.connectableTs)
: null,
- pendingAllocationAt: data.pendingAllocationAt
- ? new Date(data.pendingAllocationAt)
+ pendingAllocationTs: data.pendingAllocationTs
+ ? new Date(data.pendingAllocationTs)
: null,
- sleepingAt: data.sleepingAt ? new Date(data.sleepingAt) : null,
- region: data.region,
- runner: data.runner,
+ sleepTs: data.sleepTs ? new Date(data.sleepTs) : null,
+ datacenter: data.datacenter,
+ runner: data.runnerNameSelector,
crashPolicy: data.crashPolicy,
}),
});
},
- actorBuildQueryOptions(actorId: ActorId) {
- return queryOptions({
- queryKey: ["actor", actorId, "build"] as QueryKey,
- queryFn: async () => {
- throw new Error("Not implemented");
- return {} as Build;
- },
- enabled: false,
- });
- },
actorMetricsQueryOptions(actorId: ActorId) {
return queryOptions({
queryKey: ["actor", actorId, "metrics"] as QueryKey,
queryFn: async () => {
throw new Error("Not implemented");
- return {} as ActorMetrics;
+ // biome-ignore lint/correctness/noUnreachable: stub
+ return {};
},
enabled: false,
});
@@ -283,81 +227,47 @@ const defaultContext = {
initialPageParam: null as string | null,
queryFn: async () => {
throw new Error("Not implemented");
- return [] as ActorLogEntry[];
+ // biome-ignore lint/correctness/noUnreachable: stub
+ return [];
},
getNextPageParam: () => null,
});
},
- actorNetworkQueryOptions(actorId: ActorId) {
- return queryOptions({
- ...this.actorQueryOptions(actorId),
- select: (data) => data.network,
- });
- },
- actorNetworkPortsQueryOptions(actorId: ActorId) {
- return queryOptions({
- ...this.actorNetworkQueryOptions(actorId),
- select: (data) => data.network?.ports,
- });
- },
- actorRuntimeQueryOptions(actorId: ActorId) {
- return queryOptions({
- ...this.actorQueryOptions(actorId),
- select: ({ runtime, lifecycle, tags }) => ({
- runtime,
- lifecycle,
- tags,
- }),
- });
- },
actorWorkerQueryOptions(actorId: ActorId) {
return queryOptions({
...this.actorQueryOptions(actorId),
select: (data) => ({
- features: data.features ?? [],
name: data.name ?? null,
endpoint: this.endpoint ?? null,
- destroyedAt: data.destroyedAt
- ? new Date(data.destroyedAt)
- : null,
- runner: data.runner ?? undefined,
- sleepingAt: data.sleepingAt ? new Date(data.sleepingAt) : null,
- startedAt: data.startedAt ? new Date(data.startedAt) : null,
+ destroyedAt: data.destroyTs ? new Date(data.destroyTs) : null,
+ runner: data.runnerNameSelector ?? undefined,
+ sleepingAt: data.sleepTs ? new Date(data.sleepTs) : null,
+ startedAt: data.startTs ? new Date(data.startTs) : null,
}),
});
},
// #endregion
- regionsQueryOptions() {
+ datacentersQueryOptions() {
return infiniteQueryOptions({
queryKey: ["actor", "regions"] as QueryKey,
initialPageParam: null as string | null,
queryFn: async () => {
throw new Error("Not implemented");
- return {} as PaginatedRegionsResponse;
+ // biome-ignore lint/correctness/noUnreachable: stub
+ return {} as Rivet.DatacentersListResponse;
},
getNextPageParam: () => null,
- select: (data) => data.pages.flatMap((page) => page.regions),
+ select: (data) => data.pages.flatMap((page) => page.datacenters),
});
},
- regionQueryOptions(regionId: string | undefined) {
+ datacenterQueryOptions(regionId: string | undefined) {
return queryOptions({
queryKey: ["actor", "region", regionId] as QueryKey,
enabled: !!regionId,
queryFn: async () => {
throw new Error("Not implemented");
- return {} as Region;
- },
- });
- },
- statusQueryOptions() {
- return queryOptions({
- queryKey: ["status"] as QueryKey,
- refetchInterval: 1000,
- enabled: false,
- retry: 0,
- queryFn: async () => {
- throw new Error("Not implemented");
- return false as boolean;
+ // biome-ignore lint/correctness/noUnreachable: stub
+ return {} as Rivet.Datacenter;
},
});
},
@@ -366,6 +276,7 @@ const defaultContext = {
mutationKey: ["createActor"] as QueryKey,
mutationFn: async (_: CreateActor) => {
throw new Error("Not implemented");
+ // biome-ignore lint/correctness/noUnreachable: stub
return "";
},
onSuccess: () => {
diff --git a/frontend/src/app/data-providers/engine-data-provider.tsx b/frontend/src/app/data-providers/engine-data-provider.tsx
index 3a651e6fc3..2e8d958c9e 100644
--- a/frontend/src/app/data-providers/engine-data-provider.tsx
+++ b/frontend/src/app/data-providers/engine-data-provider.tsx
@@ -5,16 +5,10 @@ import {
mutationOptions,
type QueryKey,
queryOptions,
- UseQueryOptions,
} from "@tanstack/react-query";
import z from "zod";
import { getConfig, ls } from "@/components";
-import {
- type Actor,
- ActorFeature,
- type ActorId,
- type CrashPolicy,
-} from "@/components/actors";
+import type { ActorId } from "@/components/actors";
import { engineEnv } from "@/lib/env";
import { convertStringToId } from "@/lib/utils";
import { noThrow, shouldRetryAllExpect403 } from "@/queries/utils";
@@ -141,39 +135,17 @@ export const createNamespaceContext = ({
canCreateActors: true,
canDeleteActors: true,
},
- statusQueryOptions() {
- return queryOptions({
- ...def.statusQueryOptions(),
- queryKey: [{ namespace }, ...def.statusQueryOptions().queryKey],
- enabled: true,
- queryFn: async () => {
- return true;
- },
- retry: shouldRetryAllExpect403,
- throwOnError: noThrow,
- meta: {
- mightRequireAuth,
- },
- });
- },
- regionsQueryOptions() {
+ datacentersQueryOptions() {
return infiniteQueryOptions({
- ...def.regionsQueryOptions(),
+ ...def.datacentersQueryOptions(),
enabled: true,
queryKey: [
{ namespace },
- ...def.regionsQueryOptions().queryKey,
+ ...def.datacentersQueryOptions().queryKey,
] as QueryKey,
queryFn: async () => {
const data = await client.datacenters.list();
- return {
- regions: data.datacenters.map((dc) => ({
- id: dc.name,
- name: dc.name,
- url: dc.url,
- })),
- pagination: data.pagination,
- };
+ return data;
},
retry: shouldRetryAllExpect403,
throwOnError: noThrow,
@@ -182,27 +154,27 @@ export const createNamespaceContext = ({
},
});
},
- regionQueryOptions(regionId: string | undefined) {
+ datacenterQueryOptions(name: string | undefined) {
return queryOptions({
- ...def.regionQueryOptions(regionId),
+ ...def.datacenterQueryOptions(name),
queryKey: [
{ namespace },
- ...def.regionQueryOptions(regionId).queryKey,
+ ...def.datacenterQueryOptions(name).queryKey,
],
queryFn: async ({ client }) => {
const regions = await client.ensureInfiniteQueryData(
- this.regionsQueryOptions(),
+ this.datacentersQueryOptions(),
);
for (const page of regions.pages) {
- for (const region of page.regions) {
- if (region.id === regionId) {
+ for (const region of page.datacenters) {
+ if (region.name === name) {
return region;
}
}
}
- throw new Error(`Region not found: ${regionId}`);
+ throw new Error(`Region not found: ${name}`);
},
retry: shouldRetryAllExpect403,
throwOnError: noThrow,
@@ -328,22 +300,9 @@ export const createNamespaceContext = ({
{ abortSignal },
);
- return {
- pagination: data.pagination,
- builds: Object.keys(data.names)
- .sort()
- .map((build) => ({
- id: build,
- name: build,
- })),
- };
- },
- getNextPageParam: (lastPage) => {
- if (lastPage.builds.length < RECORDS_PER_PAGE) {
- return undefined;
- }
- return lastPage.pagination.cursor;
+ return data;
},
+ getNextPageParam: (lastPage) => lastPage.pagination.cursor,
retry: shouldRetryAllExpect403,
throwOnError: noThrow,
meta: {
@@ -679,42 +638,27 @@ function transformActor(a: Rivet.Actor): Actor {
id: a.actorId as ActorId,
name: a.name,
key: a.key ? a.key : undefined,
- connectableAt: a.connectableTs
+ connectableTs: a.connectableTs
? new Date(a.connectableTs).toISOString()
: undefined,
region: a.datacenter,
createdAt: new Date(a.createTs).toISOString(),
- startedAt: a.startTs ? new Date(a.startTs).toISOString() : undefined,
- destroyedAt: a.destroyTs
+ startTs: a.startTs ? new Date(a.startTs).toISOString() : undefined,
+ destroyTs: a.destroyTs
? new Date(a.destroyTs).toISOString()
: undefined,
- sleepingAt: a.sleepTs ? new Date(a.sleepTs).toISOString() : undefined,
- pendingAllocationAt: a.pendingAllocationTs
+ sleepTs: a.sleepTs ? new Date(a.sleepTs).toISOString() : undefined,
+ pendingAllocationTs: a.pendingAllocationTs
? new Date(a.pendingAllocationTs).toISOString()
: undefined,
crashPolicy: a.crashPolicy as CrashPolicy,
runner: a.runnerNameSelector,
- rescheduleAt: a.rescheduleTs
+ rescheduleTs: a.rescheduleTs
? new Date(a.rescheduleTs).toISOString()
: undefined,
- features: [
- ActorFeature.Config,
- ActorFeature.Connections,
- ActorFeature.State,
- ActorFeature.Console,
- ActorFeature.Database,
- ActorFeature.EventsMonitoring,
- ],
};
}
-type RunnerConfig = [
- string,
- {
- datacenters: Record;
- },
-];
-
export function hasMetadataProvider(
metadata: unknown,
): metadata is { provider?: string } {
diff --git a/frontend/src/app/data-providers/inspector-data-provider.tsx b/frontend/src/app/data-providers/inspector-data-provider.tsx
index be6afd2d9b..5853c6ddf7 100644
--- a/frontend/src/app/data-providers/inspector-data-provider.tsx
+++ b/frontend/src/app/data-providers/inspector-data-provider.tsx
@@ -46,9 +46,9 @@ export const createGlobalContext = (opts: { url?: string; token?: string }) => {
},
};
},
- regionsQueryOptions() {
+ datacentersQueryOptions() {
return infiniteQueryOptions({
- ...def.regionsQueryOptions(),
+ ...def.datacentersQueryOptions(),
enabled: true,
queryFn: async () => {
return {
diff --git a/frontend/src/app/dialogs/connect-manual-serverfull-frame.tsx b/frontend/src/app/dialogs/connect-manual-serverfull-frame.tsx
index eb0065ab32..6990b53e95 100644
--- a/frontend/src/app/dialogs/connect-manual-serverfull-frame.tsx
+++ b/frontend/src/app/dialogs/connect-manual-serverfull-frame.tsx
@@ -64,11 +64,11 @@ export default function ConnectManualServerlfullFrameContent({
provider,
}: ConnectManualServerlfullFrameContentProps) {
usePrefetchInfiniteQuery({
- ...useEngineCompatDataProvider().regionsQueryOptions(),
+ ...useEngineCompatDataProvider().datacentersQueryOptions(),
pages: Infinity,
});
const { data } = useSuspenseInfiniteQuery(
- useEngineCompatDataProvider().regionsQueryOptions(),
+ useEngineCompatDataProvider().datacentersQueryOptions(),
);
const prefferedRegionForRailway =
@@ -373,7 +373,9 @@ const useSelectedDatacenter = () => {
const datacenter = useWatch({ name: "datacenter" });
const { data } = useQuery(
- useEngineCompatDataProvider().regionQueryOptions(datacenter || "auto"),
+ useEngineCompatDataProvider().datacenterQueryOptions(
+ datacenter || "auto",
+ ),
);
return data?.url || engineEnv().VITE_APP_API_URL;
diff --git a/frontend/src/app/dialogs/connect-manual-serverless-frame.tsx b/frontend/src/app/dialogs/connect-manual-serverless-frame.tsx
index 71e32cb2f1..611516bd81 100644
--- a/frontend/src/app/dialogs/connect-manual-serverless-frame.tsx
+++ b/frontend/src/app/dialogs/connect-manual-serverless-frame.tsx
@@ -66,12 +66,12 @@ export default function ConnectManualServerlessFrameContent({
onClose,
}: ConnectManualServerlessFrameContentProps) {
usePrefetchInfiniteQuery({
- ...useEngineCompatDataProvider().regionsQueryOptions(),
+ ...useEngineCompatDataProvider().datacentersQueryOptions(),
pages: Infinity,
});
const { data: datacenters } = useSuspenseInfiniteQuery(
- useEngineCompatDataProvider().regionsQueryOptions(),
+ useEngineCompatDataProvider().datacentersQueryOptions(),
);
return ;
diff --git a/frontend/src/app/dialogs/connect-quick-railway-frame.tsx b/frontend/src/app/dialogs/connect-quick-railway-frame.tsx
index 5a3bf6631c..1c8561ebf0 100644
--- a/frontend/src/app/dialogs/connect-quick-railway-frame.tsx
+++ b/frontend/src/app/dialogs/connect-quick-railway-frame.tsx
@@ -52,11 +52,11 @@ export default function ConnectQuickRailwayFrameContent({
onClose,
}: ConnectQuickRailwayFrameContentProps) {
usePrefetchInfiniteQuery({
- ...useEngineCompatDataProvider().regionsQueryOptions(),
+ ...useEngineCompatDataProvider().datacentersQueryOptions(),
pages: Infinity,
});
const { data } = useSuspenseInfiniteQuery(
- useEngineCompatDataProvider().regionsQueryOptions(),
+ useEngineCompatDataProvider().datacentersQueryOptions(),
);
const prefferedRegionForRailway =
diff --git a/frontend/src/app/dialogs/connect-quick-vercel-frame.tsx b/frontend/src/app/dialogs/connect-quick-vercel-frame.tsx
index 2e24a0212e..14e9970ede 100644
--- a/frontend/src/app/dialogs/connect-quick-vercel-frame.tsx
+++ b/frontend/src/app/dialogs/connect-quick-vercel-frame.tsx
@@ -44,12 +44,12 @@ export default function ConnectQuickVercelFrameContent({
onClose,
}: ConnectQuickVercelFrameContentProps) {
usePrefetchInfiniteQuery({
- ...useEngineCompatDataProvider().regionsQueryOptions(),
+ ...useEngineCompatDataProvider().datacentersQueryOptions(),
pages: Infinity,
});
const { data: datacenters } = useSuspenseInfiniteQuery(
- useEngineCompatDataProvider().regionsQueryOptions(),
+ useEngineCompatDataProvider().datacentersQueryOptions(),
);
return (
diff --git a/frontend/src/app/dialogs/connect-railway-frame.tsx b/frontend/src/app/dialogs/connect-railway-frame.tsx
index 6b6581cfa0..0b9cfe14ee 100644
--- a/frontend/src/app/dialogs/connect-railway-frame.tsx
+++ b/frontend/src/app/dialogs/connect-railway-frame.tsx
@@ -66,11 +66,11 @@ export default function ConnectRailwayFrameContent({
onClose,
}: ConnectRailwayFrameContentProps) {
usePrefetchInfiniteQuery({
- ...useEngineCompatDataProvider().regionsQueryOptions(),
+ ...useEngineCompatDataProvider().datacentersQueryOptions(),
pages: Infinity,
});
const { data } = useSuspenseInfiniteQuery(
- useEngineCompatDataProvider().regionsQueryOptions(),
+ useEngineCompatDataProvider().datacentersQueryOptions(),
);
const prefferedRegionForRailway =
@@ -353,7 +353,9 @@ export const useSelectedDatacenter = () => {
const datacenter = useWatch({ name: "datacenter" });
const { data } = useQuery(
- useEngineCompatDataProvider().regionQueryOptions(datacenter || "auto"),
+ useEngineCompatDataProvider().datacenterQueryOptions(
+ datacenter || "auto",
+ ),
);
return data?.url || engineEnv().VITE_APP_API_URL;
diff --git a/frontend/src/app/dialogs/connect-vercel-frame.tsx b/frontend/src/app/dialogs/connect-vercel-frame.tsx
index 1b3e661c0a..e51272dd73 100644
--- a/frontend/src/app/dialogs/connect-vercel-frame.tsx
+++ b/frontend/src/app/dialogs/connect-vercel-frame.tsx
@@ -89,12 +89,12 @@ export default function CreateProjectFrameContent({
onClose,
}: CreateProjectFrameContentProps) {
usePrefetchInfiniteQuery({
- ...useEngineCompatDataProvider().regionsQueryOptions(),
+ ...useEngineCompatDataProvider().datacentersQueryOptions(),
pages: Infinity,
});
const { data: datacenters } = useSuspenseInfiniteQuery(
- useEngineCompatDataProvider().regionsQueryOptions(),
+ useEngineCompatDataProvider().datacentersQueryOptions(),
);
return (
diff --git a/frontend/src/app/forms/connect-manual-serverless-form.tsx b/frontend/src/app/forms/connect-manual-serverless-form.tsx
index 8ed1cdd5e1..217d9c8901 100644
--- a/frontend/src/app/forms/connect-manual-serverless-form.tsx
+++ b/frontend/src/app/forms/connect-manual-serverless-form.tsx
@@ -105,7 +105,7 @@ export const RunnerName = function RunnerName() {
export const Datacenters = function Datacenter() {
const { control } = useFormContext();
const { data, hasNextPage, fetchNextPage } = useInfiniteQuery(
- useEngineCompatDataProvider().regionsQueryOptions(),
+ useEngineCompatDataProvider().datacentersQueryOptions(),
);
return (
diff --git a/frontend/src/components/actors/actor-build.tsx b/frontend/src/components/actors/actor-build.tsx
deleted file mode 100644
index c19adb9ae1..0000000000
--- a/frontend/src/components/actors/actor-build.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { useQuery } from "@tanstack/react-query";
-import { Dd, DiscreteCopyButton, Dl, Dt, Flex } from "@/components";
-import { useDataProvider } from "./data-provider";
-import type { ActorId } from "./queries";
-
-interface ActorBuildProps {
- actorId: ActorId;
-}
-
-export function ActorBuild({ actorId }: ActorBuildProps) {
- const { data } = useQuery(
- useDataProvider().actorBuildQueryOptions(actorId),
- );
-
- if (!data) {
- return null;
- }
-
- return (
-
-
-
Build
-
-
-
- - ID
- -
-
- {data.id}
-
-
-
-
-
- );
-}
diff --git a/frontend/src/components/actors/actor-connections-tab.tsx b/frontend/src/components/actors/actor-connections-tab.tsx
index 36b04f402d..03c60d9799 100644
--- a/frontend/src/components/actors/actor-connections-tab.tsx
+++ b/frontend/src/components/actors/actor-connections-tab.tsx
@@ -1,9 +1,9 @@
import { useQuery } from "@tanstack/react-query";
import { LiveBadge, ScrollArea } from "@/components";
-import { useActor } from "./actor-queries-context";
import { ActorObjectInspector } from "./console/actor-inspector";
import { useDataProvider } from "./data-provider";
-import { type ActorId, useActorConnectionsStream } from "./queries";
+import { useInspector } from "./inspector-context";
+import type { ActorId } from "./queries";
interface ActorConnectionsTabProps {
actorId: ActorId;
@@ -14,12 +14,10 @@ export function ActorConnectionsTab({ actorId }: ActorConnectionsTabProps) {
useDataProvider().actorDestroyedAtQueryOptions(actorId),
);
- const actorQueries = useActor();
- const {
- data: { connections } = {},
- isError,
- isLoading,
- } = useQuery(actorQueries.actorConnectionsQueryOptions(actorId));
+ const inspector = useInspector();
+ const { data: { connections } = {} } = useQuery(
+ inspector.actorConnectionsQueryOptions(actorId),
+ );
// useActorConnectionsStream(actorId);
@@ -31,24 +29,6 @@ export function ActorConnectionsTab({ actorId }: ActorConnectionsTabProps) {
);
}
- if (isError) {
- return (
-
- Connections Preview is currently unavailable.
-
- See console/logs for more details.
-
- );
- }
-
- if (isLoading) {
- return (
-
- Loading connections...
-
- );
- }
-
return (
diff --git a/frontend/src/components/actors/actor-context.tsx b/frontend/src/components/actors/actor-context.tsx
deleted file mode 100644
index d62efbb389..0000000000
--- a/frontend/src/components/actors/actor-context.tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-export {
- createActorInspectorClient,
- createManagerInspectorClient,
-} from "rivetkit/inspector";
diff --git a/frontend/src/components/actors/actor-queries-context.tsx b/frontend/src/components/actors/actor-queries-context.tsx
deleted file mode 100644
index c9ef62ab70..0000000000
--- a/frontend/src/components/actors/actor-queries-context.tsx
+++ /dev/null
@@ -1,270 +0,0 @@
-import { queryOptions } from "@tanstack/react-query";
-import { createContext, useContext } from "react";
-import {
- createActorInspectorClient,
- type RecordedRealtimeEvent,
-} from "rivetkit/inspector";
-import type { ActorId } from "./queries";
-
-type RequestOptions = Parameters
[1];
-
-export const createDefaultActorContext = (
- { hash }: { hash: string } = { hash: `${Date.now()}` },
-) => ({
- createActorInspectorFetchConfiguration: async (
- actorId: ActorId | string,
- opts: { auth?: boolean } = { auth: true },
- ): Promise => ({
- headers: {
- "X-RivetKit-Query": JSON.stringify({
- getForId: { actorId },
- }),
- },
- }),
- createActorInspectorUrl(actorId: ActorId | string) {
- return "http://localhost:6420/registry/actors/inspect";
- },
- async createActorInspector(
- actorId: ActorId | string,
- opts: { auth?: boolean } = { auth: true },
- ) {
- return createActorInspectorClient(
- this.createActorInspectorUrl(actorId),
- await this.createActorInspectorFetchConfiguration(actorId, opts),
- );
- },
- actorPingQueryOptions(
- actorId: ActorId,
- opts: { enabled?: boolean; refetchInterval?: number | false } = {},
- ) {
- return queryOptions({
- enabled: false,
- refetchInterval: 1000,
- ...opts,
- queryKey: [hash, "actor", actorId, "ping"],
- queryFn: async ({ queryKey: [, , actorId] }) => {
- const client = await this.createActorInspector(actorId);
- const response = await client.ping.$get();
- if (!response.ok) {
- throw response;
- }
- return await response.json();
- },
- });
- },
-
- actorStateQueryOptions(
- actorId: ActorId,
- { enabled }: { enabled: boolean } = { enabled: true },
- ) {
- return queryOptions({
- enabled,
- refetchInterval: 1000,
- queryKey: [hash, "actor", actorId, "state"],
- queryFn: async ({ queryKey: [, , actorId] }) => {
- const client = await this.createActorInspector(actorId);
- const response = await client.state.$get();
-
- if (!response.ok) {
- throw response;
- }
- return (await response.json()) as {
- enabled: boolean;
- state: unknown;
- };
- },
- });
- },
-
- actorConnectionsQueryOptions(
- actorId: ActorId,
- { enabled }: { enabled: boolean } = { enabled: true },
- ) {
- return queryOptions({
- enabled,
- refetchInterval: 1000,
- queryKey: [hash, "actor", actorId, "connections"],
- queryFn: async ({ queryKey: [, , actorId] }) => {
- const client = await this.createActorInspector(actorId);
- const response = await client.connections.$get();
-
- if (!response.ok) {
- throw response;
- }
- return await response.json();
- },
- });
- },
-
- actorDatabaseQueryOptions(
- actorId: ActorId,
- { enabled }: { enabled: boolean } = { enabled: true },
- ) {
- return queryOptions({
- enabled,
- queryKey: [hash, "actor", actorId, "database"],
- queryFn: async ({ queryKey: [, , actorId] }) => {
- const client = await this.createActorInspector(actorId);
- const response = await client.db.$get();
-
- if (!response.ok) {
- throw response;
- }
- return await response.json();
- },
- });
- },
-
- actorDatabaseEnabledQueryOptions(
- actorId: ActorId,
- { enabled }: { enabled: boolean } = { enabled: true },
- ) {
- return queryOptions({
- ...this.actorDatabaseQueryOptions(actorId, { enabled }),
- select: (data) => data.enabled,
- notifyOnChangeProps: ["data", "isError", "isLoading"],
- });
- },
-
- actorDatabaseTablesQueryOptions(
- actorId: ActorId,
- { enabled }: { enabled: boolean } = { enabled: true },
- ) {
- return queryOptions({
- ...this.actorDatabaseQueryOptions(actorId, { enabled }),
- select: (data) =>
- data.db?.map((table) => ({
- name: table.table.name,
- type: table.table.type,
- records: table.records,
- })) || [],
- notifyOnChangeProps: ["data", "isError", "isLoading"],
- });
- },
-
- actorDatabaseRowsQueryOptions(
- actorId: ActorId,
- table: string,
- { enabled }: { enabled: boolean } = { enabled: true },
- ) {
- return queryOptions({
- enabled,
- staleTime: 0,
- gcTime: 5000,
- queryKey: [hash, "actor", actorId, "database", table],
- queryFn: async ({ queryKey: [, , actorId, , table] }) => {
- const client = await this.createActorInspector(actorId);
- const response = await client.db.$post({
- json: { query: `SELECT * FROM ${table} LIMIT 500` },
- });
- if (!response.ok) {
- throw response;
- }
- return await response.json();
- },
- });
- },
-
- actorEventsQueryOptions(
- actorId: ActorId,
- { enabled }: { enabled: boolean } = { enabled: true },
- ) {
- return queryOptions({
- enabled,
- refetchInterval: 1000,
- queryKey: [hash, "actor", actorId, "events"],
- queryFn: async ({ queryKey: [, , actorId] }) => {
- const client = await this.createActorInspector(actorId);
- const response = await client.events.$get();
-
- if (!response.ok) {
- throw response;
- }
- return (await response.json()) as {
- events: RecordedRealtimeEvent[];
- };
- },
- });
- },
-
- actorRpcsQueryOptions(
- actorId: ActorId,
- { enabled }: { enabled: boolean } = { enabled: true },
- ) {
- return queryOptions({
- enabled,
- queryKey: [hash, "actor", actorId, "rpcs"],
- queryFn: async ({ queryKey: [, , actorId] }) => {
- const client = await this.createActorInspector(actorId);
- const response = await client.rpcs.$get();
-
- if (!response.ok) {
- throw response;
- }
- return await response.json();
- },
- });
- },
-
- actorClearEventsMutationOptions(actorId: ActorId) {
- return {
- mutationKey: [hash, "actor", actorId, "clear-events"],
- mutationFn: async () => {
- const client = await this.createActorInspector(actorId);
- const response = await client.events.clear.$post();
- if (!response.ok) {
- throw response;
- }
- return await response.json();
- },
- };
- },
-
- actorWakeUpMutationOptions(actorId: ActorId) {
- return {
- mutationKey: [hash, "actor", actorId, "wake-up"],
- mutationFn: async () => {
- const client = await this.createActorInspector(actorId);
- try {
- await client.ping.$get();
- return true;
- } catch {
- return false;
- }
- },
- };
- },
-
- actorAutoWakeUpQueryOptions(
- actorId: ActorId,
- { enabled }: { enabled?: boolean } = {},
- ) {
- return queryOptions({
- enabled,
- refetchInterval: 1000,
- staleTime: 0,
- gcTime: 0,
- queryKey: [hash, "actor", actorId, "auto-wake-up"],
- queryFn: async ({ queryKey: [, , actorId] }) => {
- const client = await this.createActorInspector(actorId, {
- auth: false,
- });
- try {
- await client.ping.$get();
- return true;
- } catch {
- return false;
- }
- },
- retry: false,
- });
- },
-});
-
-export type ActorContext = ReturnType;
-
-const ActorContext = createContext({} as ActorContext);
-
-export const useActor = () => useContext(ActorContext);
-
-export const ActorProvider = ActorContext.Provider;
diff --git a/frontend/src/components/actors/actor-region.tsx b/frontend/src/components/actors/actor-region.tsx
index 9a5f0b9a1c..1921e96a71 100644
--- a/frontend/src/components/actors/actor-region.tsx
+++ b/frontend/src/components/actors/actor-region.tsx
@@ -20,7 +20,7 @@ export function ActorRegion({
className,
}: ActorRegionProps) {
const { data: region } = useQuery(
- useDataProvider().regionQueryOptions(regionId),
+ useDataProvider().datacenterQueryOptions(regionId),
);
if (!regionId || !region) {
diff --git a/frontend/src/components/actors/actor-runtime.tsx b/frontend/src/components/actors/actor-runtime.tsx
deleted file mode 100644
index c3803df811..0000000000
--- a/frontend/src/components/actors/actor-runtime.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Suspense } from "react";
-import { Skeleton } from "../ui/skeleton";
-import { ActorBuild } from "./actor-build";
-import type { ActorId } from "./queries";
-
-export interface ActorRuntimeProps {
- actorId: ActorId;
-}
-
-export function ActorRuntime({ actorId }: ActorRuntimeProps) {
- return (
- }>
-
-
- );
-}
diff --git a/frontend/src/components/actors/actor-status-label.tsx b/frontend/src/components/actors/actor-status-label.tsx
index 2faac42c55..34ed06518a 100644
--- a/frontend/src/components/actors/actor-status-label.tsx
+++ b/frontend/src/components/actors/actor-status-label.tsx
@@ -48,17 +48,17 @@ export function QueriedActorStatusAdditionalInfo({
}: {
actorId: ActorId;
}) {
- const { data: { rescheduleAt } = {} } = useQuery(
+ const { data: { rescheduleTs } = {} } = useQuery(
useDataProvider().actorStatusAdditionalInfoQueryOptions(actorId),
);
- if (rescheduleAt) {
+ if (rescheduleTs) {
return (
Will try to start again{" "}
- (
- {formatISO(rescheduleAt)}){" "}
+ (
+ {formatISO(rescheduleTs)}){" "}
);
diff --git a/frontend/src/components/actors/actors-actor-details.tsx b/frontend/src/components/actors/actors-actor-details.tsx
index 4b782e3dd5..57196a1b28 100644
--- a/frontend/src/components/actors/actors-actor-details.tsx
+++ b/frontend/src/components/actors/actors-actor-details.tsx
@@ -21,12 +21,11 @@ import { QueriedActorStatus } from "./actor-status";
import { ActorStopButton } from "./actor-stop-button";
import { useActorsView } from "./actors-view-context-provider";
import { ActorConsole } from "./console/actor-console";
-import { useDataProvider } from "./data-provider";
import {
GuardConnectableInspector,
useInspectorGuard,
} from "./guard-connectable-inspector";
-import { ActorFeature, type ActorId } from "./queries";
+import type { ActorId } from "./queries";
import { ActorWorkerContextProvider } from "./worker/actor-worker-context";
interface ActorsActorDetailsProps {
@@ -43,24 +42,17 @@ interface ActorsActorDetailsProps {
export const ActorsActorDetails = memo(
({ tab, onTabChange, actorId }: ActorsActorDetailsProps) => {
- const { data: features = [] } = useQuery(
- useDataProvider().actorFeaturesQueryOptions(actorId),
- );
-
- const supportsConsole = features.includes(ActorFeature.Console);
-
return (
- {supportsConsole ?
: null}
+
@@ -80,15 +72,11 @@ function Console({ actorId }: { actorId: ActorId }) {
);
}
-export const ActorsActorEmptyDetails = ({
- features,
-}: {
- features: ActorFeature[];
-}) => {
+export const ActorsActorEmptyDetails = () => {
const { copy } = useActorsView();
return (
-
+
{copy.selectActor}
@@ -100,7 +88,6 @@ export const ActorsActorEmptyDetails = ({
export function ActorTabs({
tab,
- features,
onTabChange,
actorId,
className,
@@ -109,22 +96,12 @@ export function ActorTabs({
}: {
disabled?: boolean;
tab?: string;
- features: ActorFeature[];
onTabChange?: (tab: string) => void;
actorId?: ActorId;
className?: string;
children?: ReactNode;
}) {
- const supportsState = features?.includes(ActorFeature.State);
- const supportsLogs = features?.includes(ActorFeature.Logs);
- const supportsConnections = features?.includes(ActorFeature.Connections);
- const supportsMetadata = features?.includes(ActorFeature.Config);
- const supportsMetrics = features?.includes(ActorFeature.Metrics);
- const supportsEvents = features?.includes(ActorFeature.EventsMonitoring);
- const supportsDatabase = features?.includes(ActorFeature.Database);
-
- const defaultTab = supportsState ? "state" : "logs";
- const value = disabled ? undefined : tab || defaultTab;
+ const value = disabled ? undefined : tab || "state";
const guardContent = useInspectorGuard();
@@ -138,69 +115,59 @@ export function ActorTabs({
- {supportsState ? (
-
- State
-
- ) : null}
- {supportsConnections ? (
-
- Connections
-
- ) : null}
- {supportsEvents ? (
-
- Events
-
- ) : null}
- {supportsDatabase ? (
-
- Database
-
- ) : null}
- {supportsLogs ? (
-
+ State
+
+
+
+ Connections
+
+
+
+ Events
+
+
+
+ Database
+
+
+ {/*
Logs
-
- ) : null}
- {supportsMetadata ? (
-
- Metadata
-
- ) : null}
- {supportsMetrics ? (
- */}
+
+ Metadata
+
+ {/*
Metrics
-
- ) : null}
+ */}
{actorId ? (
{actorId ? (
<>
- {supportsLogs ? (
-
- }>
- {guardContent || (
-
- )}
-
-
- ) : null}
- {supportsMetadata ? (
-
-
-
- ) : null}
- {supportsConnections ? (
-
- {guardContent || (
-
- )}
-
- ) : null}
- {supportsEvents ? (
-
- {guardContent || (
-
- )}
-
- ) : null}
- {supportsDatabase ? (
-
- {guardContent || (
-
- )}
-
- ) : null}
- {supportsState ? (
-
- {guardContent || (
-
- )}
-
- ) : null}
- {supportsMetrics ? (
+
+ }>
+ {guardContent || }
+
+
+
+
+
+
+ {guardContent || (
+
+ )}
+
+
+
+ {guardContent || }
+
+
+ {guardContent || }
+
+
+ {guardContent || }
+
+ {/* {supportsMetrics ? (
)}
- ) : null}
+ ) : null} */}
>
) : null}
{children}
diff --git a/frontend/src/components/actors/index.ts b/frontend/src/components/actors/index.ts
index bdf87a9973..4f39184f87 100644
--- a/frontend/src/components/actors/index.ts
+++ b/frontend/src/components/actors/index.ts
@@ -1,6 +1,4 @@
-export * from "./actor-context";
export * from "./actor-not-found";
-export * from "./actor-queries-context";
export * from "./actor-region";
export * from "./actor-status-indicator";
export * from "./actor-status-label";
diff --git a/frontend/src/components/actors/inspector-context.tsx b/frontend/src/components/actors/inspector-context.tsx
new file mode 100644
index 0000000000..fe00180040
--- /dev/null
+++ b/frontend/src/components/actors/inspector-context.tsx
@@ -0,0 +1 @@
+export const useInspector = () => {};
diff --git a/frontend/src/components/actors/queries/actor.ts b/frontend/src/components/actors/queries/actor.ts
deleted file mode 100644
index d372d9a996..0000000000
--- a/frontend/src/components/actors/queries/actor.ts
+++ /dev/null
@@ -1,226 +0,0 @@
-import { fetchEventSource } from "@microsoft/fetch-event-source";
-import { useMutation, useQueryClient } from "@tanstack/react-query";
-import { compare } from "fast-json-patch";
-import { useCallback, useEffect } from "react";
-import type { ActorId, RecordedRealtimeEvent } from "rivetkit/inspector";
-import { useAsyncMemo } from "@/components/hooks/use-async-memo";
-import { useActor } from "../actor-queries-context";
-
-export const useActorClearEventsMutation = (
- actorId: ActorId,
- options?: Parameters[1],
-) => {
- const queryClient = useQueryClient();
- const queries = useActor();
- return useMutation({
- ...queries.actorClearEventsMutationOptions(actorId),
- onMutate: async () => {
- queryClient.setQueryData(
- queries.actorEventsQueryOptions(actorId).queryKey,
- () => ({ events: [] }),
- );
- },
- ...options,
- });
-};
-
-export const useActorStatePatchMutation = (
- actorId: ActorId,
- options?: Parameters[1],
-) => {
- const queryClient = useQueryClient();
- const queries = useActor();
- return useMutation({
- mutationFn: async (data: any) => {
- const client = await queries.createActorInspector(actorId);
-
- const oldStateQuery = queryClient.getQueryData(
- queries.actorStateQueryOptions(actorId).queryKey,
- );
-
- const oldState = oldStateQuery?.state;
-
- let response: Awaited>;
-
- if (!oldState || !isPatchable(data)) {
- response = await client.state.$patch({
- // its okay, we know the type
- json: { replace: data },
- });
- } else {
- const patches = compare(oldState, data);
- response = await client.state.$patch({
- // its okay, we know the type
- // @ts-expect-error
- json: { patch: patches },
- });
- }
-
- if (!response.ok) {
- throw response;
- }
- return (await response.json()) as {
- state: unknown;
- enabled: boolean;
- };
- },
- onSuccess: (data) => {
- queryClient.setQueryData(
- queries.actorStateQueryOptions(actorId).queryKey,
- () => ({ enabled: true, state: data.state }),
- );
- },
- ...options,
- });
-};
-
-const getHeaders = (
- v:
- | Record
- | (() => Record | Promise>),
-) => {
- if (typeof v === "function") {
- return v();
- }
- return v;
-};
-
-function useStream(
- actorId: ActorId,
- onMessage: (data: T) => void,
- url: string | null | undefined,
- opts: { enabled: boolean } = { enabled: true },
-) {
- const stableOnMessage = useCallback(onMessage, []);
- const queries = useActor();
-
- useEffect(() => {
- const controller = new AbortController();
- if (!opts.enabled || !url) {
- controller.abort();
- return () => controller.abort();
- }
-
- async function establishConnection() {
- if (!url) {
- return;
- }
- fetchEventSource(url, {
- signal: controller.signal,
- headers: await getHeaders(
- (
- await queries.createActorInspectorFetchConfiguration(
- actorId,
- )
- )?.headers || {},
- ),
- onmessage: (event) => {
- const msg = JSON.parse(event.data);
- stableOnMessage(msg);
- },
- onclose: async () => {
- await new Promise((resolve) => setTimeout(resolve, 1000));
- controller.signal.throwIfAborted();
- establishConnection();
- },
- }).catch((error) => console.error(error));
- }
-
- establishConnection();
- return () => {
- controller.abort();
- };
- }, [url, actorId, opts.enabled, stableOnMessage]);
-}
-
-export const useActorStateStream = (
- actorId: ActorId,
- opts: { enabled: boolean } = { enabled: true },
-) => {
- const queryClient = useQueryClient();
- const queries = useActor();
-
- useStream(
- actorId,
- useCallback(
- (data: unknown) => {
- queryClient.setQueryData(
- queries.actorStateQueryOptions(actorId).queryKey,
- () => ({ enabled: true, state: data }),
- );
- },
- [queryClient, actorId, queries],
- ),
- useAsyncMemo(
- async () =>
- (
- await queries.createActorInspector(actorId)
- ).state.stream.$url().href,
- [actorId, queries],
- ),
- opts,
- );
-};
-
-export const useActorConnectionsStream = (actorId: ActorId) => {
- const queryClient = useQueryClient();
- const queries = useActor();
-
- useStream(
- actorId,
- useCallback(
- (data) => {
- queryClient.setQueryData(
- queries.actorConnectionsQueryOptions(actorId).queryKey,
- () => ({ enabled: true, connections: data }),
- );
- },
- [queryClient, actorId, queries],
- ),
- useAsyncMemo(
- async () =>
- (
- await queries.createActorInspector(actorId)
- ).connections.stream.$url().href,
- [actorId, queries],
- ),
- );
-};
-
-export const useActorEventsStream = (
- actorId: ActorId,
- opts: { enabled: boolean },
-) => {
- const queryClient = useQueryClient();
- const queries = useActor();
-
- useStream(
- actorId,
- useCallback(
- (data: RecordedRealtimeEvent[]) => {
- queryClient.setQueryData(
- queries.actorEventsQueryOptions(actorId).queryKey,
- () => {
- return { events: data };
- },
- );
- },
- [queryClient, actorId, queries],
- ),
- useAsyncMemo(
- async () =>
- (
- await queries.createActorInspector(actorId)
- ).events.stream.$url().href,
- [actorId, queries],
- ),
- opts,
- );
-};
-
-/**
- * Check if the object is patchable, i.e. if it is an object and not null.
- */
-function isPatchable(data: unknown) {
- return typeof data === "object" && data !== null;
-}
diff --git a/frontend/src/components/actors/queries/index.ts b/frontend/src/components/actors/queries/index.ts
index 383c469228..47f1d4f6df 100644
--- a/frontend/src/components/actors/queries/index.ts
+++ b/frontend/src/components/actors/queries/index.ts
@@ -1,95 +1,6 @@
-import type { Actor as InspectorActor } from "rivetkit/inspector";
+import type { Rivet } from "@rivetkit/engine-api-full";
-export type { ActorLogEntry } from "rivetkit/inspector";
-export { ActorFeature } from "rivetkit/inspector";
-
-import type { ActorId } from "rivetkit/inspector";
-
-export type { ActorId };
-
-export type PortRouting = {
- guard?: {};
- host?: {};
-};
-
-export type Port = {
- protocol: "http" | "https" | "tcp" | "tcp_tls" | "udp";
- internalPort?: number;
- hostname?: string;
- port?: number;
- path?: string;
- /** Fully formed connection URL including protocol, hostname, port, and path, if applicable. */
- url?: string;
- routing: PortRouting;
-};
-
-export type Runtime = {
- build: string;
- arguments?: string[];
- environment?: Record;
-};
-
-export type Lifecycle = {
- /** The duration to wait for in milliseconds before killing the actor. This should be set to a safe default, and can be overridden during a DELETE request if needed. */
- killTimeout?: number;
- /** If true, the actor will try to reschedule itself automatically in the event of a crash or a datacenter failover. The actor will not reschedule if it exits successfully. */
- durable?: boolean;
-};
-
-export type Resources = {
- /**
- * The number of CPU cores in millicores, or 1/1000 of a core. For example,
- * 1/8 of a core would be 125 millicores, and 1 core would be 1000
- * millicores.
- */
- cpu: number;
- /** The amount of memory in megabytes */
- memory: number;
-};
-
-export type Actor = Omit & {
- network?: {
- mode: "bridge" | "host";
- ports: Record;
- };
- runtime?: Runtime;
- lifecycle?: Lifecycle;
- key: string | undefined;
-
- // engine related
- runner?: string;
- crashPolicy?: CrashPolicy;
- sleepingAt?: string | null;
- connectableAt?: string | null;
- pendingAllocationAt?: string | null;
- datacenter?: string | null;
- rescheduleAt?: string | null;
-} & { id: ActorId };
-
-export enum CrashPolicy {
- Restart = "restart",
- Sleep = "sleep",
- Destroy = "destroy",
-}
-
-export type ActorMetrics = {
- metrics: Record;
- rawData: Record;
- interval: number;
-};
-
-export type Build = {
- id: string;
- name: string;
-};
-
-export type Region = {
- id: string;
- name: string;
- url?: string;
-};
-
-export * from "./actor";
+export type ActorId = string;
export type ActorStatus =
| "starting"
@@ -103,49 +14,49 @@ export type ActorStatus =
export function getActorStatus(
actor: Pick<
- Actor,
- | "createdAt"
- | "startedAt"
- | "destroyedAt"
- | "sleepingAt"
- | "pendingAllocationAt"
- | "rescheduleAt"
+ Rivet.Actor,
+ | "createTs"
+ | "destroyTs"
+ | "sleepTs"
+ | "pendingAllocationTs"
+ | "rescheduleTs"
+ | "connectableTs"
>,
): ActorStatus {
const {
- createdAt,
- startedAt,
- destroyedAt,
- sleepingAt,
- pendingAllocationAt,
- rescheduleAt,
+ createTs,
+ connectableTs,
+ destroyTs,
+ sleepTs,
+ pendingAllocationTs,
+ rescheduleTs,
} = actor;
- if (rescheduleAt) {
+ if (rescheduleTs) {
return "crash-loop";
}
- if (pendingAllocationAt && !startedAt && !destroyedAt) {
+ if (pendingAllocationTs && !connectableTs && !destroyTs) {
return "pending";
}
- if (createdAt && sleepingAt && !destroyedAt) {
+ if (createTs && sleepTs && !destroyTs) {
return "sleeping";
}
- if (createdAt && !startedAt && !destroyedAt) {
+ if (createTs && !connectableTs && !destroyTs) {
return "starting";
}
- if (createdAt && startedAt && !destroyedAt) {
+ if (createTs && connectableTs && !destroyTs) {
return "running";
}
- if (createdAt && startedAt && destroyedAt) {
+ if (createTs && connectableTs && destroyTs) {
return "stopped";
}
- if (createdAt && !startedAt && destroyedAt) {
+ if (createTs && !connectableTs && destroyTs) {
return "crashed";
}
diff --git a/frontend/src/components/actors/region-select.tsx b/frontend/src/components/actors/region-select.tsx
index bb8dee1831..de0a6742e5 100644
--- a/frontend/src/components/actors/region-select.tsx
+++ b/frontend/src/components/actors/region-select.tsx
@@ -19,7 +19,7 @@ export function RegionSelect({
fetchNextPage,
isLoading,
isFetchingNextPage,
- } = useInfiniteQuery(useDataProvider().regionsQueryOptions());
+ } = useInfiniteQuery(useDataProvider().datacentersQueryOptions());
const regions = [
...(showAuto
diff --git a/frontend/src/components/actors/worker/actor-worker-context.tsx b/frontend/src/components/actors/worker/actor-worker-context.tsx
index 4288f49c8a..2bd88690c3 100644
--- a/frontend/src/components/actors/worker/actor-worker-context.tsx
+++ b/frontend/src/components/actors/worker/actor-worker-context.tsx
@@ -1,3 +1,4 @@
+/** biome-ignore-all lint/correctness/useHookAtTopLevel: guarded by build constant */
import { useQuery } from "@tanstack/react-query";
import {
createContext,
@@ -13,7 +14,7 @@ import { useInspectorCredentials } from "@/app/credentials-context";
import { assertNonNullable, ls } from "../../lib/utils";
import { useActor } from "../actor-queries-context";
import { useDataProvider, useEngineCompatDataProvider } from "../data-provider";
-import { ActorFeature, type ActorId } from "../queries";
+import type { ActorId } from "../queries";
import { ActorWorkerContainer } from "./actor-worker-container";
export const ActorWorkerContext = createContext(
@@ -70,7 +71,6 @@ export const ActorWorkerContextProvider = ({
const {
data: {
- features,
name,
endpoint,
destroyedAt,
@@ -81,12 +81,7 @@ export const ActorWorkerContextProvider = ({
} = useQuery(dataProvider.actorWorkerQueryOptions(actorId));
const inspectorToken = useInspectorToken(runner || "");
- const enabled =
- (features?.includes(ActorFeature.Console) &&
- !destroyedAt &&
- !sleepingAt &&
- !!startedAt) ??
- false;
+ const enabled = (!destroyedAt && !sleepingAt && !!startedAt) ?? false;
const actorQueries = useActor();
const { data: { rpcs } = {} } = useQuery(
diff --git a/frontend/src/queries/actor-inspector.ts b/frontend/src/queries/actor-inspector.ts
index f37c61ba8a..f3d36e81fd 100644
--- a/frontend/src/queries/actor-inspector.ts
+++ b/frontend/src/queries/actor-inspector.ts
@@ -1,7 +1,3 @@
-import {
- type ActorContext,
- createDefaultActorContext,
-} from "@/components/actors";
import { ensureTrailingSlash } from "@/lib/utils";
export const createInspectorActorContext = ({
@@ -13,37 +9,39 @@ export const createInspectorActorContext = ({
token: string | (() => string) | (() => Promise);
engineToken?: string;
}) => {
- const def = createDefaultActorContext({
- hash: btoa(url + inspectorToken + (engineToken || "")).slice(0, 8),
- });
- const newUrl = new URL(url);
- if (!newUrl.pathname.endsWith("inspect")) {
- newUrl.pathname = `${ensureTrailingSlash(newUrl.pathname)}inspect`;
- }
- return {
- ...def,
- async createActorInspectorFetchConfiguration(actorId, opts) {
- return {
- headers: {
- "x-rivet-actor": actorId,
- "x-rivet-target": "actor",
- ...(engineToken ? { "x-rivet-token": engineToken } : {}),
- ...(opts?.auth
- ? {
- ...{
- authorization: `Bearer ${
- typeof inspectorToken === "string"
- ? inspectorToken
- : await inspectorToken()
- }`,
- },
- }
- : {}),
- },
- };
- },
- createActorInspectorUrl() {
- return new URL(`${url}/inspect`, window.location.origin).href;
- },
- } satisfies ActorContext;
+ // const def = createDefaultActorContext({
+ // hash: btoa(url + inspectorToken + (engineToken || "")).slice(0, 8),
+ // });
+ // const newUrl = new URL(url);
+ // if (!newUrl.pathname.endsWith("inspect")) {
+ // newUrl.pathname = `${ensureTrailingSlash(newUrl.pathname)}inspect`;
+ // }
+ // return {
+ // ...def,
+ // async createActorInspectorFetchConfiguration(actorId, opts) {
+ // return {
+ // headers: {
+ // "x-rivet-actor": actorId,
+ // "x-rivet-target": "actor",
+ // ...(engineToken ? { "x-rivet-token": engineToken } : {}),
+ // ...(opts?.auth
+ // ? {
+ // ...{
+ // authorization: `Bearer ${
+ // typeof inspectorToken === "string"
+ // ? inspectorToken
+ // : await inspectorToken()
+ // }`,
+ // },
+ // }
+ // : {}),
+ // },
+ // };
+ // },
+ // createActorInspectorUrl() {
+ // return new URL(`${url}/inspect`, window.location.origin).href;
+ // },
+ // } satisfies ActorContext;
+
+ return {};
};
diff --git a/frontend/src/utils/use-railway-template-link.ts b/frontend/src/utils/use-railway-template-link.ts
index 64e2bcc1b3..c652b5dc4a 100644
--- a/frontend/src/utils/use-railway-template-link.ts
+++ b/frontend/src/utils/use-railway-template-link.ts
@@ -24,7 +24,7 @@ export function useRailwayTemplateLink({
const useDatacenterEndpoint = ({ datacenter }: { datacenter: string }) => {
const { data } = useQuery(
- useEngineCompatDataProvider().regionQueryOptions(datacenter),
+ useEngineCompatDataProvider().datacenterQueryOptions(datacenter),
);
return data?.url || engineEnv().VITE_APP_API_URL;
};
diff --git a/rivetkit-openapi/openapi.json b/rivetkit-openapi/openapi.json
index 6f7096cccc..aab9828926 100644
--- a/rivetkit-openapi/openapi.json
+++ b/rivetkit-openapi/openapi.json
@@ -331,6 +331,60 @@
}
}
}
+ },
+ "/actors/names": {
+ "get": {
+ "parameters": [
+ {
+ "schema": {
+ "type": "string"
+ },
+ "required": true,
+ "name": "namespace",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "names": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "metadata": {
+ "type": "object",
+ "additionalProperties": {
+ "nullable": true
+ }
+ }
+ },
+ "required": [
+ "metadata"
+ ]
+ }
+ }
+ },
+ "required": [
+ "names"
+ ]
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "User error"
+ },
+ "500": {
+ "description": "Internal error"
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts b/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts
index cfdf78ebbe..82da009a3b 100644
--- a/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts
+++ b/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts
@@ -388,8 +388,4 @@ export class CloudflareActorsManagerDriver implements ManagerDriver {
properties: {},
};
}
-
- getOrCreateInspectorAccessToken() {
- return generateRandomString();
- }
}
diff --git a/rivetkit-typescript/packages/rivetkit/package.json b/rivetkit-typescript/packages/rivetkit/package.json
index d9548edc7e..d69c300e19 100644
--- a/rivetkit-typescript/packages/rivetkit/package.json
+++ b/rivetkit-typescript/packages/rivetkit/package.json
@@ -153,7 +153,7 @@
],
"scripts": {
"build": "tsup src/mod.ts src/client/mod.ts src/common/log.ts src/common/websocket.ts src/actor/errors.ts src/topologies/coordinate/mod.ts src/topologies/partition/mod.ts src/utils.ts src/driver-helpers/mod.ts src/driver-test-suite/mod.ts src/test/mod.ts src/inspector/mod.ts",
- "build:schema": "./scripts/compile-bare.ts compile schemas/client-protocol/v1.bare -o dist/schemas/client-protocol/v1.ts && ./scripts/compile-bare.ts compile schemas/client-protocol/v2.bare -o dist/schemas/client-protocol/v2.ts && ./scripts/compile-bare.ts compile schemas/file-system-driver/v1.bare -o dist/schemas/file-system-driver/v1.ts && ./scripts/compile-bare.ts compile schemas/file-system-driver/v2.bare -o dist/schemas/file-system-driver/v2.ts && ./scripts/compile-bare.ts compile schemas/actor-persist/v1.bare -o dist/schemas/actor-persist/v1.ts && ./scripts/compile-bare.ts compile schemas/actor-persist/v2.bare -o dist/schemas/actor-persist/v2.ts && ./scripts/compile-bare.ts compile schemas/actor-persist/v3.bare -o dist/schemas/actor-persist/v3.ts",
+ "build:schema": "./scripts/compile-bare.ts compile schemas/client-protocol/v1.bare -o dist/schemas/client-protocol/v1.ts && ./scripts/compile-bare.ts compile schemas/client-protocol/v2.bare -o dist/schemas/client-protocol/v2.ts && ./scripts/compile-bare.ts compile schemas/file-system-driver/v1.bare -o dist/schemas/file-system-driver/v1.ts && ./scripts/compile-bare.ts compile schemas/file-system-driver/v2.bare -o dist/schemas/file-system-driver/v2.ts && ./scripts/compile-bare.ts compile schemas/actor-persist/v1.bare -o dist/schemas/actor-persist/v1.ts && ./scripts/compile-bare.ts compile schemas/actor-persist/v2.bare -o dist/schemas/actor-persist/v2.ts && ./scripts/compile-bare.ts compile schemas/actor-persist/v3.bare -o dist/schemas/actor-persist/v3.ts && ./scripts/compile-bare.ts compile schemas/actor-inspector/v1.bare -o dist/schemas/actor-inspector/v1.ts",
"check-types": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest",
diff --git a/rivetkit-typescript/packages/rivetkit/schemas/actor-inspector/v1.bare b/rivetkit-typescript/packages/rivetkit/schemas/actor-inspector/v1.bare
new file mode 100644
index 0000000000..c10adcc579
--- /dev/null
+++ b/rivetkit-typescript/packages/rivetkit/schemas/actor-inspector/v1.bare
@@ -0,0 +1,183 @@
+# MARK: Message To Server
+
+type OpTest struct {
+ path: str
+ value: data
+}
+
+type OpCopy struct {
+ path: str
+ from: str
+}
+
+type OpMove struct {
+ path: str
+ from: str
+}
+
+type OpReplace struct {
+ path: str
+ value: data
+}
+
+type OpAdd struct {
+ path: str
+ value: data
+}
+
+type OpRemove struct {
+ path: str
+}
+
+type PatchOperation union {
+ OpRemove |
+ OpAdd |
+ OpReplace |
+ OpMove |
+ OpCopy |
+ OpTest
+}
+
+type PatchStateRequest struct {
+ operations: list
+}
+
+type ClearStateRequest void
+
+type DatabaseQueryRequest struct {
+ id: uint
+ query: str
+ params: data
+}
+
+type ActionRequest struct {
+ id: uint
+ name: str
+ args: data
+}
+
+type ToServerBody union {
+ PatchStateRequest |
+ ClearStateRequest |
+ DatabaseQueryRequest |
+ ActionRequest
+}
+
+type ToServer struct {
+ body: ToServerBody
+}
+
+# MARK: Message To Client
+
+type State data
+
+type Connection struct {
+ id: str
+ details: data
+}
+
+type ActionEvent struct {
+ name: str
+ args: data
+ connId: str
+}
+
+type BroadcastEvent struct {
+ eventName: str
+ args: data
+}
+
+type SubscribeEvent struct {
+ eventName: str
+ connId: str
+}
+
+type UnSubscribeEvent struct {
+ eventName: str
+ connId: str
+}
+
+type FiredEvent struct {
+ eventName: str
+ args: data
+ connId: str
+}
+
+type EventBody union {
+ ActionEvent |
+ BroadcastEvent |
+ SubscribeEvent |
+ UnSubscribeEvent |
+ FiredEvent
+}
+
+type Event struct {
+ id: str
+ timestamp: uint
+ body: EventBody
+}
+
+type Init struct {
+ connections: list
+ events: list
+ state: State
+ rpcs: list
+}
+
+type ConnectionsResponse struct {
+ connections: list
+}
+
+type StateResponse struct {
+ state: State
+}
+
+type DatabaseColumn struct {
+ name: str
+ type: str
+}
+
+type DatabaseForeignKey struct {
+ id: str
+ table: str
+ from: str
+ to: str
+}
+
+type DatabaseTable struct {
+ name: str
+ schema: data
+ type: str
+ columns: list
+ foreignKeys: list
+ records: uint
+}
+
+type DatabaseResponse struct {
+ tables: list
+}
+
+type DatabaseQueryResponse data
+
+type ActionResponse struct {
+ id: uint
+ output: data
+}
+
+type Error struct {
+ message: str
+}
+
+type ToClientBody union {
+ StateResponse |
+ ConnectionsResponse |
+ DatabaseResponse |
+ DatabaseQueryResponse |
+ ActionResponse |
+ Error |
+ Init
+}
+
+type ToClient struct {
+ body: ToClientBody
+}
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts b/rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts
index e60a09c436..d1e708cdf7 100644
--- a/rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts
+++ b/rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts
@@ -38,7 +38,6 @@ function main() {
proxyRequest: unimplemented,
proxyWebSocket: unimplemented,
displayInformation: unimplemented,
- getOrCreateInspectorAccessToken: unimplemented,
};
const client = createClientWithDriver(
diff --git a/rivetkit-typescript/packages/rivetkit/src/actor/instance/mod.ts b/rivetkit-typescript/packages/rivetkit/src/actor/instance/mod.ts
index ccadfcfa38..4aadbe4309 100644
--- a/rivetkit-typescript/packages/rivetkit/src/actor/instance/mod.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/actor/instance/mod.ts
@@ -142,22 +142,25 @@ export class ActorInstance {
return Array.from(
this.#connectionManager.connections.entries(),
).map(([id, conn]) => ({
- type: conn[CONN_DRIVER_SYMBOL]?.type,
id,
- params: conn.params as any,
- state: conn[CONN_STATE_ENABLED_SYMBOL]
- ? conn.state
- : undefined,
- subscriptions: conn.subscriptions.size,
- lastSeen: conn.lastSeen,
- stateEnabled: conn[CONN_STATE_ENABLED_SYMBOL],
- isHibernatable: conn.isHibernatable,
- hibernatableRequestId: conn[CONN_PERSIST_SYMBOL]
- .hibernatableRequestId
- ? idToStr(
- conn[CONN_PERSIST_SYMBOL].hibernatableRequestId,
- )
- : undefined,
+ details: {
+ type: conn[CONN_DRIVER_SYMBOL]?.type || null,
+ params: conn.params as any,
+ state: conn[CONN_STATE_ENABLED_SYMBOL]
+ ? conn.state
+ : null,
+ subscriptions: conn.subscriptions.size,
+ lastSeen: conn.lastSeen,
+ stateEnabled: conn[CONN_STATE_ENABLED_SYMBOL],
+ isHibernatable: conn.isHibernatable,
+ hibernatableRequestId: conn[CONN_PERSIST_SYMBOL]
+ .hibernatableRequestId
+ ? idToStr(
+ conn[CONN_PERSIST_SYMBOL]
+ .hibernatableRequestId,
+ )
+ : null,
+ },
}));
},
setState: async (state: unknown) => {
diff --git a/rivetkit-typescript/packages/rivetkit/src/actor/router.ts b/rivetkit-typescript/packages/rivetkit/src/actor/router.ts
index d56c980b30..5fffa74a50 100644
--- a/rivetkit-typescript/packages/rivetkit/src/actor/router.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/actor/router.ts
@@ -1,5 +1,4 @@
import { Hono } from "hono";
-import invariant from "invariant";
import {
type ActionOpts,
type ActionOutput,
@@ -22,11 +21,7 @@ import {
loggerMiddleware,
} from "@/common/router";
import { noopNext } from "@/common/utils";
-import {
- type ActorInspectorRouterEnv,
- createActorInspectorRouter,
-} from "@/inspector/actor";
-import { isInspectorEnabled, secureInspector } from "@/inspector/utils";
+import { createActorInspectorRouter } from "@/inspector/actor";
import type { RunnerConfig } from "@/registry/run-config";
import { CONN_DRIVER_SYMBOL, generateConnRequestId } from "./conn/mod";
import type { ActorDriver } from "./driver";
@@ -199,25 +194,13 @@ export function createActorRouter(
}
});
- if (isInspectorEnabled(runConfig, "actor")) {
- router.route(
+ if (runConfig.inspector.enabled) {
+ router.use(
"/inspect",
- new Hono<
- ActorInspectorRouterEnv & { Bindings: ActorRouterBindings }
- >()
- .use(secureInspector(runConfig), async (c, next) => {
- const inspector = (
- await actorDriver.loadActor(c.env.actorId)
- ).inspector;
- invariant(
- inspector,
- "inspector not supported on this platform",
- );
-
- c.set("inspector", inspector);
- return next();
- })
- .route("/", createActorInspectorRouter()),
+ createActorInspectorRouter({
+ actorDriver,
+ upgradeWebSocket: runConfig.getUpgradeWebSocket,
+ }),
);
}
diff --git a/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/test-inline-client-driver.ts b/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/test-inline-client-driver.ts
index f25376b2f3..7a5844a2b3 100644
--- a/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/test-inline-client-driver.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/test-inline-client-driver.ts
@@ -226,8 +226,6 @@ export function createTestInlineClientDriver(
displayInformation(): ManagerDisplayInformation {
return { name: "Test Inline", properties: {} };
},
- // TODO:
- getOrCreateInspectorAccessToken: () => "",
// action: async = unknown[], Response = unknown>(
// _c: HonoContext | undefined,
diff --git a/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/tests/actor-inspector.ts b/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/tests/actor-inspector.ts
index 48bc7f3e7c..9b2a51dede 100644
--- a/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/tests/actor-inspector.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/tests/actor-inspector.ts
@@ -1,681 +1,363 @@
-import { describe, expect, test } from "vitest";
-import { HEADER_ACTOR_QUERY } from "@/driver-helpers/mod";
-import {
- createActorInspectorClient,
- createManagerInspectorClient,
-} from "@/inspector/mod";
-import type { ActorQuery } from "@/mod";
+import { describe } from "vitest";
import type { DriverTestConfig } from "../mod";
-import { setupDriverTest } from "../utils";
export function runActorInspectorTests(driverTestConfig: DriverTestConfig) {
// TODO: Add back
describe.skip("Actor Inspector Tests", () => {
- describe("Manager Inspector", () => {
- test("should respond to ping", async (c) => {
- const { endpoint } = await setupDriverTest(c, driverTestConfig);
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.ping.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual({ message: "pong" });
- });
-
- test("should get actors with pagination", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- // Create some actors first
- await client.counter.create(["test-actor-1"]);
- await client.counter.create(["test-actor-2"]);
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.actors.$get({
- query: { limit: "1" },
- });
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual(
- expect.arrayContaining([
- expect.objectContaining({ key: ["test-actor-1"] }),
- ]),
- );
- expect(data.length).toBe(1);
- });
-
- test("should get all actors with pagination", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const actorKey1 = ["test-cursor-1"];
- const actorKey2 = ["test-cursor-2"];
-
- // Create some actors first
- await client.counter.create(actorKey1);
- await client.counter.create(actorKey2);
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.actors.$get({
- query: { limit: "5" },
- });
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual(
- expect.arrayContaining([
- expect.objectContaining({
- id: expect.any(String),
- key: actorKey1,
- }),
- expect.objectContaining({
- id: expect.any(String),
- key: actorKey2,
- }),
- ]),
- );
- });
-
- test("should handle invalid limit parameter", async (c) => {
- const { endpoint } = await setupDriverTest(c, driverTestConfig);
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.actors.$get({
- query: { limit: "0" },
- });
- expect(response.status).toBe(400);
- });
-
- test("should create a new actor", async (c) => {
- const { endpoint } = await setupDriverTest(c, driverTestConfig);
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.actors.$post({
- json: {
- name: "default",
- key: ["test-create-actor"],
- input: {},
- },
- });
-
- expect(response.status).toBe(201);
- const data = await response.json();
- expect(data).toEqual(
- expect.objectContaining({
- id: expect.any(String),
- name: "default",
- key: ["test-create-actor"],
- }),
- );
- });
-
- test("should get builds", async (c) => {
- const { endpoint } = await setupDriverTest(c, driverTestConfig);
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.builds.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual(
- expect.arrayContaining([
- expect.objectContaining({ name: expect.any(String) }),
- ]),
- );
- });
-
- test("should get actor by id", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- // Create an actor and get its ID
- const handle = await client.counter.create(["test-get-by-id"]);
- const actorId = await handle.resolve();
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.actor[":id"].$get({
- param: { id: actorId },
- });
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toHaveProperty("id", actorId);
- });
-
- test("should return 404 for non-existent actor", async (c) => {
- const { endpoint } = await setupDriverTest(c, driverTestConfig);
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.actor[":id"].$get({
- param: { id: "non-existent-id" },
- });
- expect(response.status).toBe(404);
-
- const data = await response.json();
- expect(data).toEqual({ error: "Actor not found" });
- });
-
- test("should get bootstrap data", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- // Create at least one actor to ensure bootstrap has data
- // Create an actor and get its ID
- const handle = await client.counter.create(["test-bootstrap"]);
- await handle.resolve();
-
- const http = createManagerInspectorClient(
- `${endpoint}/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- },
- },
- );
-
- const response = await http.bootstrap.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data.actors).toEqual(
- expect.arrayContaining([
- expect.objectContaining({
- key: ["test-bootstrap"],
- name: "counter",
- }),
- ]),
- );
- });
- });
-
- describe("Actor Inspector", () => {
- test("should handle actor not found", async (c) => {
- const { endpoint } = await setupDriverTest(c, driverTestConfig);
-
- const actorId = "non-existing";
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- const response = await http.ping.$get();
- expect(response.ok).toBe(false);
- });
- test("should respond to ping", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create(["test-ping"]);
- const actorId = await handle.resolve();
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- const response = await http.ping.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual({ message: "pong" });
- });
-
- test("should get actor state", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create(["test-state"]);
- const actorId = await handle.resolve();
-
- // Increment the counter to set some state
- await handle.increment(5);
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- const response = await http.state.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual({
- enabled: true,
- state: expect.objectContaining({
- count: 5,
- }),
- });
- });
-
- test("should update actor state with replace", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create([
- "test-state-replace",
- ]);
- const actorId = await handle.resolve();
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- // Replace the entire state
- const response = await http.state.$patch({
- json: {
- replace: { count: 10 },
- },
- });
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual({
- enabled: true,
- state: { count: 10 },
- });
- });
-
- test("should update actor state with patch", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create([
- "test-state-patch",
- ]);
- const actorId = await handle.resolve();
-
- // Set initial state
- await handle.increment(3);
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- // Patch the state
- const response = await http.state.$patch({
- json: {
- patch: [
- {
- op: "replace",
- path: "/count",
- value: 7,
- },
- ],
- },
- });
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual({
- enabled: true,
- state: expect.objectContaining({
- count: 7,
- }),
- });
- });
-
- test("should get actor connections", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create([
- "test-connections",
- ]);
- const actorId = await handle.resolve();
- handle.connect();
- await handle.increment(10);
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- const response = await http.connections.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data.connections).toEqual(
- expect.arrayContaining([
- expect.objectContaining({
- id: expect.any(String),
- }),
- ]),
- );
- });
-
- test("should get actor events", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create(["test-events"]);
- const actorId = await handle.resolve();
-
- handle.connect();
- await handle.increment(10);
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- const response = await http.events.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data.events).toEqual(
- expect.arrayContaining([
- expect.objectContaining({
- type: "broadcast",
- id: expect.any(String),
- }),
- ]),
- );
- });
-
- test("should clear actor events", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create([
- "test-events-clear",
- ]);
- const actorId = await handle.resolve();
-
- handle.connect();
- await handle.increment(10);
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- {
- const response = await http.events.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data.events).toEqual(
- expect.arrayContaining([
- expect.objectContaining({
- type: "broadcast",
- id: expect.any(String),
- }),
- ]),
- );
- }
-
- const response = await http.events.clear.$post();
- expect(response.status).toBe(200);
- });
-
- test("should get actor rpcs", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create(["test-rpcs"]);
- const actorId = await handle.resolve();
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- const response = await http.rpcs.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- expect(data).toEqual(
- expect.objectContaining({
- rpcs: expect.arrayContaining(["increment", "getCount"]),
- }),
- );
- });
-
- // database is not officially supported yet
- test.skip("should get actor database info", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create(["test-db"]);
- const actorId = await handle.resolve();
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- const response = await http.db.$get();
- expect(response.status).toBe(200);
-
- const data = await response.json();
- // Database might be enabled or disabled depending on actor configuration
- expect(data).toHaveProperty("enabled");
- expect(typeof data.enabled).toBe("boolean");
-
- if (data.enabled) {
- expect(data).toHaveProperty("db");
- expect(Array.isArray(data.db)).toBe(true);
- } else {
- expect(data.db).toBe(null);
- }
- });
-
- test.skip("should execute database query when database is enabled", async (c) => {
- const { client, endpoint } = await setupDriverTest(
- c,
- driverTestConfig,
- );
-
- const handle = await client.counter.create(["test-db-query"]);
- const actorId = await handle.resolve();
-
- const http = createActorInspectorClient(
- `${endpoint}/actors/inspect`,
- {
- headers: {
- Authorization: `Bearer token`,
- [HEADER_ACTOR_QUERY]: JSON.stringify({
- getForId: { name: "counter", actorId },
- } satisfies ActorQuery),
- },
- },
- );
-
- // First check if database is enabled
- const dbInfoResponse = await http.db.$get();
- const dbInfo = await dbInfoResponse.json();
-
- if (dbInfo.enabled) {
- // Execute a simple query
- const queryResponse = await http.db.$post({
- json: {
- query: "SELECT 1 as test",
- params: [],
- },
- });
- expect(queryResponse.status).toBe(200);
-
- const queryData = await queryResponse.json();
- expect(queryData).toHaveProperty("result");
- } else {
- // If database is not enabled, the POST should return enabled: false
- const queryResponse = await http.db.$post({
- json: {
- query: "SELECT 1 as test",
- params: [],
- },
- });
- expect(queryResponse.status).toBe(200);
-
- const queryData = await queryResponse.json();
- expect(queryData).toEqual({ enabled: false });
- }
- });
- });
+ // describe("Actor Inspector", () => {
+ // test("should handle actor not found", async (c) => {
+ // const { endpoint } = await setupDriverTest(c, driverTestConfig);
+ // const actorId = "non-existing";
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // const response = await http.ping.$get();
+ // expect(response.ok).toBe(false);
+ // });
+ // test("should respond to ping", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create(["test-ping"]);
+ // const actorId = await handle.resolve();
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // const response = await http.ping.$get();
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // expect(data).toEqual({ message: "pong" });
+ // });
+ // test("should get actor state", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create(["test-state"]);
+ // const actorId = await handle.resolve();
+ // // Increment the counter to set some state
+ // await handle.increment(5);
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // const response = await http.state.$get();
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // expect(data).toEqual({
+ // enabled: true,
+ // state: expect.objectContaining({
+ // count: 5,
+ // }),
+ // });
+ // });
+ // test("should update actor state with replace", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create([
+ // "test-state-replace",
+ // ]);
+ // const actorId = await handle.resolve();
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // // Replace the entire state
+ // const response = await http.state.$patch({
+ // json: {
+ // replace: { count: 10 },
+ // },
+ // });
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // expect(data).toEqual({
+ // enabled: true,
+ // state: { count: 10 },
+ // });
+ // });
+ // test("should update actor state with patch", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create([
+ // "test-state-patch",
+ // ]);
+ // const actorId = await handle.resolve();
+ // // Set initial state
+ // await handle.increment(3);
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // // Patch the state
+ // const response = await http.state.$patch({
+ // json: {
+ // patch: [
+ // {
+ // op: "replace",
+ // path: "/count",
+ // value: 7,
+ // },
+ // ],
+ // },
+ // });
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // expect(data).toEqual({
+ // enabled: true,
+ // state: expect.objectContaining({
+ // count: 7,
+ // }),
+ // });
+ // });
+ // test("should get actor connections", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create([
+ // "test-connections",
+ // ]);
+ // const actorId = await handle.resolve();
+ // handle.connect();
+ // await handle.increment(10);
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // const response = await http.connections.$get();
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // expect(data.connections).toEqual(
+ // expect.arrayContaining([
+ // expect.objectContaining({
+ // id: expect.any(String),
+ // }),
+ // ]),
+ // );
+ // });
+ // test("should get actor events", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create(["test-events"]);
+ // const actorId = await handle.resolve();
+ // handle.connect();
+ // await handle.increment(10);
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // const response = await http.events.$get();
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // expect(data.events).toEqual(
+ // expect.arrayContaining([
+ // expect.objectContaining({
+ // type: "broadcast",
+ // id: expect.any(String),
+ // }),
+ // ]),
+ // );
+ // });
+ // test("should clear actor events", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create([
+ // "test-events-clear",
+ // ]);
+ // const actorId = await handle.resolve();
+ // handle.connect();
+ // await handle.increment(10);
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // {
+ // const response = await http.events.$get();
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // expect(data.events).toEqual(
+ // expect.arrayContaining([
+ // expect.objectContaining({
+ // type: "broadcast",
+ // id: expect.any(String),
+ // }),
+ // ]),
+ // );
+ // }
+ // const response = await http.events.clear.$post();
+ // expect(response.status).toBe(200);
+ // });
+ // test("should get actor rpcs", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create(["test-rpcs"]);
+ // const actorId = await handle.resolve();
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // const response = await http.rpcs.$get();
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // expect(data).toEqual(
+ // expect.objectContaining({
+ // rpcs: expect.arrayContaining(["increment", "getCount"]),
+ // }),
+ // );
+ // });
+ // // database is not officially supported yet
+ // test.skip("should get actor database info", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create(["test-db"]);
+ // const actorId = await handle.resolve();
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // const response = await http.db.$get();
+ // expect(response.status).toBe(200);
+ // const data = await response.json();
+ // // Database might be enabled or disabled depending on actor configuration
+ // expect(data).toHaveProperty("enabled");
+ // expect(typeof data.enabled).toBe("boolean");
+ // if (data.enabled) {
+ // expect(data).toHaveProperty("db");
+ // expect(Array.isArray(data.db)).toBe(true);
+ // } else {
+ // expect(data.db).toBe(null);
+ // }
+ // });
+ // test.skip("should execute database query when database is enabled", async (c) => {
+ // const { client, endpoint } = await setupDriverTest(
+ // c,
+ // driverTestConfig,
+ // );
+ // const handle = await client.counter.create(["test-db-query"]);
+ // const actorId = await handle.resolve();
+ // const http = createActorInspectorClient(
+ // `${endpoint}/actors/inspect`,
+ // {
+ // headers: {
+ // Authorization: `Bearer token`,
+ // [HEADER_ACTOR_QUERY]: JSON.stringify({
+ // getForId: { name: "counter", actorId },
+ // } satisfies ActorQuery),
+ // },
+ // },
+ // );
+ // // First check if database is enabled
+ // const dbInfoResponse = await http.db.$get();
+ // const dbInfo = await dbInfoResponse.json();
+ // if (dbInfo.enabled) {
+ // // Execute a simple query
+ // const queryResponse = await http.db.$post({
+ // json: {
+ // query: "SELECT 1 as test",
+ // params: [],
+ // },
+ // });
+ // expect(queryResponse.status).toBe(200);
+ // const queryData = await queryResponse.json();
+ // expect(queryData).toHaveProperty("result");
+ // } else {
+ // // If database is not enabled, the POST should return enabled: false
+ // const queryResponse = await http.db.$post({
+ // json: {
+ // query: "SELECT 1 as test",
+ // params: [],
+ // },
+ // });
+ // expect(queryResponse.status).toBe(200);
+ // const queryData = await queryResponse.json();
+ // expect(queryData).toEqual({ enabled: false });
+ // }
+ // });
+ // });
});
}
diff --git a/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/global-state.ts b/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/global-state.ts
index 9bc76f87a8..41cfad34ee 100644
--- a/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/global-state.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/global-state.ts
@@ -7,7 +7,6 @@ import { lookupInRegistry } from "@/actor/definition";
import { ActorAlreadyExists } from "@/actor/errors";
import type { AnyActorInstance } from "@/actor/instance/mod";
import type { ActorKey } from "@/actor/mod";
-import { generateRandomString } from "@/actor/utils";
import type { AnyClient } from "@/client/client";
import {
type ActorDriver,
@@ -683,17 +682,6 @@ export class FileSystemGlobalState {
}, delay);
}
- getOrCreateInspectorAccessToken(): string {
- const tokenPath = path.join(this.#storagePath, "inspector-token");
- if (fsSync.existsSync(tokenPath)) {
- return fsSync.readFileSync(tokenPath, "utf-8");
- }
-
- const newToken = generateRandomString();
- fsSync.writeFileSync(tokenPath, newToken);
- return newToken;
- }
-
/**
* Cleanup stale temp files on startup (synchronous)
*/
diff --git a/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts b/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts
index d080642900..31d3d1f283 100644
--- a/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts
@@ -20,8 +20,6 @@ import type {
ListActorsInput,
ManagerDriver,
} from "@/driver-helpers/mod";
-import { ManagerInspector } from "@/inspector/manager";
-import { type Actor, ActorFeature, type ActorId } from "@/inspector/mod";
import type { ManagerDisplayInformation } from "@/manager/driver";
import {
type DriverConfig,
@@ -46,8 +44,6 @@ export class FileSystemManagerDriver implements ManagerDriver {
#actorDriver: ActorDriver;
#actorRouter: ActorRouter;
- inspector?: ManagerInspector;
-
constructor(
registryConfig: RegistryConfig,
runConfig: RunConfig,
@@ -59,73 +55,6 @@ export class FileSystemManagerDriver implements ManagerDriver {
this.#state = state;
this.#driverConfig = driverConfig;
- if (runConfig.inspector.enabled) {
- const startedAt = new Date().toISOString();
- function transformActor(actorState: schema.ActorState): Actor {
- return {
- id: actorState.actorId as ActorId,
- name: actorState.name,
- key: actorState.key as string[],
- startedAt: startedAt,
- createdAt: new Date(
- Number(actorState.createdAt),
- ).toISOString(),
- features: [
- ActorFeature.State,
- ActorFeature.Connections,
- ActorFeature.Console,
- ActorFeature.EventsMonitoring,
- ActorFeature.Database,
- ],
- };
- }
-
- this.inspector = new ManagerInspector(() => {
- return {
- getAllActors: async ({ cursor, limit }) => {
- const itr = this.#state.getActorsIterator({ cursor });
- const actors: Actor[] = [];
-
- for await (const actor of itr) {
- actors.push(transformActor(actor));
- if (limit && actors.length >= limit) {
- break;
- }
- }
- return actors;
- },
- getActorById: async (id) => {
- try {
- const result =
- await this.#state.loadActorStateOrError(id);
- return transformActor(result);
- } catch {
- return null;
- }
- },
- getBuilds: async () => {
- return Object.keys(this.#registryConfig.use).map(
- (name) => ({
- name,
- }),
- );
- },
- createActor: async (input) => {
- const { actorId } = await this.createActor(input);
- try {
- const result =
- await this.#state.loadActorStateOrError(
- actorId,
- );
- return transformActor(result);
- } catch {
- return null;
- }
- },
- };
- });
- }
-
// Actors run on the same node as the manager, so we create a dummy actor router that we route requests to
const inlineClient = createClientWithDriver(
this,
@@ -377,8 +306,4 @@ export class FileSystemManagerDriver implements ManagerDriver {
data: this.#state.storagePath,
};
}
-
- getOrCreateInspectorAccessToken() {
- return this.#state.getOrCreateInspectorAccessToken();
- }
}
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/actor.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/actor.ts
index fc080bcc74..157b963817 100644
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/actor.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/inspector/actor.ts
@@ -1,23 +1,17 @@
-import { sValidator } from "@hono/standard-validator";
-import jsonPatch from "@rivetkit/fast-json-patch";
-import { Hono } from "hono";
-import { streamSSE } from "hono/streaming";
-import { createNanoEvents, type Unsubscribe } from "nanoevents";
-import z from "zod/v4";
+import type { WSContext } from "hono/ws";
+import { createNanoEvents } from "nanoevents";
import type {
AnyDatabaseProvider,
InferDatabaseClient,
} from "@/actor/database";
-import { promiseWithResolvers } from "@/utils";
+import type { ActorDriver } from "@/actor/driver";
+import type { UpgradeWebSocketArgs } from "@/mod";
+import type * as schema from "@/schemas/actor-inspector/mod";
import {
- ColumnsSchema,
- type Connection,
- ForeignKeysSchema,
- PatchSchema,
- type RealtimeEvent,
- type RecordedRealtimeEvent,
- TablesSchema,
-} from "./protocol/common";
+ TO_CLIENT_VERSIONED,
+ TO_SERVER_VERSIONED,
+} from "@/schemas/actor-inspector/versioned";
+import type { GetUpgradeWebSocket } from "@/utils";
export type ActorInspectorRouterEnv = {
Variables: {
@@ -29,8 +23,45 @@ export type ActorInspectorRouterEnv = {
* Create a router for the Actor Inspector.
* @internal
*/
-export function createActorInspectorRouter() {
- return new Hono()
+export function createActorInspectorRouter(config: {
+ upgradeWebSocket?: GetUpgradeWebSocket;
+ actorDriver: ActorDriver;
+}) {
+ const upgradeWebSocket = config.upgradeWebSocket?.();
+
+ if (!upgradeWebSocket) {
+ throw new Error("WebSocket upgrade function is not provided.");
+ // return c.text(
+ // "WebSockets are not enabled for this driver, enable them to use the Actor Inspector.",
+ // 400,
+ // );
+ }
+
+ return upgradeWebSocket(async (c) => {
+ const inspector = (await config.actorDriver.loadActor(c.env.actorId))
+ .inspector;
+
+ if (!inspector) {
+ return c.text("Inspector is not enabled for this actor.", 400);
+ }
+
+ return {
+ onOpen: async (event, ws) => {
+ console.log("onOpen", event, ws);
+ },
+ onMessage: (event: any, ws: WSContext): void => {
+ console.log("onMsg", event, ws);
+ },
+ onClose: (event: any, ws: WSContext): void => {
+ console.log("onClose", event, ws);
+ },
+ onError: (error: any, ws: WSContext): void => {
+ console.log("onError", error, ws);
+ },
+ } satisfies UpgradeWebSocketArgs;
+ });
+}
+/*
.get("/ping", (c) => {
return c.json({ message: "pong" }, 200);
})
@@ -289,8 +320,7 @@ export function createActorInspectorRouter() {
);
return c.json({ result }, 200);
},
- );
-}
+ );*/
interface ActorInspectorAccessors {
isStateEnabled: () => Promise;
@@ -306,9 +336,39 @@ interface ActorInspectorAccessors {
interface ActorInspectorEmitterEvents {
stateUpdated: (state: unknown) => void;
connectionUpdated: () => void;
- eventFired: (event: RealtimeEvent) => void;
+ eventFired: (event: EventDetails) => void;
}
+type Connection = Omit & {
+ details: unknown;
+};
+
+type EventDetails =
+ | {
+ type: "action";
+ name: string;
+ args: unknown[];
+ connId: string;
+ }
+ | {
+ type: "subscribe";
+ eventName: string;
+ connId: string;
+ }
+ | {
+ type: "unsubscribe";
+ eventName: string;
+ connId: string;
+ }
+ | {
+ type: "event";
+ eventName: string;
+ args: unknown[];
+ connId: string;
+ };
+
+type Event = { id: string; timestamp: number } & EventDetails;
+
/**
* Provides a unified interface for inspecting actor external and internal state.
*/
@@ -316,23 +376,23 @@ export class ActorInspector {
public readonly accessors: ActorInspectorAccessors;
public readonly emitter = createNanoEvents();
- #lastRealtimeEvents: RecordedRealtimeEvent[] = [];
+ #lastEvents: Event[] = [];
- get lastRealtimeEvents() {
- return this.#lastRealtimeEvents;
+ get lastEvents() {
+ return this.#lastEvents;
}
constructor(accessors: () => ActorInspectorAccessors) {
this.accessors = accessors();
this.emitter.on("eventFired", (event) => {
- this.#lastRealtimeEvents.push({
+ this.#lastEvents.push({
id: crypto.randomUUID(),
timestamp: Date.now(),
...event,
});
// keep the last 100 events
- if (this.#lastRealtimeEvents.length > 100) {
- this.#lastRealtimeEvents = this.#lastRealtimeEvents.slice(-100);
+ if (this.#lastEvents.length > 100) {
+ this.#lastEvents = this.#lastEvents.slice(-100);
}
});
}
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/config.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/config.ts
index 69688f3c8e..945f12c114 100644
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/config.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/inspector/config.ts
@@ -20,16 +20,7 @@ const defaultEnabled = () => {
export const InspectorConfigSchema = z
.object({
- enabled: z
- .boolean()
- .or(
- z.object({
- actor: z.boolean().optional().default(true),
- manager: z.boolean().optional().default(true),
- }),
- )
- .optional()
- .default(defaultEnabled),
+ enabled: z.boolean().default(defaultEnabled),
/**
* Token used to access the Inspector.
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/manager.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/manager.ts
deleted file mode 100644
index 3c131db1b0..0000000000
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/manager.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import { sValidator } from "@hono/standard-validator";
-import { Hono } from "hono";
-import invariant from "invariant";
-import type { CreateInput } from "@/manager/driver";
-import { inspectorLogger } from "./log";
-import { type Actor, type Builds, CreateActorSchema } from "./protocol/common";
-
-export type ManagerInspectorRouterEnv = {
- Variables: {
- inspector: ManagerInspector;
- };
-};
-
-/**
- * Create a router for the Manager Inspector.
- * @internal
- */
-export function createManagerInspectorRouter() {
- return new Hono()
- .get("/ping", (c) => {
- return c.json({ message: "pong" }, 200);
- })
- .get("/actors", async (c) => {
- const limit =
- Number.parseInt(c.req.query("limit") ?? "") || undefined;
- const cursor = c.req.query("cursor") || undefined;
-
- if (!limit || (limit && limit <= 0)) {
- return c.json("Invalid limit", 400);
- }
-
- try {
- const actors = await c.var.inspector.accessors.getAllActors({
- limit,
- cursor,
- });
- return c.json(actors, 200);
- } catch (error) {
- inspectorLogger().error({
- msg: "Failed to fetch actors",
- error,
- });
- return c.json("Failed to fetch actors", 500);
- }
- })
-
- .post("/actors", sValidator("json", CreateActorSchema), async (c) => {
- const actor = await c.var.inspector.accessors.createActor(
- c.req.valid("json"),
- );
- return c.json(actor, 201);
- })
- .get("/builds", async (c) => {
- const builds = await c.var.inspector.accessors.getBuilds();
- return c.json(builds, 200);
- })
- .get("/actor/:id", async (c) => {
- const id = c.req.param("id");
- const actor = await c.var.inspector.accessors.getActorById(id);
- if (!actor) {
- return c.json({ error: "Actor not found" }, 404);
- }
- return c.json(actor, 200);
- })
- .get("/bootstrap", async (c) => {
- const actors = await c.var.inspector.accessors.getAllActors({
- limit: 10,
- });
- return c.json({ actors }, 200);
- });
-}
-
-interface ManagerInspectorAccessors {
- getAllActors: (param: {
- cursor?: string;
- limit: number;
- }) => Promise;
- getActorById: (id: string) => Promise;
- getBuilds: () => Promise;
- createActor: (input: CreateInput) => Promise;
-}
-
-/**
- * Provides a unified interface for inspecting actor external and internal state.
- */
-export class ManagerInspector {
- public readonly accessors: ManagerInspectorAccessors;
-
- constructor(accessors: () => ManagerInspectorAccessors) {
- this.accessors = accessors();
- inspectorLogger().debug({ msg: "Manager Inspector enabled and ready" });
- }
-}
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/mod.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/mod.ts
index da4942a6eb..1ee3127821 100644
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/mod.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/inspector/mod.ts
@@ -1,3 +1,2 @@
-export * from "./protocol/common";
-export * from "./protocol/mod";
-export * from "./utils";
+export * from "../schemas/actor-inspector/mod";
+export * from "../schemas/actor-inspector/versioned";
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/actor.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/actor.ts
deleted file mode 100644
index a625044233..0000000000
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/actor.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { hc } from "hono/client";
-import type { createActorInspectorRouter } from "../actor";
-
-type ActorInspectorRouter = ReturnType;
-const client = hc("");
-export type ActorInspectorClient = typeof client;
-
-export const createActorInspectorClient = (
- ...args: Parameters
-): ActorInspectorClient => hc(...args);
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/common.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/common.ts
deleted file mode 100644
index 26410ac73b..0000000000
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/common.ts
+++ /dev/null
@@ -1,196 +0,0 @@
-import z from "zod/v4";
-import { ActorKeySchema, MAX_ACTOR_KEY_SIZE } from "@/manager/protocol/query";
-
-export const ActorId = z.string().brand("ActorId");
-export type ActorId = z.infer;
-
-export enum ActorFeature {
- Logs = "logs",
- Config = "config",
- Connections = "connections",
- State = "state",
- Console = "console",
- Runtime = "runtime",
- Metrics = "metrics",
- EventsMonitoring = "events-monitoring",
- Database = "database",
-}
-
-export const ActorLogEntry = z.object({
- level: z.string(),
- message: z.string(),
- timestamp: z.string(),
- metadata: z.record(z.string(), z.any()).optional(),
-});
-
-export const ActorSchema = z.object({
- id: ActorId,
- name: z.string(),
- key: z.array(z.string()),
- tags: z.record(z.string(), z.string()).optional(),
- region: z.string().optional(),
- createdAt: z.string().optional(),
- startedAt: z.string().optional(),
- destroyedAt: z.string().optional(),
- features: z.array(z.enum(ActorFeature)).optional(),
-});
-
-export type Actor = z.infer;
-export type ActorLogEntry = z.infer;
-
-// MARK: State
-
-export const OperationSchema = z.discriminatedUnion("op", [
- z.object({
- op: z.literal("remove"),
- path: z.string(),
- }),
- z.object({
- op: z.literal("add"),
- path: z.string(),
- value: z.unknown(),
- }),
- z.object({
- op: z.literal("replace"),
- path: z.string(),
- value: z.unknown(),
- }),
- z.object({
- op: z.literal("move"),
- path: z.string(),
- from: z.string(),
- }),
- z.object({
- op: z.literal("copy"),
- path: z.string(),
- from: z.string(),
- }),
- z.object({
- op: z.literal("test"),
- path: z.string(),
- value: z.unknown(),
- }),
-]);
-export type Operation = z.infer;
-
-export const PatchSchema = z.array(OperationSchema);
-export type Patch = z.infer;
-
-// MARK: Connections
-
-export const ConnectionSchema = z.object({
- params: z.record(z.string(), z.any()).optional(),
- id: z.string(),
- stateEnabled: z.boolean().optional(),
- state: z.any().optional(),
- auth: z.record(z.string(), z.any()).optional(),
-});
-export type Connection = z.infer;
-
-// MARK: Realtime Events
-
-export const RealtimeEventSchema = z.discriminatedUnion("type", [
- z.object({
- type: z.literal("action"),
- name: z.string(),
- args: z.array(z.any()),
- connId: z.string(),
- }),
- z.object({
- type: z.literal("broadcast"),
- eventName: z.string(),
- args: z.array(z.any()),
- }),
- z.object({
- type: z.literal("subscribe"),
- eventName: z.string(),
- connId: z.string(),
- }),
- z.object({
- type: z.literal("unsubscribe"),
- eventName: z.string(),
- connId: z.string(),
- }),
- z.object({
- type: z.literal("event"),
- eventName: z.string(),
- args: z.array(z.any()),
- connId: z.string(),
- }),
-]);
-export type RealtimeEvent = z.infer;
-export const RecordedRealtimeEventSchema = RealtimeEventSchema.and(
- z.object({
- id: z.string(),
- timestamp: z.number(),
- }),
-);
-export type RecordedRealtimeEvent = z.infer;
-
-// MARK: Database
-
-export const DatabaseQuerySchema = z.object({
- sql: z.string(),
- args: z.array(z.string().or(z.number())),
-});
-export type DatabaseQuery = z.infer;
-
-export const TableSchema = z.object({
- schema: z.string(),
- name: z.string(),
- type: z.enum(["table", "view"]),
-});
-export type Table = z.infer;
-
-export const TablesSchema = z.array(TableSchema);
-export type Tables = z.infer;
-
-export const ColumnSchema = z.object({
- cid: z.number(),
- name: z.string(),
- type: z
- .string()
- .toLowerCase()
- .transform((val) => {
- return z
- .enum(["integer", "text", "real", "blob", "numeric", "serial"])
- .parse(val);
- }),
- notnull: z.coerce.boolean(),
- dflt_value: z.string().nullable(),
- pk: z.coerce.boolean().nullable(),
-});
-export type Column = z.infer;
-
-export const ColumnsSchema = z.array(ColumnSchema);
-export type Columns = z.infer;
-
-export const ForeignKeySchema = z.object({
- id: z.number(),
- table: z.string(),
- from: z.string(),
- to: z.string(),
-});
-export type ForeignKey = z.infer;
-
-export const ForeignKeysSchema = z.array(ForeignKeySchema);
-export type ForeignKeys = z.infer;
-
-// MARK: Builds
-
-export const BuildSchema = z.object({
- name: z.string(),
- createdAt: z.string().optional(),
- tags: z.record(z.string(), z.string()).optional(),
-});
-export type Build = z.infer;
-export const BuildsSchema = z.array(BuildSchema);
-export type Builds = z.infer;
-
-export const CreateActorSchema = z.object({
- name: z.string(),
- // FIXME: Replace with ActorKeySchema when ready
- key: z.array(z.string().max(MAX_ACTOR_KEY_SIZE)),
- input: z.any(),
-});
-export type CreateActor = z.infer;
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/manager.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/manager.ts
deleted file mode 100644
index ac3e1aecb7..0000000000
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/manager.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { hc } from "hono/client";
-import type { createManagerInspectorRouter } from "../manager";
-
-type ManagerInspectorRouter = ReturnType;
-const client = hc("");
-export type ManagerInspectorClient = typeof client;
-
-export const createManagerInspectorClient = (
- ...args: Parameters
-): ManagerInspectorClient => hc(...args);
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/mod.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/mod.ts
deleted file mode 100644
index a4f6f07f14..0000000000
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/protocol/mod.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from "./actor";
-export * from "./manager";
diff --git a/rivetkit-typescript/packages/rivetkit/src/inspector/utils.ts b/rivetkit-typescript/packages/rivetkit/src/inspector/utils.ts
index edb2b78daa..b753fdccda 100644
--- a/rivetkit-typescript/packages/rivetkit/src/inspector/utils.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/inspector/utils.ts
@@ -75,19 +75,6 @@ export function getInspectorUrl(runConfig: RunnerConfigInput | undefined) {
return url.href;
}
-export const isInspectorEnabled = (
- runConfig: RunConfig,
- // TODO(kacper): Remove context in favor of using the gateway, so only context is the actor
- context: "actor" | "manager",
-) => {
- if (typeof runConfig.inspector?.enabled === "boolean") {
- return runConfig.inspector.enabled;
- } else if (typeof runConfig.inspector?.enabled === "object") {
- return runConfig.inspector.enabled[context];
- }
- return false;
-};
-
export const configureInspectorAccessToken = (
runConfig: RunConfig,
managerDriver: ManagerDriver,
diff --git a/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts b/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts
index 22dcabd1ed..8c4eb01334 100644
--- a/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts
@@ -1,6 +1,5 @@
import type { Env, Hono, Context as HonoContext } from "hono";
import type { ActorKey, Encoding, UniversalWebSocket } from "@/actor/mod";
-import type { ManagerInspector } from "@/inspector/manager";
import type { RegistryConfig } from "@/registry/config";
import type { RunnerConfig } from "@/registry/run-config";
@@ -44,20 +43,6 @@ export interface ManagerDriver {
registryConfig: RegistryConfig,
router: Hono,
) => void;
-
- // TODO(kacper): Remove this in favor of standard manager API
- /**
- * @internal
- */
- readonly inspector?: ManagerInspector;
-
- // TODO(kacper): Remove this in favor of ActorDriver.getinspectorToken
- /**
- * Get or create the inspector access token.
- * @experimental
- * @returns creates or returns existing inspector access token
- */
- getOrCreateInspectorAccessToken: () => string;
}
export interface ManagerDisplayInformation {
diff --git a/rivetkit-typescript/packages/rivetkit/src/manager/router.ts b/rivetkit-typescript/packages/rivetkit/src/manager/router.ts
index b54523335e..a751f60934 100644
--- a/rivetkit-typescript/packages/rivetkit/src/manager/router.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/manager/router.ts
@@ -1,10 +1,10 @@
import { createRoute, OpenAPIHono } from "@hono/zod-openapi";
import * as cbor from "cbor-x";
-import {
+import type {
Hono,
- type Context as HonoContext,
- type MiddlewareHandler,
- type Next,
+ Context as HonoContext,
+ MiddlewareHandler,
+ Next,
} from "hono";
import { createMiddleware } from "hono/factory";
import invariant from "invariant";
@@ -220,24 +220,6 @@ function addManagerRoutes(
managerDriver: ManagerDriver,
router: OpenAPIHono,
) {
- // TODO(kacper): Remove this in favor of standard manager API
- // Inspector
- if (isInspectorEnabled(runConfig, "manager")) {
- if (!managerDriver.inspector) {
- throw new Unsupported("inspector");
- }
- router.route(
- "/inspect",
- new Hono<{ Variables: { inspector: any } }>()
- .use(secureInspector(runConfig))
- .use((c, next) => {
- c.set("inspector", managerDriver.inspector!);
- return next();
- })
- .route("/", createManagerInspectorRouter()),
- );
- }
-
// Actor gateway
router.use("*", actorGateway.bind(undefined, runConfig, managerDriver));
diff --git a/rivetkit-typescript/packages/rivetkit/src/registry/mod.ts b/rivetkit-typescript/packages/rivetkit/src/registry/mod.ts
index 78fb798778..708b084a2e 100644
--- a/rivetkit-typescript/packages/rivetkit/src/registry/mod.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/registry/mod.ts
@@ -8,7 +8,6 @@ import { ENGINE_ENDPOINT, ensureEngineProcess } from "@/engine-process/mod";
import {
configureInspectorAccessToken,
getInspectorUrl,
- isInspectorEnabled,
} from "@/inspector/utils";
import { createManagerRouter } from "@/manager/router";
import {
@@ -139,15 +138,12 @@ export class Registry {
// Set defaults based on the driver
if (driver.name === "engine") {
- config.inspector.enabled = { manager: false, actor: true };
-
// We need to leave the default server enabled for dev
if (config.runnerKind !== "serverless") {
config.disableDefaultServer = true;
}
}
if (driver.name === "cloudflare-workers") {
- config.inspector.enabled = { manager: false, actor: true };
config.disableDefaultServer = true;
config.disableActorDriver = true;
config.noWelcome = true;
@@ -173,7 +169,7 @@ export class Registry {
definitions: Object.keys(this.#config.use).length,
...driverLog,
});
- if (isInspectorEnabled(config, "manager") && managerDriver.inspector) {
+ if (config.inspector.enabled) {
logger().info({
msg: "inspector ready",
url: getInspectorUrl(config),
@@ -200,10 +196,7 @@ export class Registry {
const padding = " ".repeat(Math.max(0, 13 - k.length));
console.log(` - ${k}:${padding}${v}`);
}
- if (
- isInspectorEnabled(config, "manager") &&
- managerDriver.inspector
- ) {
+ if (config.inspector.enabled) {
console.log(` - Inspector: ${getInspectorUrl(config)}`);
}
console.log();
diff --git a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts
index e9eecef6e3..e81da48ef1 100644
--- a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts
+++ b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts
@@ -381,8 +381,4 @@ export class RemoteManagerDriver implements ManagerDriver {
displayInformation(): ManagerDisplayInformation {
return { name: "Remote", properties: {} };
}
-
- getOrCreateInspectorAccessToken() {
- return generateRandomString();
- }
}
diff --git a/rivetkit-typescript/packages/rivetkit/src/schemas/actor-inspector/mod.ts b/rivetkit-typescript/packages/rivetkit/src/schemas/actor-inspector/mod.ts
new file mode 100644
index 0000000000..f5c9cb363e
--- /dev/null
+++ b/rivetkit-typescript/packages/rivetkit/src/schemas/actor-inspector/mod.ts
@@ -0,0 +1 @@
+export * from "../../../dist/schemas/actor-inspector/v1";
diff --git a/rivetkit-typescript/packages/rivetkit/src/schemas/actor-inspector/versioned.ts b/rivetkit-typescript/packages/rivetkit/src/schemas/actor-inspector/versioned.ts
new file mode 100644
index 0000000000..849025a44b
--- /dev/null
+++ b/rivetkit-typescript/packages/rivetkit/src/schemas/actor-inspector/versioned.ts
@@ -0,0 +1,23 @@
+import {
+ createVersionedDataHandler,
+ type MigrationFn,
+} from "@/common/versioned-data";
+import * as v1 from "../../../dist/schemas/actor-inspector/v1";
+
+export const CURRENT_VERSION = 1;
+
+const migrations = new Map>([]);
+
+export const TO_SERVER_VERSIONED = createVersionedDataHandler({
+ currentVersion: CURRENT_VERSION,
+ migrations,
+ serializeVersion: (data) => v1.encodeToServer(data),
+ deserializeVersion: (bytes) => v1.decodeToServer(bytes),
+});
+
+export const TO_CLIENT_VERSIONED = createVersionedDataHandler({
+ currentVersion: CURRENT_VERSION,
+ migrations,
+ serializeVersion: (data) => v1.encodeToClient(data),
+ deserializeVersion: (bytes) => v1.decodeToClient(bytes),
+});