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.
Unified · Split
| @@ -160,11 +160,28 @@ · 159 unmodified lines | |||
| 160 | "yaml": "^2.7.1", | 160 | "yaml": "^2.7.1", |
| 161 | }, | 161 | }, |
| 162 | "devDependencies": { | 162 | "devDependencies": { |
| 163 | "@effect-atom/atom-react": "^0.5.0", | ||
| 163 | "@effect/vitest": "catalog:", | 164 | "@effect/vitest": "catalog:", |
| 165 | "@executor/api": "workspace:*", | ||
| 166 | "@executor/react": "workspace:*", | ||
| 164 | "@types/node": "catalog:", | 167 | "@types/node": "catalog:", |
| 168 | "@types/react": "^19.1.0", | ||
| 165 | "bun-types": "catalog:", | 169 | "bun-types": "catalog:", |
| 170 | "react": "^19.1.0", | ||
| 166 | "vitest": "catalog:", | 171 | "vitest": "catalog:", |
| 167 | }, | 172 | }, |
| 173 | "peerDependencies": { | ||
| 174 | "@effect-atom/atom-react": "^0.5.0", | ||
| 175 | "@executor/api": "workspace:*", | ||
| 176 | "@executor/react": "workspace:*", | ||
| 177 | "react": "^19.1.0", | ||
| 178 | }, | ||
| 179 | "optionalPeers": [ | ||
| 180 | "@effect-atom/atom-react", | ||
| 181 | "@executor/api", | ||
| 182 | "@executor/react", | ||
| 183 | "react", | ||
| 184 | ], | ||
| 168 | }, | 185 | }, |
| 169 | }, | 186 | }, |
| 170 | "catalog": { | 187 | "catalog": { |
| @@ -5,7 +5,7 @@ import { ExecutorApi } from "@executor/api"; · 4 unmodified lines | |||
| 5 | import { getBaseUrl } from "./base-url"; | 5 | import { getBaseUrl } from "./base-url"; |
| 6 | 6 | ||
| 7 | // --------------------------------------------------------------------------- | 7 | // --------------------------------------------------------------------------- |
| 8 | // Typed HTTP API client — cached per base URL | 8 | // Core API client — tools + secrets |
| 9 | // --------------------------------------------------------------------------- | 9 | // --------------------------------------------------------------------------- |
| 10 | 10 | ||
| 11 | class ExecutorApiClient extends AtomHttpApi.Tag<ExecutorApiClient>()( | 11 | class ExecutorApiClient extends AtomHttpApi.Tag<ExecutorApiClient>()( |
| @@ -32,3 +32,6 @@ export { · 31 unmodified lines | |||
| 32 | 32 | ||
| 33 | // Provider | 33 | // Provider |
| 34 | export { ExecutorProvider } from "./provider"; | 34 | export { ExecutorProvider } from "./provider"; |
| 35 | |||
| 36 | // Plugin contracts | ||
| 37 | export type { SourcePlugin } from "./source-plugin"; | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | import type { ComponentType } from "react"; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * Contract between the shell and source plugins. | ||
| 5 | * | ||
| 6 | * The shell owns: | ||
| 7 | * - Source list page (renders all sources, common delete action, "add" button) | ||
| 8 | * - Source detail chrome (title bar, edit/delete buttons) | ||
| 9 | * - Routing between sources | ||
| 10 | * | ||
| 11 | * The plugin owns: | ||
| 12 | * - Everything inside `add` (could be 1 step or a multi-step wizard) | ||
| 13 | * - Everything inside `edit` (the config view for that source type) | ||
| 14 | * - Optional `summary` for the source list item | ||
| 15 | */ | ||
| 16 | export interface SourcePlugin { | ||
| 17 | /** Unique key matching the SDK plugin key (e.g. "openapi") */ | ||
| 18 | readonly key: string; | ||
| 19 | |||
| 20 | /** Display label (e.g. "OpenAPI", "GraphQL") */ | ||
| 21 | readonly label: string; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * The "add source" flow. | ||
| 25 | * Plugin controls the entire experience. | ||
| 26 | * Call `onComplete` when done, `onCancel` to bail out. | ||
| 27 | */ | ||
| 28 | readonly add: ComponentType<{ | ||
| 29 | readonly onComplete: () => void; | ||
| 30 | readonly onCancel: () => void; | ||
| 31 | }>; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * The source edit/detail view. | ||
| 35 | * Rendered inside the shell's detail chrome. | ||
| 36 | */ | ||
| 37 | readonly edit: ComponentType<{ | ||
| 38 | readonly sourceId: string; | ||
| 39 | readonly onSave: () => void; | ||
| 40 | }>; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Optional summary for the source list item. | ||
| 44 | * If not provided, the shell renders a default. | ||
| 45 | */ | ||
| 46 | readonly summary?: ComponentType<{ | ||
| 47 | readonly sourceId: string; | ||
| 48 | }>; | ||
| 49 | } | ||
| @@ -1,9 +1,10 @@ | |||
| 1 | import { HttpApi, OpenApi } from "@effect/platform"; | 1 | import { HttpApi, OpenApi } from "@effect/platform"; |
| 2 | import type { HttpApiGroup } from "@effect/platform"; | ||
| 2 | 3 | ||
| 3 | import { ToolsApi } from "./tools/api"; | 4 | import { ToolsApi } from "./tools/api"; |
| 4 | import { SecretsApi } from "./secrets/api"; | 5 | import { SecretsApi } from "./secrets/api"; |
| 5 | 6 | ||
| 6 | export const ExecutorApi = HttpApi.make("executor") | 7 | export const CoreExecutorApi = HttpApi.make("executor") |
| 7 | .add(ToolsApi) | 8 | .add(ToolsApi) |
| 8 | .add(SecretsApi) | 9 | .add(SecretsApi) |
| 9 | .annotateContext( | 10 | .annotateContext( |
| @@ -12,3 +13,13 @@ export const ExecutorApi = HttpApi.make("executor") · 2 unmodified lines | |||
| 12 | description: "Tool execution platform API", | 13 | description: "Tool execution platform API", |
| 13 | }), | 14 | }), |
| 14 | ); | 15 | ); |
| 16 | |||
| 17 | /** | ||
| 18 | * Compose the core API with a plugin group. | ||
| 19 | */ | ||
| 20 | export const addGroup = <G extends HttpApiGroup.HttpApiGroup.Any>( | ||
| 21 | group: G, | ||
| 22 | ) => CoreExecutorApi.add(group); | ||
| 23 | |||
| 24 | /** Default API with no plugin groups */ | ||
| 25 | export const ExecutorApi = CoreExecutorApi; | ||
| @@ -1,3 +1,3 @@ | |||
| 1 | export { ExecutorApi } from "./api"; | 1 | export { ExecutorApi, CoreExecutorApi, addGroup } from "./api"; |
| 2 | export { ToolsApi } from "./tools/api"; | 2 | export { ToolsApi } from "./tools/api"; |
| 3 | export { SecretsApi } from "./secrets/api"; | 3 | export { SecretsApi } from "./secrets/api"; |
| @@ -4,7 +4,9 @@ · 3 unmodified lines | |||
| 4 | "type": "module", | 4 | "type": "module", |
| 5 | "version": "0.0.0", | 5 | "version": "0.0.0", |
| 6 | "exports": { | 6 | "exports": { |
| 7 | ".": "./src/index.ts" | 7 | ".": "./src/index.ts", |
| 8 | "./api": "./src/api/index.ts", | ||
| 9 | "./react": "./src/react/index.ts" | ||
| 8 | }, | 10 | }, |
| 9 | "scripts": { | 11 | "scripts": { |
| 10 | "typecheck": "bunx tsc --noEmit -p tsconfig.json", | 12 | "typecheck": "bunx tsc --noEmit -p tsconfig.json", |
| @@ -20,7 +22,24 @@ · 9 unmodified lines | |||
| 20 | "openapi-types": "^12.1.3", | 22 | "openapi-types": "^12.1.3", |
| 21 | "yaml": "^2.7.1" | 23 | "yaml": "^2.7.1" |
| 22 | }, | 24 | }, |
| 25 | "peerDependencies": { | ||
| 26 | "react": "^19.1.0", | ||
| 27 | "@effect-atom/atom-react": "^0.5.0", | ||
| 28 | "@executor/api": "workspace:*", | ||
| 29 | "@executor/react": "workspace:*" | ||
| 30 | }, | ||
| 31 | "peerDependenciesMeta": { | ||
| 32 | "react": { "optional": true }, | ||
| 33 | "@effect-atom/atom-react": { "optional": true }, | ||
| 34 | "@executor/api": { "optional": true }, | ||
| 35 | "@executor/react": { "optional": true } | ||
| 36 | }, | ||
| 23 | "devDependencies": { | 37 | "devDependencies": { |
| 38 | "react": "^19.1.0", | ||
| 39 | "@effect-atom/atom-react": "^0.5.0", | ||
| 40 | "@executor/api": "workspace:*", | ||
| 41 | "@executor/react": "workspace:*", | ||
| 42 | "@types/react": "^19.1.0", | ||
| 24 | "@effect/vitest": "catalog:", | 43 | "@effect/vitest": "catalog:", |
| 25 | "@types/node": "catalog:", | 44 | "@types/node": "catalog:", |
| 26 | "bun-types": "catalog:", | 45 | "bun-types": "catalog:", |
| @@ -0,0 +1,90 @@ | |||
| 1 | import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform"; | ||
| 2 | import { Schema } from "effect"; | ||
| 3 | import { ScopeId } from "@executor/sdk"; | ||
| 4 | |||
| 5 | import { | ||
| 6 | OpenApiParseError, | ||
| 7 | OpenApiExtractionError, | ||
| 8 | } from "../errors"; | ||
| 9 | import { SpecPreview } from "../preview"; | ||
| 10 | |||
| 11 | // --------------------------------------------------------------------------- | ||
| 12 | // Params | ||
| 13 | // --------------------------------------------------------------------------- | ||
| 14 | |||
| 15 | const scopeIdParam = HttpApiSchema.param("scopeId", ScopeId); | ||
| 16 | |||
| 17 | // --------------------------------------------------------------------------- | ||
| 18 | // Payloads | ||
| 19 | // --------------------------------------------------------------------------- | ||
| 20 | |||
| 21 | const AddSpecPayload = Schema.Struct({ | ||
| 22 | spec: Schema.String, | ||
| 23 | baseUrl: Schema.optional(Schema.String), | ||
| 24 | namespace: Schema.optional(Schema.String), | ||
| 25 | headers: Schema.optional( | ||
| 26 | Schema.Record({ key: Schema.String, value: Schema.Unknown }), | ||
| 27 | ), | ||
| 28 | }); | ||
| 29 | |||
| 30 | const PreviewSpecPayload = Schema.Struct({ | ||
| 31 | spec: Schema.String, | ||
| 32 | }); | ||
| 33 | |||
| 34 | // --------------------------------------------------------------------------- | ||
| 35 | // Responses | ||
| 36 | // --------------------------------------------------------------------------- | ||
| 37 | |||
| 38 | const AddSpecResponse = Schema.Struct({ | ||
| 39 | toolCount: Schema.Number, | ||
| 40 | namespace: Schema.String, | ||
| 41 | }); | ||
| 42 | |||
| 43 | const ListSpecsResponse = Schema.Array( | ||
| 44 | Schema.Struct({ | ||
| 45 | namespace: Schema.String, | ||
| 46 | toolCount: Schema.Number, | ||
| 47 | }), | ||
| 48 | ); | ||
| 49 | |||
| 50 | // --------------------------------------------------------------------------- | ||
| 51 | // Errors with HTTP status | ||
| 52 | // --------------------------------------------------------------------------- | ||
| 53 | |||
| 54 | const ParseError = OpenApiParseError.annotations( | ||
| 55 | HttpApiSchema.annotations({ status: 400 }), | ||
| 56 | ); | ||
| 57 | const ExtractionError = OpenApiExtractionError.annotations( | ||
| 58 | HttpApiSchema.annotations({ status: 400 }), | ||
| 59 | ); | ||
| 60 | |||
| 61 | // --------------------------------------------------------------------------- | ||
| 62 | // Group | ||
| 63 | // --------------------------------------------------------------------------- | ||
| 64 | |||
| 65 | const namespaceParam = HttpApiSchema.param("namespace", Schema.String); | ||
| 66 | |||
| 67 | export class OpenApiGroup extends HttpApiGroup.make("openapi") | ||
| 68 | .add( | ||
| 69 | HttpApiEndpoint.post("previewSpec")`/scopes/${scopeIdParam}/openapi/preview` | ||
| 70 | .setPayload(PreviewSpecPayload) | ||
| 71 | .addSuccess(SpecPreview) | ||
| 72 | .addError(ParseError) | ||
| 73 | .addError(ExtractionError), | ||
| 74 | ) | ||
| 75 | .add( | ||
| 76 | HttpApiEndpoint.post("addSpec")`/scopes/${scopeIdParam}/openapi/specs` | ||
| 77 | .setPayload(AddSpecPayload) | ||
| 78 | .addSuccess(AddSpecResponse) | ||
| 79 | .addError(ParseError) | ||
| 80 | .addError(ExtractionError), | ||
| 81 | ) | ||
| 82 | .add( | ||
| 83 | HttpApiEndpoint.get("listSpecs")`/scopes/${scopeIdParam}/openapi/specs` | ||
| 84 | .addSuccess(ListSpecsResponse), | ||
| 85 | ) | ||
| 86 | .add( | ||
| 87 | HttpApiEndpoint.del("removeSpec")`/scopes/${scopeIdParam}/openapi/specs/${namespaceParam}` | ||
| 88 | .addSuccess(Schema.Struct({ removed: Schema.Boolean })), | ||
| 89 | ) | ||
| 90 | .prefix("/v1") {} | ||
| @@ -0,0 +1,63 @@ | |||
| 1 | import { HttpApiBuilder } from "@effect/platform"; | ||
| 2 | import { Context, Effect } from "effect"; | ||
| 3 | |||
| 4 | import { addGroup } from "@executor/api"; | ||
| 5 | import type { OpenApiPluginExtension } from "../plugin"; | ||
| 6 | import { OpenApiGroup } from "./group"; | ||
| 7 | |||
| 8 | // --------------------------------------------------------------------------- | ||
| 9 | // Service tag — the server provides the OpenAPI extension | ||
| 10 | // --------------------------------------------------------------------------- | ||
| 11 | |||
| 12 | export class OpenApiExtensionService extends Context.Tag( | ||
| 13 | "OpenApiExtensionService", | ||
| 14 | )<OpenApiExtensionService, OpenApiPluginExtension>() {} | ||
| 15 | |||
| 16 | // --------------------------------------------------------------------------- | ||
| 17 | // Composed API — core + openapi group | ||
| 18 | // --------------------------------------------------------------------------- | ||
| 19 | |||
| 20 | const ExecutorApiWithOpenApi = addGroup(OpenApiGroup); | ||
| 21 | |||
| 22 | // --------------------------------------------------------------------------- | ||
| 23 | // Handlers | ||
| 24 | // --------------------------------------------------------------------------- | ||
| 25 | |||
| 26 | export const OpenApiHandlers = HttpApiBuilder.group( | ||
| 27 | ExecutorApiWithOpenApi, | ||
| 28 | "openapi", | ||
| 29 | (handlers) => | ||
| 30 | handlers | ||
| 31 | .handle("previewSpec", ({ payload }) => | ||
| 32 | Effect.gen(function* () { | ||
| 33 | const ext = yield* OpenApiExtensionService; | ||
| 34 | return yield* ext.previewSpec(payload.spec); | ||
| 35 | }).pipe(Effect.orDie), | ||
| 36 | ) | ||
| 37 | .handle("addSpec", ({ payload }) => | ||
| 38 | Effect.gen(function* () { | ||
| 39 | const ext = yield* OpenApiExtensionService; | ||
| 40 | const result = yield* ext.addSpec({ | ||
| 41 | spec: payload.spec, | ||
| 42 | baseUrl: payload.baseUrl, | ||
| 43 | namespace: payload.namespace, | ||
| 44 | headers: payload.headers as Record<string, string> | undefined, | ||
| 45 | }); | ||
| 46 | return { | ||
| 47 | toolCount: result.toolCount, | ||
| 48 | namespace: payload.namespace ?? "api", | ||
| 49 | }; | ||
| 50 | }).pipe(Effect.orDie), | ||
| 51 | ) | ||
| 52 | .handle("listSpecs", () => | ||
| 53 | // TODO: wire to operation store listByNamespace | ||
| 54 | Effect.succeed([] as { namespace: string; toolCount: number }[]), | ||
| 55 | ) | ||
| 56 | .handle("removeSpec", ({ path }) => | ||
| 57 | Effect.gen(function* () { | ||
| 58 | const ext = yield* OpenApiExtensionService; | ||
| 59 | yield* ext.removeSpec(path.namespace); | ||
| 60 | return { removed: true }; | ||
| 61 | }), | ||
| 62 | ), | ||
| 63 | ); | ||
| @@ -0,0 +1,2 @@ | |||
| 1 | export { OpenApiGroup } from "./group"; | ||
| 2 | export { OpenApiHandlers, OpenApiExtensionService } from "./handlers"; | ||
| @@ -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 | } | ||
| @@ -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 | } | ||
| @@ -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 | } | ||
| @@ -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 | ); | ||
| @@ -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 | ); | ||
| @@ -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"; | ||
| @@ -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 | }; | ||
| @@ -6,10 +6,11 @@ · 5 unmodified lines | |||
| 6 | "strict": true, | 6 | "strict": true, |
| 7 | "skipLibCheck": true, | 7 | "skipLibCheck": true, |
| 8 | "noEmit": true, | 8 | "noEmit": true, |
| 9 | "lib": ["ES2022"], | 9 | "lib": ["ES2022", "DOM"], |
| 10 | "types": ["bun-types", "node"], | 10 | "types": ["bun-types", "node"], |
| 11 | "noUnusedLocals": true, | 11 | "noUnusedLocals": true, |
| 12 | "noImplicitOverride": true, | 12 | "noImplicitOverride": true, |
| 13 | "jsx": "react-jsx", | ||
| 13 | "plugins": [ | 14 | "plugins": [ |
| 14 | { | 15 | { |
| 15 | "name": "@effect/language-service", | 16 | "name": "@effect/language-service", |
| @@ -20,6 +21,7 @@ · 4 unmodified lines | |||
| 20 | ] | 21 | ] |
| 21 | }, | 22 | }, |
| 22 | "include": [ | 23 | "include": [ |
| 23 | "src/**/*.ts" | 24 | "src/**/*.ts", |
| 25 | "src/**/*.tsx" | ||
| 24 | ] | 26 | ] |
| 25 | } | 27 | } |