import { WS_METHODS } from "@t3tools/contracts";import { Atom } from "effect/unstable/reactivity"; import type { EnvironmentRegistry } from "../connection/registry.ts";import { createAtomCommandScheduler, createEnvironmentRpcCommand, createEnvironmentRpcQueryAtomFamily, createEnvironmentRpcSubscriptionAtomFamily,} from "./runtime.ts"; export const previewAutomationHostFocusConcurrencyKey = (value: { readonly environmentId: string; readonly input: { readonly clientId: string; readonly connectionId: string; };}): string => JSON.stringify([value.environmentId, value.input.clientId, value.input.connectionId]); export function createPreviewEnvironmentAtoms<R, E>( runtime: Atom.AtomRuntime<EnvironmentRegistry | R, E>,) { const lifecycleScheduler = createAtomCommandScheduler(); const statusScheduler = createAtomCommandScheduler(); const automationScheduler = createAtomCommandScheduler(); const lifecycleConcurrency = { mode: "serial" as const, key: ({ environmentId, input }: { environmentId: string; input: { threadId: string } }) => JSON.stringify([environmentId, input.threadId]), }; return { list: createEnvironmentRpcQueryAtomFamily(runtime, { label: "environment-data:preview:list", tag: WS_METHODS.previewList, staleTimeMs: 5_000, }), events: createEnvironmentRpcSubscriptionAtomFamily(runtime, { label: "environment-data:preview:events", tag: WS_METHODS.subscribePreviewEvents, }), discoveredServers: createEnvironmentRpcSubscriptionAtomFamily(runtime, { label: "environment-data:preview:discovered-servers", tag: WS_METHODS.subscribeDiscoveredLocalServers, // Configured URLs are part of this atom's key. Dispose immediately so // unmounted projects stop contributing probe candidates on the server. idleTtlMs: 0, }), automationRequests: createEnvironmentRpcSubscriptionAtomFamily(runtime, { label: "environment-data:preview:automation-requests", tag: WS_METHODS.previewAutomationConnect, // Automation requests are commands, not cached query data. Dispose the // stream immediately with its owner so stale requests cannot replay when // a thread remounts and the server can clear disconnected hosts promptly. idleTtlMs: 0, }), open: createEnvironmentRpcCommand(runtime, { label: "environment-data:preview:open", tag: WS_METHODS.previewOpen, scheduler: lifecycleScheduler, concurrency: lifecycleConcurrency, }), navigate: createEnvironmentRpcCommand(runtime, { label: "environment-data:preview:navigate", tag: WS_METHODS.previewNavigate, scheduler: lifecycleScheduler, concurrency: lifecycleConcurrency, }), resize: createEnvironmentRpcCommand(runtime, { label: "environment-data:preview:resize", tag: WS_METHODS.previewResize, scheduler: lifecycleScheduler, concurrency: lifecycleConcurrency, }), refresh: createEnvironmentRpcCommand(runtime, { label: "environment-data:preview:refresh", tag: WS_METHODS.previewRefresh, scheduler: lifecycleScheduler, concurrency: lifecycleConcurrency, }), close: createEnvironmentRpcCommand(runtime, { label: "environment-data:preview:close", tag: WS_METHODS.previewClose, scheduler: lifecycleScheduler, concurrency: lifecycleConcurrency, }), reportStatus: createEnvironmentRpcCommand(runtime, { label: "environment-data:preview:report-status", tag: WS_METHODS.previewReportStatus, scheduler: statusScheduler, concurrency: { mode: "latest", key: ({ environmentId, input }) => JSON.stringify([environmentId, input.threadId, input.tabId]), }, }), respondToAutomation: createEnvironmentRpcCommand(runtime, { label: "environment-data:preview:automation-respond", tag: WS_METHODS.previewAutomationRespond, scheduler: automationScheduler, concurrency: { mode: "singleFlight", key: ({ environmentId, input }) => JSON.stringify([environmentId, input.connectionId, input.requestId]), }, }), focusAutomationHost: createEnvironmentRpcCommand(runtime, { label: "environment-data:preview:automation-focus-host", tag: WS_METHODS.previewAutomationFocusHost, scheduler: automationScheduler, concurrency: { mode: "latest", key: previewAutomationHostFocusConcurrencyKey, }, }), };}
import { WS_METHODS } from "@t3tools/contracts";
import { Atom } from "effect/unstable/reactivity";
import type { EnvironmentRegistry } from "../connection/registry.ts";
import {
createAtomCommandScheduler,
createEnvironmentRpcCommand,
createEnvironmentRpcQueryAtomFamily,
createEnvironmentRpcSubscriptionAtomFamily,
} from "./runtime.ts";
export const previewAutomationHostFocusConcurrencyKey = (value: {
readonly environmentId: string;
readonly input: {
readonly clientId: string;
readonly connectionId: string;
};
}): string => JSON.stringify([value.environmentId, value.input.clientId, value.input.connectionId]);
export function createPreviewEnvironmentAtoms<R, E>(
runtime: Atom.AtomRuntime<EnvironmentRegistry | R, E>,
) {
const lifecycleScheduler = createAtomCommandScheduler();
const statusScheduler = createAtomCommandScheduler();
const automationScheduler = createAtomCommandScheduler();
const lifecycleConcurrency = {
mode: "serial" as const,
key: ({ environmentId, input }: { environmentId: string; input: { threadId: string } }) =>
JSON.stringify([environmentId, input.threadId]),
};
return {
list: createEnvironmentRpcQueryAtomFamily(runtime, {
label: "environment-data:preview:list",
tag: WS_METHODS.previewList,
staleTimeMs: 5_000,
}),
events: createEnvironmentRpcSubscriptionAtomFamily(runtime, {
label: "environment-data:preview:events",
tag: WS_METHODS.subscribePreviewEvents,
}),
discoveredServers: createEnvironmentRpcSubscriptionAtomFamily(runtime, {
label: "environment-data:preview:discovered-servers",
tag: WS_METHODS.subscribeDiscoveredLocalServers,
// Configured URLs are part of this atom's key. Dispose immediately so
// unmounted projects stop contributing probe candidates on the server.
idleTtlMs: 0,
}),
automationRequests: createEnvironmentRpcSubscriptionAtomFamily(runtime, {
label: "environment-data:preview:automation-requests",
tag: WS_METHODS.previewAutomationConnect,
// Automation requests are commands, not cached query data. Dispose the
// stream immediately with its owner so stale requests cannot replay when
// a thread remounts and the server can clear disconnected hosts promptly.
idleTtlMs: 0,
}),
open: createEnvironmentRpcCommand(runtime, {
label: "environment-data:preview:open",
tag: WS_METHODS.previewOpen,
scheduler: lifecycleScheduler,
concurrency: lifecycleConcurrency,
}),
navigate: createEnvironmentRpcCommand(runtime, {
label: "environment-data:preview:navigate",
tag: WS_METHODS.previewNavigate,
scheduler: lifecycleScheduler,
concurrency: lifecycleConcurrency,
}),
resize: createEnvironmentRpcCommand(runtime, {
label: "environment-data:preview:resize",
tag: WS_METHODS.previewResize,
scheduler: lifecycleScheduler,
concurrency: lifecycleConcurrency,
}),
refresh: createEnvironmentRpcCommand(runtime, {
label: "environment-data:preview:refresh",
tag: WS_METHODS.previewRefresh,
scheduler: lifecycleScheduler,
concurrency: lifecycleConcurrency,
}),
close: createEnvironmentRpcCommand(runtime, {
label: "environment-data:preview:close",
tag: WS_METHODS.previewClose,
scheduler: lifecycleScheduler,
concurrency: lifecycleConcurrency,
}),
reportStatus: createEnvironmentRpcCommand(runtime, {
label: "environment-data:preview:report-status",
tag: WS_METHODS.previewReportStatus,
scheduler: statusScheduler,
concurrency: {
mode: "latest",
key: ({ environmentId, input }) =>
JSON.stringify([environmentId, input.threadId, input.tabId]),
},
}),
respondToAutomation: createEnvironmentRpcCommand(runtime, {
label: "environment-data:preview:automation-respond",
tag: WS_METHODS.previewAutomationRespond,
scheduler: automationScheduler,
concurrency: {
mode: "singleFlight",
key: ({ environmentId, input }) =>
JSON.stringify([environmentId, input.connectionId, input.requestId]),
},
}),
focusAutomationHost: createEnvironmentRpcCommand(runtime, {
label: "environment-data:preview:automation-focus-host",
tag: WS_METHODS.previewAutomationFocusHost,
scheduler: automationScheduler,
concurrency: {
mode: "latest",
key: previewAutomationHostFocusConcurrencyKey,
},
}),
};
}