Rhys Sullivanfmtc92c57d6 months agoHistory
82 lines · 2.8 KB · TypeScript
RawDownload
// ---------------------------------------------------------------------------// @executor-js/plugin-example/client//// Frontend half: a single page that calls the plugin's `greet` mutation// and renders the response. Demonstrates the typed reactive client// pattern — `ExampleClient.mutation(...)` is fully typed against// `ExampleApi` from `./shared` with no codegen.//// Server-only deps (Effect, Node, executor.config) MUST NOT be imported// here — the Vite plugin bundles this entry into the frontend.// --------------------------------------------------------------------------- import { useState } from "react";import { defineClientPlugin, createPluginAtomClient, useAtomSet } from "@executor-js/sdk/client"; import { ExampleApi } from "./shared"; const ExampleClient = createPluginAtomClient(ExampleApi); const greetAtom = ExampleClient.mutation("example", "greet"); function ExamplePage() {  const [name, setName] = useState("world");  const [result, setResult] = useState<string>();  const doGreet = useAtomSet(greetAtom, { mode: "promise" });   return (    <div style={{ maxWidth: 480, margin: "2rem auto", padding: "1rem" }}>      <h1 style={{ fontSize: "1.25rem", marginBottom: "1rem" }}>Example plugin</h1>      <div style={{ display: "flex", gap: 8 }}>        {/* The example plugin demonstrates the SDK in isolation, so it            uses raw HTML controls instead of `@executor-js/react`'s            wrapped components — third-party plugin authors don't have            to depend on the host's component library. */}        {/* oxlint-disable-next-line react/forbid-elements */}        <input          value={name}          onChange={(e) => setName(e.target.value)}          style={{            flex: 1,            padding: "0.5rem 0.75rem",            border: "1px solid #ccc",            borderRadius: 6,          }}        />        {/* oxlint-disable-next-line react/forbid-elements */}        <button          type="button"          onClick={async () => {            // Mutation has no shared state to invalidate — explicit            // empty `reactivityKeys` documents the intent for the            // `require-reactivity-keys` rule.            const g = await doGreet({ payload: { name }, reactivityKeys: [] });            setResult(`${g.message} (#${g.count})`);          }}          style={{            padding: "0.5rem 0.75rem",            border: "1px solid #ccc",            borderRadius: 6,            cursor: "pointer",          }}        >          Greet        </button>      </div>      {result && (        <pre style={{ marginTop: "1rem", padding: "0.5rem", background: "#f5f5f5" }}>{result}</pre>      )}    </div>  );} export default defineClientPlugin({  id: "example" as const,  pages: [    {      path: "/",      component: ExamplePage,      nav: { label: "Example" },    },  ],});