mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-27 19:52:49 +00:00
Give each observation a server-issued ID owned by its physical connection. Preserve snapshot-only reads and combined list-and-subscribe, source-bound replies, lifecycle cleanup and legacy wire compatibility.
45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import type { InstalledPlugin } from "./types";
|
|
import { createPaseoApi, type PaseoApi } from "@getpaseo/client";
|
|
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
|
|
|
export interface PluginSurfaceRuntime {
|
|
paseo: PaseoApi;
|
|
invoke(method: string, input: unknown): Promise<unknown>;
|
|
}
|
|
|
|
export function createPluginSurfaceRuntime(
|
|
client: DaemonClient | null,
|
|
plugin: Pick<InstalledPlugin, "id" | "lifetime">,
|
|
): PluginSurfaceRuntime | null {
|
|
if (!client || plugin.lifetime.signal.aborted) return null;
|
|
return {
|
|
paseo: createPaseoApi(client, { signal: plugin.lifetime.signal }),
|
|
invoke: (method, input) => client.invokePluginRpc(plugin.id, method, input),
|
|
};
|
|
}
|
|
|
|
/** A mounted surface owns its API; creating a React element creates no server demand. */
|
|
export function usePluginSurfaceRuntime(
|
|
client: DaemonClient | null,
|
|
plugin: InstalledPlugin | null | undefined,
|
|
): PluginSurfaceRuntime | null {
|
|
const [mounted, setMounted] = useState<{
|
|
client: DaemonClient;
|
|
plugin: InstalledPlugin;
|
|
runtime: PluginSurfaceRuntime;
|
|
} | null>(null);
|
|
useEffect(() => {
|
|
if (!client || !plugin) return;
|
|
const runtime = createPluginSurfaceRuntime(client, plugin);
|
|
if (!runtime) return;
|
|
setMounted({ client, plugin, runtime });
|
|
return () => {
|
|
void runtime.paseo
|
|
.dispose()
|
|
.catch((error) => console.warn(`[Plugins] Surface cleanup failed for ${plugin.id}`, error));
|
|
};
|
|
}, [client, plugin]);
|
|
return mounted?.client === client && mounted.plugin === plugin ? mounted.runtime : null;
|
|
}
|