History

feat: plugin API routes + frontend component registration

OpenAPI plugin now has three sub-path exports: - @executor/plugin-openapi (SDK plugin, unchanged) - @executor/plugin-openapi/api (HTTP routes + handlers) - OpenApiGroup: previewSpec, addSpec, listSpecs, removeSpec endpoints - OpenApiHandlers: Effect HttpApiBuilder handlers - OpenApiExtensionService: Context tag for wiring - @executor/plugin-openapi/react (React components + atoms) - OpenApiClient: typed AtomHttpApi client (core + openapi routes) - Atoms: openApiSpecsAtom, previewOpenApiSpec, addOpenApiSpec, removeOpenApiSpec - openApiSourcePlugin: implements SourcePlugin contract - Lazy-loaded components: AddOpenApiSource (multi-step), EditOpenApiSource, OpenApiSourceSummary Shared infrastructure: - @executor/api: addGroup() composes plugin HttpApiGroups with CoreExecutorApi - @executor/react: SourcePlugin contract type for shell↔plugin UI composition Shell owns layout/nav/common actions, plugin owns content within typed frames Pattern: plugins use addGroup() + AtomHttpApi.Tag to get typed query/mutation atoms for both core and plugin-specific routes. Peer deps with optional metadata so SDK-only consumers don't pull in React. 10 packages typecheck clean, 21 OpenAPI tests passing.

Rhys Sullivan committed 7 months ago

Commit 1ddae2a1 parent f973e3218 files · +416 −6Browse files

Showing the changes to packages/plugins/openapi/src/react (7 of 18 files). Show all

Unified · Split

7 files
  1. packages/plugins/openapi/src/react/AddOpenApiSource.tsx +55 −0
  2. packages/plugins/openapi/src/react/EditOpenApiSource.tsx +13 −0
  3. packages/plugins/openapi/src/react/OpenApiSourceSummary.tsx +9 −0
  4. packages/plugins/openapi/src/react/atoms.ts +35 −0
  5. packages/plugins/openapi/src/react/client.ts +20 −0
  6. packages/plugins/openapi/src/react/index.ts +8 −0
  7. packages/plugins/openapi/src/react/source-plugin.ts +14 −0
packages/plugins/openapi/src/react/AddOpenApiSource.tsx+55−0
@@ -0,0 +1,55 @@
1+import { useState } from "react";
2+
3+export default function AddOpenApiSource(props: {
4+ onComplete: () => void;
5+ onCancel: () => void;
6+}) {
7+ const [step, setStep] = useState<"url" | "auth" | "confirm">("url");
8+ const [specUrl, setSpecUrl] = useState("");
9+
10+ return (
11+ <div>
12+ <h3>Add OpenAPI Source</h3>
13+
14+ {step === "url" && (
15+ <div>
16+ <label>
17+ Spec URL or paste content:
18+ <input
19+ type="text"
20+ value={specUrl}
21+ onChange={(e) => setSpecUrl((e.target as HTMLInputElement).value)}
22+ placeholder="https://api.example.com/openapi.json"
23+ />
24+ </label>
25+ <div>
26+ <button onClick={props.onCancel}>Cancel</button>
27+ <button onClick={() => setStep("auth")} disabled={!specUrl}>
28+ Next
29+ </button>
30+ </div>
31+ </div>
32+ )}
33+
34+ {step === "auth" && (
35+ <div>
36+ <p>Configure authentication headers…</p>
37+ <div>
38+ <button onClick={() => setStep("url")}>Back</button>
39+ <button onClick={() => setStep("confirm")}>Next</button>
40+ </div>
41+ </div>
42+ )}
43+
44+ {step === "confirm" && (
45+ <div>
46+ <p>Ready to add spec from: {specUrl}</p>
47+ <div>
48+ <button onClick={() => setStep("auth")}>Back</button>
49+ <button onClick={props.onComplete}>Add Source</button>
50+ </div>
51+ </div>
52+ )}
53+ </div>
54+ );
55+}
packages/plugins/openapi/src/react/EditOpenApiSource.tsx+13−0
@@ -0,0 +1,13 @@
1+export default function EditOpenApiSource(props: {
2+ sourceId: string;
3+ onSave: () => void;
4+}) {
5+ return (
6+ <div>
7+ <h3>Edit OpenAPI Source</h3>
8+ <p>Source: {props.sourceId}</p>
9+ {/* TODO: show spec info, auth config, operation list */}
10+ <button onClick={props.onSave}>Save</button>
11+ </div>
12+ );
13+}
packages/plugins/openapi/src/react/OpenApiSourceSummary.tsx+9−0
@@ -0,0 +1,9 @@
1+export default function OpenApiSourceSummary(props: {
2+ sourceId: string;
3+}) {
4+ return (
5+ <span>
6+ OpenAPI · {props.sourceId}
7+ </span>
8+ );
9+}
packages/plugins/openapi/src/react/atoms.ts+35−0
@@ -0,0 +1,35 @@
1+import { ScopeId } from "@executor/sdk";
2+
3+import { OpenApiClient } from "./client";
4+
5+// ---------------------------------------------------------------------------
6+// Default scope
7+// ---------------------------------------------------------------------------
8+
9+const DEFAULT_SCOPE = ScopeId.make("default");
10+
11+// ---------------------------------------------------------------------------
12+// Query atoms
13+// ---------------------------------------------------------------------------
14+
15+export const openApiSpecsAtom = (scopeId: ScopeId = DEFAULT_SCOPE) =>
16+ OpenApiClient.query("openapi", "listSpecs", {
17+ path: { scopeId },
18+ timeToLive: "30 seconds",
19+ });
20+
21+// ---------------------------------------------------------------------------
22+// Mutation atoms
23+// ---------------------------------------------------------------------------
24+
25+export const previewOpenApiSpec = OpenApiClient.mutation(
26+ "openapi",
27+ "previewSpec",
28+);
29+
30+export const addOpenApiSpec = OpenApiClient.mutation("openapi", "addSpec");
31+
32+export const removeOpenApiSpec = OpenApiClient.mutation(
33+ "openapi",
34+ "removeSpec",
35+);
packages/plugins/openapi/src/react/client.ts+20−0
@@ -0,0 +1,20 @@
1+import { AtomHttpApi } from "@effect-atom/atom-react";
2+import { FetchHttpClient } from "@effect/platform";
3+import { addGroup } from "@executor/api";
4+import { getBaseUrl } from "@executor/react";
5+import { OpenApiGroup } from "../api/group";
6+
7+// ---------------------------------------------------------------------------
8+// OpenAPI-aware client — core routes + openapi routes
9+// ---------------------------------------------------------------------------
10+
11+const OpenApiApi = addGroup(OpenApiGroup);
12+
13+export const OpenApiClient = AtomHttpApi.Tag<"OpenApiClient">()(
14+ "OpenApiClient",
15+ {
16+ api: OpenApiApi,
17+ httpClient: FetchHttpClient.layer,
18+ baseUrl: getBaseUrl(),
19+ },
20+);
packages/plugins/openapi/src/react/index.ts+8−0
@@ -0,0 +1,8 @@
1+export { openApiSourcePlugin } from "./source-plugin";
2+export { OpenApiClient } from "./client";
3+export {
4+ openApiSpecsAtom,
5+ previewOpenApiSpec,
6+ addOpenApiSpec,
7+ removeOpenApiSpec,
8+} from "./atoms";
packages/plugins/openapi/src/react/source-plugin.ts+14−0
@@ -0,0 +1,14 @@
1+import { lazy } from "react";
2+import type { SourcePlugin } from "@executor/react";
3+
4+// ---------------------------------------------------------------------------
5+// OpenAPI source plugin — lazy-loaded components
6+// ---------------------------------------------------------------------------
7+
8+export const openApiSourcePlugin: SourcePlugin = {
9+ key: "openapi",
10+ label: "OpenAPI",
11+ add: lazy(() => import("./AddOpenApiSource")),
12+ edit: lazy(() => import("./EditOpenApiSource")),
13+ summary: lazy(() => import("./OpenApiSourceSummary")),
14+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t