@executor-js/plugin-openapi 1.5.15 → 1.5.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/AddOpenApiSource-U7AYB224.js +369 -0
  2. package/dist/AddOpenApiSource-U7AYB224.js.map +1 -0
  3. package/dist/{OpenApiAccountsPanel-X5Z5XKRM.js → OpenApiAccountsPanel-GHFHHE6P.js} +15 -39
  4. package/dist/OpenApiAccountsPanel-GHFHHE6P.js.map +1 -0
  5. package/dist/{UpdateSpecSection-Z2DEEWZW.js → UpdateSpecSection-PLCBUU4I.js} +4 -4
  6. package/dist/UpdateSpecSection-PLCBUU4I.js.map +1 -0
  7. package/dist/api/group.d.ts +0 -21
  8. package/dist/api/index.d.ts +0 -21
  9. package/dist/chunk-CKBX4SXK.js +95 -0
  10. package/dist/chunk-CKBX4SXK.js.map +1 -0
  11. package/dist/{chunk-NJ4Q3VF4.js → chunk-CPPTKUOW.js} +5 -14
  12. package/dist/chunk-CPPTKUOW.js.map +1 -0
  13. package/dist/{chunk-O54VFSWE.js → chunk-KVPUDOJZ.js} +18 -4
  14. package/dist/chunk-KVPUDOJZ.js.map +1 -0
  15. package/dist/{chunk-PAHWRRS3.js → chunk-QQFCICLX.js} +4 -13
  16. package/dist/chunk-QQFCICLX.js.map +1 -0
  17. package/dist/{chunk-ZTOOUP67.js → chunk-UEKOP6NZ.js} +544 -580
  18. package/dist/chunk-UEKOP6NZ.js.map +1 -0
  19. package/dist/client.js +4 -5
  20. package/dist/client.js.map +1 -1
  21. package/dist/core.js +25 -15
  22. package/dist/core.js.map +1 -1
  23. package/dist/index.js +4 -5
  24. package/dist/index.js.map +1 -1
  25. package/dist/react/atoms.d.ts +0 -22
  26. package/dist/react/client.d.ts +0 -21
  27. package/dist/react/index.d.ts +2 -0
  28. package/dist/sdk/backing.d.ts +54 -0
  29. package/dist/sdk/config.d.ts +0 -2
  30. package/dist/sdk/derive-auth.d.ts +5 -3
  31. package/dist/sdk/index.d.ts +2 -1
  32. package/dist/sdk/plugin.d.ts +4 -10
  33. package/dist/sdk/presets.d.ts +0 -1
  34. package/dist/sdk/preview.d.ts +79 -0
  35. package/dist/testing/index.d.ts +0 -6
  36. package/package.json +6 -5
  37. package/dist/AddOpenApiSource-IRMLVLDK.js +0 -765
  38. package/dist/AddOpenApiSource-IRMLVLDK.js.map +0 -1
  39. package/dist/OpenApiAccountsPanel-X5Z5XKRM.js.map +0 -1
  40. package/dist/UpdateSpecSection-Z2DEEWZW.js.map +0 -1
  41. package/dist/chunk-MZWZQ24W.js +0 -226
  42. package/dist/chunk-MZWZQ24W.js.map +0 -1
  43. package/dist/chunk-NJ4Q3VF4.js.map +0 -1
  44. package/dist/chunk-O54VFSWE.js.map +0 -1
  45. package/dist/chunk-PAHWRRS3.js.map +0 -1
  46. package/dist/chunk-UOLBAX5D.js +0 -712
  47. package/dist/chunk-UOLBAX5D.js.map +0 -1
  48. package/dist/chunk-ZTOOUP67.js.map +0 -1
  49. package/dist/react/GoogleProductPicker.d.ts +0 -9
  50. package/dist/sdk/google-discovery.d.ts +0 -43
  51. package/dist/sdk/google-oauth-batches.d.ts +0 -13
  52. package/dist/sdk/google-oauth-batches.test.d.ts +0 -1
  53. package/dist/sdk/google-oauth-scopes.d.ts +0 -3
  54. package/dist/sdk/google-oauth-scopes.test.d.ts +0 -1
  55. package/dist/sdk/google-presets.d.ts +0 -16
  56. package/dist/sdk/google-presets.test.d.ts +0 -1
  57. package/dist/sdk/google-product-picker-scopes.test.d.ts +0 -1
  58. /package/dist/sdk/{google-bundle.test.d.ts → request-user-agent.test.d.ts} +0 -0
  59. /package/dist/sdk/{google-discovery.test.d.ts → store.test.d.ts} +0 -0
@@ -0,0 +1,369 @@
1
+ import {
2
+ detectedAuthenticationTemplates
3
+ } from "./chunk-CKBX4SXK.js";
4
+ import {
5
+ openApiPresets
6
+ } from "./chunk-QQFCICLX.js";
7
+ import {
8
+ authenticationFromEditorValue,
9
+ editorValueFromAuthentication,
10
+ openApiWireAuthInput
11
+ } from "./chunk-RCBR3QMJ.js";
12
+ import {
13
+ addOpenApiSpec,
14
+ previewOpenApiSpec
15
+ } from "./chunk-CPPTKUOW.js";
16
+ import {
17
+ resolveServerUrl
18
+ } from "./chunk-KVPUDOJZ.js";
19
+
20
+ // src/react/AddOpenApiSource.tsx
21
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
22
+ import { useAtomSet } from "@effect/atom-react";
23
+ import * as Effect from "effect/Effect";
24
+ import * as Exit from "effect/Exit";
25
+ import * as Option from "effect/Option";
26
+ import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
27
+ import {
28
+ slugifyNamespace,
29
+ useIntegrationIdentity
30
+ } from "@executor-js/react/plugins/integration-identity";
31
+ import { Button } from "@executor-js/react/components/button";
32
+ import {
33
+ AuthMethodListEditor,
34
+ useAuthMethodList
35
+ } from "@executor-js/react/components/auth-method-list-editor";
36
+ import { CardStack as CardStack2, CardStackContent as CardStackContent2 } from "@executor-js/react/components/card-stack";
37
+ import { FieldLabel } from "@executor-js/react/components/field";
38
+ import { FloatActions } from "@executor-js/react/components/float-actions";
39
+ import { Textarea as Textarea2 } from "@executor-js/react/components/textarea";
40
+ import { IOSSpinner, Spinner } from "@executor-js/react/components/spinner";
41
+ import {
42
+ addIntegrationErrorMessage,
43
+ errorMessageFromExit,
44
+ FormErrorAlert,
45
+ SlugCollisionAlert,
46
+ useSlugAlreadyExists
47
+ } from "@executor-js/react/lib/integration-add";
48
+
49
+ // src/react/OpenApiSourceDetailsFields.tsx
50
+ import {
51
+ CardStack,
52
+ CardStackContent,
53
+ CardStackEntry,
54
+ CardStackEntryContent,
55
+ CardStackEntryDescription,
56
+ CardStackEntryField,
57
+ CardStackEntryTitle
58
+ } from "@executor-js/react/components/card-stack";
59
+ import {
60
+ FreeformCombobox
61
+ } from "@executor-js/react/components/combobox";
62
+ import { Input } from "@executor-js/react/components/input";
63
+ import { Textarea } from "@executor-js/react/components/textarea";
64
+ import { IntegrationFavicon } from "@executor-js/react/components/integration-favicon";
65
+ import {
66
+ IntegrationIdentityFieldRows
67
+ } from "@executor-js/react/plugins/integration-identity";
68
+ import { jsx, jsxs } from "react/jsx-runtime";
69
+ var isUrlInput = (value) => URL.canParse(value.trim());
70
+ function OpenApiSourceDetailsFields(props) {
71
+ const specIsUrl = props.specUrl !== void 0 && isUrlInput(props.specUrl);
72
+ return /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsxs(CardStackContent, { className: "border-t-0", children: [
73
+ /* @__PURE__ */ jsxs(CardStackEntry, { children: [
74
+ (props.faviconIcon || props.faviconUrl) && /* @__PURE__ */ jsx(IntegrationFavicon, { icon: props.faviconIcon, url: props.faviconUrl, size: 16 }),
75
+ /* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
76
+ /* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.title }),
77
+ props.subtitle && /* @__PURE__ */ jsx(CardStackEntryDescription, { children: props.subtitle })
78
+ ] }),
79
+ props.saveState && props.saveState !== "idle" && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: props.saveState === "saving" ? "Saving\u2026" : "Saved" })
80
+ ] }),
81
+ /* @__PURE__ */ jsx(
82
+ IntegrationIdentityFieldRows,
83
+ {
84
+ identity: props.identity,
85
+ namespaceReadOnly: props.namespaceReadOnly
86
+ }
87
+ ),
88
+ props.onDescriptionChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Description", children: /* @__PURE__ */ jsx(
89
+ Textarea,
90
+ {
91
+ value: props.description ?? "",
92
+ onChange: (e) => props.onDescriptionChange?.(e.target.value),
93
+ placeholder: "What this API is and when to reach for it",
94
+ rows: 2,
95
+ maxRows: 6,
96
+ className: "text-sm"
97
+ }
98
+ ) }),
99
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2", children: [
100
+ /* @__PURE__ */ jsxs(CardStackEntryField, { label: props.baseUrlLabel ?? "Base URL", children: [
101
+ props.baseUrlOptions && props.baseUrlOptions.length > 0 ? /* @__PURE__ */ jsx(
102
+ FreeformCombobox,
103
+ {
104
+ value: props.baseUrl,
105
+ onValueChange: props.onBaseUrlChange,
106
+ options: props.baseUrlOptions,
107
+ placeholder: props.baseUrlPlaceholder ?? "https://api.example.com",
108
+ className: "w-full",
109
+ inputClassName: "font-mono text-sm"
110
+ }
111
+ ) : /* @__PURE__ */ jsx(
112
+ Input,
113
+ {
114
+ value: props.baseUrl,
115
+ onChange: (e) => props.onBaseUrlChange(e.target.value),
116
+ placeholder: props.baseUrlPlaceholder ?? "https://api.example.com",
117
+ className: "font-mono text-sm"
118
+ }
119
+ ),
120
+ props.baseUrlMissingMessage && !props.baseUrl && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-amber-600 dark:text-amber-400", children: props.baseUrlMissingMessage }),
121
+ props.baseUrlHint && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: props.baseUrlHint })
122
+ ] }),
123
+ specIsUrl && props.onSpecUrlChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Spec URL", children: /* @__PURE__ */ jsx(
124
+ Input,
125
+ {
126
+ value: props.specUrl,
127
+ onChange: (e) => props.onSpecUrlChange?.(e.target.value),
128
+ placeholder: "https://api.example.com/openapi.json",
129
+ className: "font-mono text-sm",
130
+ disabled: props.specUrlDisabled
131
+ }
132
+ ) })
133
+ ] }),
134
+ props.specUrl !== void 0 && !specIsUrl && props.onSpecUrlChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Spec", children: /* @__PURE__ */ jsx(
135
+ Textarea,
136
+ {
137
+ value: props.specUrl,
138
+ onChange: (e) => props.onSpecUrlChange?.(e.target.value),
139
+ placeholder: "Pasted OpenAPI JSON/YAML",
140
+ rows: 4,
141
+ maxRows: 12,
142
+ className: "font-mono text-xs",
143
+ disabled: props.specUrlDisabled
144
+ }
145
+ ) }),
146
+ props.footer && /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsx(CardStackEntryContent, { children: /* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.footer }) }) })
147
+ ] }) });
148
+ }
149
+
150
+ // src/react/AddOpenApiSource.tsx
151
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
152
+ var normalizePresetUrl = (url) => {
153
+ const trimmed = url.trim();
154
+ if (!URL.canParse(trimmed)) return trimmed.replace(/\/$/, "");
155
+ const parsed = new URL(trimmed);
156
+ parsed.hash = "";
157
+ parsed.searchParams.sort();
158
+ return parsed.toString().replace(/\/$/, "");
159
+ };
160
+ var specInputForAdd = (input) => {
161
+ const value = input.trim();
162
+ const parsed = Effect.runSyncExit(
163
+ Effect.try({
164
+ try: () => new URL(value),
165
+ catch: () => null
166
+ })
167
+ );
168
+ return Exit.isSuccess(parsed) ? { kind: "url", url: value } : { kind: "blob", value };
169
+ };
170
+ function AddOpenApiSource(props) {
171
+ const [specUrl, setSpecUrl] = useState(props.initialUrl ?? "");
172
+ const [analyzing, setAnalyzing] = useState(false);
173
+ const [analyzeError, setAnalyzeError] = useState(null);
174
+ const [preview, setPreview] = useState(null);
175
+ const [baseUrl, setBaseUrl] = useState("");
176
+ const [descriptionDraft, setDescriptionDraft] = useState(null);
177
+ const identityFallbackName = preview ? Option.getOrElse(preview.title, () => "") : "";
178
+ const identity = useIntegrationIdentity({
179
+ fallbackName: identityFallbackName,
180
+ fallbackNamespace: props.initialNamespace
181
+ });
182
+ const [adding, setAdding] = useState(false);
183
+ const [addError, setAddError] = useState(null);
184
+ const doPreview = useAtomSet(previewOpenApiSpec, { mode: "promiseExit" });
185
+ const doAdd = useAtomSet(addOpenApiSpec, { mode: "promiseExit" });
186
+ const handleAnalyzeRef = useRef(() => {
187
+ });
188
+ useEffect(() => {
189
+ const trimmed = specUrl.trim();
190
+ if (!trimmed) return;
191
+ if (preview) return;
192
+ const handle = setTimeout(() => {
193
+ handleAnalyzeRef.current();
194
+ }, 400);
195
+ return () => clearTimeout(handle);
196
+ }, [specUrl, preview]);
197
+ const previewHasNoServers = preview !== null && preview.servers.length === 0;
198
+ const baseUrlOptions = preview && preview.servers.length > 1 ? preview.servers.map((server) => {
199
+ const url = resolveServerUrl(server.url, Option.getOrUndefined(server.variables), {});
200
+ return { value: url, label: url };
201
+ }) : void 0;
202
+ const firstServer = preview?.servers[0];
203
+ const firstServerUrl = firstServer ? resolveServerUrl(firstServer.url, Option.getOrUndefined(firstServer.variables), {}) : "";
204
+ const previewPresetIcon = openApiPresets.find(
205
+ (preset) => preset.url && normalizePresetUrl(preset.url) === normalizePresetUrl(specUrl)
206
+ )?.icon ?? null;
207
+ const resolvedBaseUrl = baseUrl.trim();
208
+ const resolvedSourceId = slugifyNamespace(identity.namespace) || (preview ? Option.getOrElse(preview.title, () => "openapi") : "openapi");
209
+ const resolvedDisplayName = identity.name.trim() || (preview ? Option.getOrElse(preview.title, () => resolvedSourceId) : resolvedSourceId);
210
+ const resolvedDescription = descriptionDraft ?? (preview ? Option.getOrElse(preview.description, () => "") : "");
211
+ const authenticationTemplate = useMemo(
212
+ () => detectedAuthenticationTemplates(
213
+ preview?.headerPresets ?? [],
214
+ preview?.oauth2Presets ?? [],
215
+ resolvedBaseUrl
216
+ ),
217
+ [preview, resolvedBaseUrl]
218
+ );
219
+ const authMethodSeeds = useMemo(() => {
220
+ const labels = [
221
+ ...(preview?.headerPresets ?? []).map((preset) => preset.label),
222
+ ...(preview?.oauth2Presets ?? []).map((preset) => preset.label)
223
+ ];
224
+ return authenticationTemplate.map(
225
+ (template, index) => ({
226
+ value: editorValueFromAuthentication(template),
227
+ slug: String(template.slug),
228
+ ...labels[index] !== void 0 ? { label: labels[index] } : {}
229
+ })
230
+ );
231
+ }, [preview, authenticationTemplate]);
232
+ const authMethodList = useAuthMethodList(authMethodSeeds);
233
+ const editedAuthenticationTemplate = useMemo(() => {
234
+ const templates = [];
235
+ authMethodList.rows.forEach((row, index) => {
236
+ const slug = row.seedSlug ?? (row.value.kind === "oauth" ? `oauth-${index}` : `apikey-${index}`);
237
+ const template = authenticationFromEditorValue(row.value, slug);
238
+ if (template !== null) templates.push(template);
239
+ });
240
+ return templates;
241
+ }, [authMethodList.rows]);
242
+ const slugAlreadyExists = useSlugAlreadyExists(resolvedSourceId);
243
+ const canAdd = preview !== null && !slugAlreadyExists && (!previewHasNoServers || resolvedBaseUrl.length > 0);
244
+ const handleAnalyze = async () => {
245
+ setAnalyzing(true);
246
+ setAnalyzeError(null);
247
+ setAddError(null);
248
+ const exit = await doPreview({ payload: { spec: specUrl } });
249
+ if (Exit.isFailure(exit)) {
250
+ setAnalyzeError(errorMessageFromExit(exit, "Failed to parse spec"));
251
+ setAnalyzing(false);
252
+ return;
253
+ }
254
+ const result = exit.value;
255
+ setPreview(result);
256
+ setBaseUrl("");
257
+ setAnalyzing(false);
258
+ };
259
+ handleAnalyzeRef.current = handleAnalyze;
260
+ const ensureIntegration = useCallback(async () => {
261
+ const exit = await doAdd({
262
+ payload: {
263
+ spec: specInputForAdd(specUrl),
264
+ slug: resolvedSourceId,
265
+ name: resolvedDisplayName,
266
+ ...resolvedDescription.trim().length > 0 ? { description: resolvedDescription.trim() } : {},
267
+ baseUrl: resolvedBaseUrl,
268
+ // Always send the edited method list (even empty) when the user has
269
+ // inspected a preview: an explicit [] means "no auth methods", while
270
+ // OMITTING the field tells the server to derive defaults from the
271
+ // spec, which would silently resurrect methods the user deleted.
272
+ // Serialize to the wire input dialect (apikey -> request-shaped).
273
+ authenticationTemplate: editedAuthenticationTemplate.map(openApiWireAuthInput)
274
+ },
275
+ reactivityKeys: integrationWriteKeys
276
+ });
277
+ if (Exit.isFailure(exit)) {
278
+ setAddError(addIntegrationErrorMessage(exit, resolvedSourceId, "Failed to add integration"));
279
+ return null;
280
+ }
281
+ return exit.value.slug;
282
+ }, [
283
+ specUrl,
284
+ doAdd,
285
+ resolvedSourceId,
286
+ resolvedDisplayName,
287
+ resolvedDescription,
288
+ resolvedBaseUrl,
289
+ editedAuthenticationTemplate
290
+ ]);
291
+ const handleAdd = async () => {
292
+ setAdding(true);
293
+ setAddError(null);
294
+ const integration = await ensureIntegration();
295
+ if (!integration) {
296
+ setAdding(false);
297
+ return;
298
+ }
299
+ props.onComplete(String(integration));
300
+ };
301
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-6", children: [
302
+ /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2("h1", { className: "text-xl font-semibold text-foreground", children: "Add OpenAPI Integration" }) }),
303
+ !preview ? /* @__PURE__ */ jsx2(CardStack2, { children: /* @__PURE__ */ jsx2(CardStackContent2, { className: "border-t-0", children: /* @__PURE__ */ jsxs2("div", { className: "space-y-2 p-3", children: [
304
+ /* @__PURE__ */ jsx2(FieldLabel, { children: "OpenAPI Spec" }),
305
+ /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
306
+ /* @__PURE__ */ jsx2(
307
+ Textarea2,
308
+ {
309
+ value: specUrl,
310
+ onChange: (e) => setSpecUrl(e.target.value),
311
+ placeholder: "https://api.example.com/openapi.json",
312
+ rows: 3,
313
+ maxRows: 10,
314
+ className: "font-mono text-sm"
315
+ }
316
+ ),
317
+ analyzing && /* @__PURE__ */ jsx2("div", { className: "pointer-events-none absolute right-2 top-2", children: /* @__PURE__ */ jsx2(IOSSpinner, { className: "size-4" }) })
318
+ ] }),
319
+ /* @__PURE__ */ jsx2("p", { className: "text-[11px] text-muted-foreground", children: "Paste a URL or raw JSON/YAML content." })
320
+ ] }) }) }) : null,
321
+ preview ? /* @__PURE__ */ jsx2(
322
+ OpenApiSourceDetailsFields,
323
+ {
324
+ title: Option.getOrElse(preview.title, () => "API"),
325
+ subtitle: `${Option.getOrElse(preview.version, () => "")}${Option.isSome(preview.version) ? " \xB7 " : ""}${preview.operationCount} operation${preview.operationCount !== 1 ? "s" : ""}${preview.tags.length > 0 ? ` \xB7 ${preview.tags.length} tag${preview.tags.length !== 1 ? "s" : ""}` : ""}`,
326
+ identity,
327
+ description: resolvedDescription,
328
+ onDescriptionChange: setDescriptionDraft,
329
+ baseUrl: resolvedBaseUrl,
330
+ onBaseUrlChange: setBaseUrl,
331
+ baseUrlOptions,
332
+ baseUrlLabel: previewHasNoServers ? "Base URL" : "Base URL override (optional)",
333
+ baseUrlPlaceholder: firstServerUrl || "https://api.example.com",
334
+ baseUrlHint: previewHasNoServers ? void 0 : "Overrides the spec's servers; leave empty to choose the server (and variables) per tool call.",
335
+ baseUrlMissingMessage: previewHasNoServers ? "This spec declares no servers, enter a base URL." : void 0,
336
+ specUrl,
337
+ onSpecUrlChange: (value) => {
338
+ setSpecUrl(value);
339
+ setPreview(null);
340
+ setBaseUrl("");
341
+ },
342
+ faviconIcon: previewPresetIcon,
343
+ faviconUrl: resolvedBaseUrl || firstServerUrl
344
+ }
345
+ ) : null,
346
+ analyzeError && /* @__PURE__ */ jsx2(FormErrorAlert, { message: analyzeError }),
347
+ preview && /* @__PURE__ */ jsx2(
348
+ AuthMethodListEditor,
349
+ {
350
+ list: authMethodList,
351
+ emptyHint: "No authentication detected. Add a method, or add the integration without auth and connect an account from the integration page later.",
352
+ footerHint: "Every method here is registered with the integration. Connect an account from the integration page after adding."
353
+ }
354
+ ),
355
+ preview && slugAlreadyExists && !adding && /* @__PURE__ */ jsx2(SlugCollisionAlert, { slug: resolvedSourceId }),
356
+ addError && /* @__PURE__ */ jsx2(FormErrorAlert, { message: addError }),
357
+ /* @__PURE__ */ jsxs2(FloatActions, { children: [
358
+ /* @__PURE__ */ jsx2(Button, { variant: "ghost", onClick: () => props.onCancel(), disabled: adding, children: "Cancel" }),
359
+ preview && /* @__PURE__ */ jsxs2(Button, { onClick: () => void handleAdd(), disabled: !canAdd || adding, children: [
360
+ adding && /* @__PURE__ */ jsx2(Spinner, { className: "size-3.5" }),
361
+ adding ? "Adding..." : "Add integration"
362
+ ] })
363
+ ] })
364
+ ] });
365
+ }
366
+ export {
367
+ AddOpenApiSource as default
368
+ };
369
+ //# sourceMappingURL=AddOpenApiSource-U7AYB224.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/AddOpenApiSource.tsx","../src/react/OpenApiSourceDetailsFields.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAtomSet } from \"@effect/atom-react\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as Option from \"effect/Option\";\n\nimport { IntegrationSlug } from \"@executor-js/sdk/shared\";\nimport { integrationWriteKeys } from \"@executor-js/react/api/reactivity-keys\";\nimport {\n slugifyNamespace,\n useIntegrationIdentity,\n} from \"@executor-js/react/plugins/integration-identity\";\nimport { Button } from \"@executor-js/react/components/button\";\nimport {\n AuthMethodListEditor,\n useAuthMethodList,\n type AuthMethodRow,\n type AuthMethodSeed,\n} from \"@executor-js/react/components/auth-method-list-editor\";\nimport { CardStack, CardStackContent } from \"@executor-js/react/components/card-stack\";\nimport { FieldLabel } from \"@executor-js/react/components/field\";\nimport { FloatActions } from \"@executor-js/react/components/float-actions\";\nimport { Textarea } from \"@executor-js/react/components/textarea\";\nimport { IOSSpinner, Spinner } from \"@executor-js/react/components/spinner\";\nimport {\n addIntegrationErrorMessage,\n errorMessageFromExit,\n FormErrorAlert,\n SlugCollisionAlert,\n useSlugAlreadyExists,\n} from \"@executor-js/react/lib/integration-add\";\n\nimport {\n authenticationFromEditorValue,\n editorValueFromAuthentication,\n openApiWireAuthInput,\n} from \"./auth-method-config\";\nimport { addOpenApiSpec, previewOpenApiSpec } from \"./atoms\";\nimport { OpenApiSourceDetailsFields } from \"./OpenApiSourceDetailsFields\";\nimport { openApiPresets } from \"../sdk/presets\";\nimport type { SpecPreviewSummary } from \"../sdk/preview\";\nimport { type Authentication } from \"../sdk/types\";\nimport { resolveServerUrl } from \"../sdk/openapi-utils\";\nimport { detectedAuthenticationTemplates } from \"../sdk/derive-auth\";\n\nconst normalizePresetUrl = (url: string): string => {\n const trimmed = url.trim();\n if (!URL.canParse(trimmed)) return trimmed.replace(/\\/$/, \"\");\n const parsed = new URL(trimmed);\n parsed.hash = \"\";\n parsed.searchParams.sort();\n return parsed.toString().replace(/\\/$/, \"\");\n};\n\nconst specInputForAdd = (input: string) => {\n const value = input.trim();\n const parsed = Effect.runSyncExit(\n Effect.try({\n try: () => new URL(value),\n catch: () => null,\n }),\n );\n return Exit.isSuccess(parsed)\n ? { kind: \"url\" as const, url: value }\n : { kind: \"blob\" as const, value };\n};\n\n// ---------------------------------------------------------------------------\n// Component: single progressive form. Post-redesign: preview -> addSpec\n// (register the integration catalog entry with ALL detected auth methods) →\n// route to the integration's detail hub, where the user adds accounts. The add\n// flow no longer creates a connection.\n// ---------------------------------------------------------------------------\n\nexport default function AddOpenApiSource(props: {\n onComplete: (slug?: string) => void;\n onCancel: () => void;\n initialUrl?: string;\n initialPreset?: string;\n initialNamespace?: string;\n}) {\n const [specUrl, setSpecUrl] = useState(props.initialUrl ?? \"\");\n const [analyzing, setAnalyzing] = useState(false);\n const [analyzeError, setAnalyzeError] = useState<string | null>(null);\n\n // After analysis\n const [preview, setPreview] = useState<SpecPreviewSummary | null>(null);\n const [baseUrl, setBaseUrl] = useState(\"\");\n // Agent-visible description: prefilled from the spec's `info.description`\n // until the user types (null = untouched, keep deriving from the preview).\n const [descriptionDraft, setDescriptionDraft] = useState<string | null>(null);\n const identityFallbackName = preview ? Option.getOrElse(preview.title, () => \"\") : \"\";\n const identity = useIntegrationIdentity({\n fallbackName: identityFallbackName,\n fallbackNamespace: props.initialNamespace,\n });\n\n // Submit\n const [adding, setAdding] = useState(false);\n const [addError, setAddError] = useState<string | null>(null);\n\n const doPreview = useAtomSet(previewOpenApiSpec, { mode: \"promiseExit\" });\n const doAdd = useAtomSet(addOpenApiSpec, { mode: \"promiseExit\" });\n\n // Keep the latest handleAnalyze in a ref so the debounced effect doesn't need\n // it as a dependency (it closes over fresh state).\n const handleAnalyzeRef = useRef<() => void>(() => {});\n\n useEffect(() => {\n const trimmed = specUrl.trim();\n if (!trimmed) return;\n if (preview) return;\n const handle = setTimeout(() => {\n handleAnalyzeRef.current();\n }, 400);\n return () => clearTimeout(handle);\n }, [specUrl, preview]);\n\n // ---- Derived state ----\n\n const previewHasNoServers = preview !== null && preview.servers.length === 0;\n // Offer the spec's servers (resolved with defaults) as base-URL choices when\n // there's more than one; a single/no server uses a plain input.\n const baseUrlOptions =\n preview && preview.servers.length > 1\n ? preview.servers.map((server) => {\n const url = resolveServerUrl(server.url, Option.getOrUndefined(server.variables), {});\n return { value: url, label: url };\n })\n : undefined;\n const firstServer = preview?.servers[0];\n const firstServerUrl = firstServer\n ? resolveServerUrl(firstServer.url, Option.getOrUndefined(firstServer.variables), {})\n : \"\";\n const previewPresetIcon =\n openApiPresets.find(\n (preset) => preset.url && normalizePresetUrl(preset.url) === normalizePresetUrl(specUrl),\n )?.icon ?? null;\n\n const resolvedBaseUrl = baseUrl.trim();\n const resolvedSourceId =\n slugifyNamespace(identity.namespace) ||\n (preview ? Option.getOrElse(preview.title, () => \"openapi\") : \"openapi\");\n const resolvedDisplayName =\n identity.name.trim() ||\n (preview ? Option.getOrElse(preview.title, () => resolvedSourceId) : resolvedSourceId);\n const resolvedDescription =\n descriptionDraft ?? (preview ? Option.getOrElse(preview.description, () => \"\") : \"\");\n\n // Register EVERY spec-detected auth method, not just a single selected one.\n // Keyed off `preview` (stable per analysis) so the memo doesn't re-run on the\n // freshly-allocated `?? []` fallback arrays.\n const authenticationTemplate: readonly Authentication[] = useMemo(\n () =>\n detectedAuthenticationTemplates(\n preview?.headerPresets ?? [],\n preview?.oauth2Presets ?? [],\n resolvedBaseUrl,\n ),\n [preview, resolvedBaseUrl],\n );\n\n // Editable auth methods, seeded from the spec-detected templates. The add flow\n // registers EVERY method (P6), so this is a LIST, preserving multi-method\n // specs (e.g. apiKey + OAuth). Each seed carries the detected template's\n // original slug, so an unedited detected method submits with its EXACT\n // original slug (preserving behavior); added methods (no seed) get a\n // deterministic fresh slug. Re-seeded whenever a fresh detection arrives\n // (keyed on the detected templates, stable per analysis + base URL).\n const authMethodSeeds: readonly AuthMethodSeed[] = useMemo(() => {\n const labels = [\n ...(preview?.headerPresets ?? []).map((preset) => preset.label),\n ...(preview?.oauth2Presets ?? []).map((preset) => preset.label),\n ];\n return authenticationTemplate.map(\n (template: Authentication, index: number): AuthMethodSeed => ({\n value: editorValueFromAuthentication(template),\n slug: String(template.slug),\n ...(labels[index] !== undefined ? { label: labels[index] } : {}),\n }),\n );\n }, [preview, authenticationTemplate]);\n const authMethodList = useAuthMethodList(authMethodSeeds);\n\n // The methods to register, mapped back to stored `Authentication[]`. Drops\n // `none` rows (nothing to register). An unedited detected method keeps its\n // original `seedSlug`; an added method gets a deterministic fresh slug.\n const editedAuthenticationTemplate: readonly Authentication[] = useMemo(() => {\n const templates: Authentication[] = [];\n authMethodList.rows.forEach((row: AuthMethodRow, index: number) => {\n const slug =\n row.seedSlug ?? (row.value.kind === \"oauth\" ? `oauth-${index}` : `apikey-${index}`);\n const template = authenticationFromEditorValue(row.value, slug);\n if (template !== null) templates.push(template);\n });\n return templates;\n }, [authMethodList.rows]);\n\n // Pre-empt the API's `IntegrationAlreadyExistsError`: adding an integration\n // whose slug already exists clobbers the existing one's connections/policies,\n // so the API blocks it. Surface that here from the tenant-scoped catalog list.\n const slugAlreadyExists = useSlugAlreadyExists(resolvedSourceId);\n\n // The base URL is optional when the spec declares servers (resolved per call);\n // required only when it doesn't.\n const canAdd =\n preview !== null && !slugAlreadyExists && (!previewHasNoServers || resolvedBaseUrl.length > 0);\n\n // ---- Handlers ----\n\n const handleAnalyze = async () => {\n setAnalyzing(true);\n setAnalyzeError(null);\n setAddError(null);\n const exit = await doPreview({ payload: { spec: specUrl } });\n if (Exit.isFailure(exit)) {\n setAnalyzeError(errorMessageFromExit(exit, \"Failed to parse spec\"));\n setAnalyzing(false);\n return;\n }\n const result = exit.value;\n setPreview(result);\n setBaseUrl(\"\");\n setAnalyzing(false);\n };\n\n handleAnalyzeRef.current = handleAnalyze;\n\n // Persist the integration and return its slug. Registers the catalog entry\n // with every detected auth method. Adding a slug that already exists is\n // rejected by the API (`IntegrationAlreadyExistsError`), surfaced inline.\n const ensureIntegration = useCallback(async (): Promise<IntegrationSlug | null> => {\n const exit = await doAdd({\n payload: {\n spec: specInputForAdd(specUrl),\n slug: resolvedSourceId,\n name: resolvedDisplayName,\n ...(resolvedDescription.trim().length > 0\n ? { description: resolvedDescription.trim() }\n : {}),\n baseUrl: resolvedBaseUrl,\n // Always send the edited method list (even empty) when the user has\n // inspected a preview: an explicit [] means \"no auth methods\", while\n // OMITTING the field tells the server to derive defaults from the\n // spec, which would silently resurrect methods the user deleted.\n // Serialize to the wire input dialect (apikey -> request-shaped).\n authenticationTemplate: editedAuthenticationTemplate.map(openApiWireAuthInput),\n },\n reactivityKeys: integrationWriteKeys,\n });\n if (Exit.isFailure(exit)) {\n setAddError(addIntegrationErrorMessage(exit, resolvedSourceId, \"Failed to add integration\"));\n return null;\n }\n return exit.value.slug;\n }, [\n specUrl,\n doAdd,\n resolvedSourceId,\n resolvedDisplayName,\n resolvedDescription,\n resolvedBaseUrl,\n editedAuthenticationTemplate,\n ]);\n\n const handleAdd = async () => {\n setAdding(true);\n setAddError(null);\n\n const integration = await ensureIntegration();\n if (!integration) {\n setAdding(false);\n return;\n }\n\n props.onComplete(String(integration));\n };\n\n // ---- Render ----\n\n return (\n <div className=\"flex flex-1 flex-col gap-6\">\n <div>\n <h1 className=\"text-xl font-semibold text-foreground\">Add OpenAPI Integration</h1>\n </div>\n\n {!preview ? (\n <CardStack>\n <CardStackContent className=\"border-t-0\">\n <div className=\"space-y-2 p-3\">\n <FieldLabel>OpenAPI Spec</FieldLabel>\n <div className=\"relative\">\n <Textarea\n value={specUrl}\n onChange={(e) => setSpecUrl((e.target as HTMLTextAreaElement).value)}\n placeholder=\"https://api.example.com/openapi.json\"\n rows={3}\n maxRows={10}\n className=\"font-mono text-sm\"\n />\n {analyzing && (\n <div className=\"pointer-events-none absolute right-2 top-2\">\n <IOSSpinner className=\"size-4\" />\n </div>\n )}\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n Paste a URL or raw JSON/YAML content.\n </p>\n </div>\n </CardStackContent>\n </CardStack>\n ) : null}\n\n {preview ? (\n <OpenApiSourceDetailsFields\n title={Option.getOrElse(preview.title, () => \"API\")}\n subtitle={`${Option.getOrElse(preview.version, () => \"\")}${\n Option.isSome(preview.version) ? \" · \" : \"\"\n }${preview.operationCount} operation${preview.operationCount !== 1 ? \"s\" : \"\"}${\n preview.tags.length > 0\n ? ` · ${preview.tags.length} tag${preview.tags.length !== 1 ? \"s\" : \"\"}`\n : \"\"\n }`}\n identity={identity}\n description={resolvedDescription}\n onDescriptionChange={setDescriptionDraft}\n baseUrl={resolvedBaseUrl}\n onBaseUrlChange={setBaseUrl}\n baseUrlOptions={baseUrlOptions}\n baseUrlLabel={previewHasNoServers ? \"Base URL\" : \"Base URL override (optional)\"}\n baseUrlPlaceholder={firstServerUrl || \"https://api.example.com\"}\n baseUrlHint={\n previewHasNoServers\n ? undefined\n : \"Overrides the spec's servers; leave empty to choose the server (and variables) per tool call.\"\n }\n baseUrlMissingMessage={\n previewHasNoServers ? \"This spec declares no servers, enter a base URL.\" : undefined\n }\n specUrl={specUrl}\n onSpecUrlChange={(value) => {\n setSpecUrl(value);\n setPreview(null);\n setBaseUrl(\"\");\n }}\n faviconIcon={previewPresetIcon}\n faviconUrl={resolvedBaseUrl || firstServerUrl}\n />\n ) : null}\n\n {analyzeError && <FormErrorAlert message={analyzeError} />}\n\n {preview && (\n <AuthMethodListEditor\n list={authMethodList}\n emptyHint=\"No authentication detected. Add a method, or add the integration without auth and connect an account from the integration page later.\"\n footerHint=\"Every method here is registered with the integration. Connect an account from the integration page after adding.\"\n />\n )}\n\n {preview && slugAlreadyExists && !adding && <SlugCollisionAlert slug={resolvedSourceId} />}\n\n {addError && <FormErrorAlert message={addError} />}\n\n <FloatActions>\n <Button variant=\"ghost\" onClick={() => props.onCancel()} disabled={adding}>\n Cancel\n </Button>\n {preview && (\n <Button onClick={() => void handleAdd()} disabled={!canAdd || adding}>\n {adding && <Spinner className=\"size-3.5\" />}\n {adding ? \"Adding...\" : \"Add integration\"}\n </Button>\n )}\n </FloatActions>\n </div>\n );\n}\n","import {\n CardStack,\n CardStackContent,\n CardStackEntry,\n CardStackEntryContent,\n CardStackEntryDescription,\n CardStackEntryField,\n CardStackEntryTitle,\n} from \"@executor-js/react/components/card-stack\";\nimport {\n FreeformCombobox,\n type FreeformComboboxOption,\n} from \"@executor-js/react/components/combobox\";\nimport { Input } from \"@executor-js/react/components/input\";\nimport { Textarea } from \"@executor-js/react/components/textarea\";\nimport { IntegrationFavicon } from \"@executor-js/react/components/integration-favicon\";\nimport {\n IntegrationIdentityFieldRows,\n type IntegrationIdentity,\n} from \"@executor-js/react/plugins/integration-identity\";\n\n/** The spec input is shown as a one-line \"Spec URL\" field only when it IS a\n * URL; pasted document content gets a multi-line editor instead. */\nconst isUrlInput = (value: string): boolean => URL.canParse(value.trim());\n\nexport function OpenApiSourceDetailsFields(props: {\n readonly title: string;\n readonly subtitle?: string;\n readonly identity: IntegrationIdentity;\n /** The integration's agent-visible description (prefilled from the spec). */\n readonly description?: string;\n readonly onDescriptionChange?: (value: string) => void;\n readonly baseUrl: string;\n readonly onBaseUrlChange: (value: string) => void;\n readonly baseUrlOptions?: readonly FreeformComboboxOption[];\n readonly baseUrlLabel?: string;\n readonly baseUrlPlaceholder?: string;\n readonly baseUrlHint?: string;\n readonly specUrl?: string;\n readonly onSpecUrlChange?: (value: string) => void;\n readonly faviconIcon?: string | null;\n readonly faviconUrl?: string;\n readonly namespaceReadOnly?: boolean;\n readonly specUrlDisabled?: boolean;\n readonly saveState?: \"idle\" | \"saving\" | \"saved\";\n readonly baseUrlMissingMessage?: string;\n readonly footer?: string;\n}) {\n const specIsUrl = props.specUrl !== undefined && isUrlInput(props.specUrl);\n\n return (\n <CardStack>\n <CardStackContent className=\"border-t-0\">\n <CardStackEntry>\n {(props.faviconIcon || props.faviconUrl) && (\n <IntegrationFavicon icon={props.faviconIcon} url={props.faviconUrl} size={16} />\n )}\n <CardStackEntryContent>\n <CardStackEntryTitle>{props.title}</CardStackEntryTitle>\n {props.subtitle && (\n <CardStackEntryDescription>{props.subtitle}</CardStackEntryDescription>\n )}\n </CardStackEntryContent>\n {props.saveState && props.saveState !== \"idle\" && (\n <span className=\"text-xs text-muted-foreground\">\n {props.saveState === \"saving\" ? \"Saving…\" : \"Saved\"}\n </span>\n )}\n </CardStackEntry>\n <IntegrationIdentityFieldRows\n identity={props.identity}\n namespaceReadOnly={props.namespaceReadOnly}\n />\n {props.onDescriptionChange && (\n <CardStackEntryField label=\"Description\">\n <Textarea\n value={props.description ?? \"\"}\n onChange={(e) => props.onDescriptionChange?.((e.target as HTMLTextAreaElement).value)}\n placeholder=\"What this API is and when to reach for it\"\n rows={2}\n maxRows={6}\n className=\"text-sm\"\n />\n </CardStackEntryField>\n )}\n <div className=\"grid grid-cols-1 md:grid-cols-2\">\n <CardStackEntryField label={props.baseUrlLabel ?? \"Base URL\"}>\n {props.baseUrlOptions && props.baseUrlOptions.length > 0 ? (\n <FreeformCombobox\n value={props.baseUrl}\n onValueChange={props.onBaseUrlChange}\n options={props.baseUrlOptions}\n placeholder={props.baseUrlPlaceholder ?? \"https://api.example.com\"}\n className=\"w-full\"\n inputClassName=\"font-mono text-sm\"\n />\n ) : (\n <Input\n value={props.baseUrl}\n onChange={(e) => props.onBaseUrlChange((e.target as HTMLInputElement).value)}\n placeholder={props.baseUrlPlaceholder ?? \"https://api.example.com\"}\n className=\"font-mono text-sm\"\n />\n )}\n\n {props.baseUrlMissingMessage && !props.baseUrl && (\n <p className=\"text-[11px] text-amber-600 dark:text-amber-400\">\n {props.baseUrlMissingMessage}\n </p>\n )}\n {props.baseUrlHint && (\n <p className=\"text-[11px] text-muted-foreground\">{props.baseUrlHint}</p>\n )}\n </CardStackEntryField>\n {specIsUrl && props.onSpecUrlChange && (\n <CardStackEntryField label=\"Spec URL\">\n <Input\n value={props.specUrl}\n onChange={(e) => props.onSpecUrlChange?.((e.target as HTMLInputElement).value)}\n placeholder=\"https://api.example.com/openapi.json\"\n className=\"font-mono text-sm\"\n disabled={props.specUrlDisabled}\n />\n </CardStackEntryField>\n )}\n </div>\n {props.specUrl !== undefined && !specIsUrl && props.onSpecUrlChange && (\n <CardStackEntryField label=\"Spec\">\n <Textarea\n value={props.specUrl}\n onChange={(e) => props.onSpecUrlChange?.((e.target as HTMLTextAreaElement).value)}\n placeholder=\"Pasted OpenAPI JSON/YAML\"\n rows={4}\n maxRows={12}\n className=\"font-mono text-xs\"\n disabled={props.specUrlDisabled}\n />\n </CardStackEntryField>\n )}\n {props.footer && (\n <CardStackEntry>\n <CardStackEntryContent>\n <CardStackEntryTitle>{props.footer}</CardStackEntryTitle>\n </CardStackEntryContent>\n </CardStackEntry>\n )}\n </CardStackContent>\n </CardStack>\n );\n}\n"],"mappings":"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","names":["CardStack","CardStackContent","Textarea","jsx","jsxs","CardStack","CardStackContent","Textarea"]}
@@ -3,14 +3,11 @@ import {
3
3
  openApiWireAuthInput,
4
4
  templateFromPlacements
5
5
  } from "./chunk-RCBR3QMJ.js";
6
- import {
7
- googleAudienceWarningsForUrls
8
- } from "./chunk-MZWZQ24W.js";
9
6
  import {
10
7
  openApiConfigAtom,
11
8
  openapiConfigure
12
- } from "./chunk-NJ4Q3VF4.js";
13
- import "./chunk-O54VFSWE.js";
9
+ } from "./chunk-CPPTKUOW.js";
10
+ import "./chunk-KVPUDOJZ.js";
14
11
 
15
12
  // src/react/OpenApiAccountsPanel.tsx
16
13
  import { useCallback, useMemo } from "react";
@@ -18,18 +15,12 @@ import { useAtomValue, useAtomSet } from "@effect/atom-react";
18
15
  import * as Exit from "effect/Exit";
19
16
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
20
17
  import { AuthTemplateSlug, IntegrationSlug } from "@executor-js/sdk/shared";
21
- import { TriangleAlert } from "lucide-react";
22
18
  import { AccountsSection } from "@executor-js/react/components/accounts-section";
23
- import { Alert, AlertDescription, AlertTitle } from "@executor-js/react/components/alert";
24
19
  import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
25
20
  import {
26
21
  useCustomMethodActions
27
22
  } from "@executor-js/react/lib/custom-auth-methods";
28
- import { jsx, jsxs } from "react/jsx-runtime";
29
- var GOOGLE_AUDIENCE_WARNING = {
30
- "workspace-admin": "This connection includes Google Workspace admin APIs (Chat, Admin Directory, Admin Reports). Connecting requires a Workspace admin account \u2014 personal Gmail accounts cannot grant these scopes.",
31
- "unsupported-user": "This connection includes APIs (e.g. Google Keep) that Google does not grant through standard user OAuth consent. Those tools may fail to authorize."
32
- };
23
+ import { jsx } from "react/jsx-runtime";
33
24
  var NO_AUTH_METHOD = {
34
25
  id: "none",
35
26
  label: "No authentication",
@@ -81,34 +72,19 @@ function OpenApiAccountsPanel(props) {
81
72
  codec,
82
73
  configure
83
74
  });
84
- const audienceWarnings = useMemo(() => {
85
- if (!AsyncResult.isSuccess(configResult) || configResult.value == null) return [];
86
- const urls = configResult.value.googleDiscoveryUrls ?? [];
87
- return googleAudienceWarningsForUrls(urls).flatMap((audience) => {
88
- const message = GOOGLE_AUDIENCE_WARNING[audience];
89
- return message ? [message] : [];
90
- });
91
- }, [configResult]);
92
- return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-3xl space-y-8 px-6 py-8", children: [
93
- audienceWarnings.length > 0 && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
94
- /* @__PURE__ */ jsx(TriangleAlert, {}),
95
- /* @__PURE__ */ jsx(AlertTitle, { children: "Some Google APIs need special consent" }),
96
- /* @__PURE__ */ jsx(AlertDescription, { children: audienceWarnings.map((message) => /* @__PURE__ */ jsx("p", { children: message }, message)) })
97
- ] }),
98
- /* @__PURE__ */ jsx(
99
- AccountsSection,
100
- {
101
- integration: slug,
102
- integrationName,
103
- methods,
104
- accountHandoff,
105
- createCustomMethod,
106
- removeCustomMethod
107
- }
108
- )
109
- ] });
75
+ return /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-3xl space-y-8 px-6 py-8", children: /* @__PURE__ */ jsx(
76
+ AccountsSection,
77
+ {
78
+ integration: slug,
79
+ integrationName,
80
+ methods,
81
+ accountHandoff,
82
+ createCustomMethod,
83
+ removeCustomMethod
84
+ }
85
+ ) });
110
86
  }
111
87
  export {
112
88
  OpenApiAccountsPanel as default
113
89
  };
114
- //# sourceMappingURL=OpenApiAccountsPanel-X5Z5XKRM.js.map
90
+ //# sourceMappingURL=OpenApiAccountsPanel-GHFHHE6P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/OpenApiAccountsPanel.tsx"],"sourcesContent":["import { useCallback, useMemo } from \"react\";\nimport { useAtomValue, useAtomSet } from \"@effect/atom-react\";\nimport * as Exit from \"effect/Exit\";\nimport * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\nimport { AuthTemplateSlug, IntegrationSlug } from \"@executor-js/sdk/shared\";\nimport type { IntegrationAccountHandoff } from \"@executor-js/sdk/client\";\n\nimport { AccountsSection } from \"@executor-js/react/components/accounts-section\";\nimport { integrationWriteKeys } from \"@executor-js/react/api/reactivity-keys\";\nimport type { AuthMethod, Placement } from \"@executor-js/react/lib/auth-placements\";\nimport {\n useCustomMethodActions,\n type AuthMethodsCodec,\n type ConfigureAuthMethods,\n} from \"@executor-js/react/lib/custom-auth-methods\";\n\nimport { openApiConfigAtom, openapiConfigure } from \"./atoms\";\nimport {\n authMethodsFromConfig,\n templateFromPlacements,\n openApiWireAuthInput,\n} from \"./auth-method-config\";\nimport type { Authentication } from \"../sdk/types\";\n\nconst NO_AUTH_METHOD: AuthMethod = {\n id: \"none\",\n label: \"No authentication\",\n kind: \"none\",\n source: \"spec\",\n template: AuthTemplateSlug.make(\"none\"),\n placements: [],\n};\n\n// ---------------------------------------------------------------------------\n// OpenAPI Accounts hub: fills the generic detail page's `accounts` slot.\n//\n// Reads the integration's real `authenticationTemplate` (via `getConfig`),\n// converts it to generic `AuthMethod[]`, and composes the generic\n// `AccountsSection`, whose Add-account offers those methods plus a \"+ Custom\n// method\" row (apiKey-only). The custom-method create is INJECTED here\n// (`createCustomMethod`): generic placements → an `APIKeyAuthentication`\n// (`templateFromPlacements`, slug omitted → backend `custom_<id>`) merge-\n// appended onto the existing template and persisted via `configure`. Stays\n// plugin-side because it touches the OpenAPI sdk `Authentication` types.\n// ---------------------------------------------------------------------------\n\nexport default function OpenApiAccountsPanel(props: {\n readonly sourceId: string;\n readonly integrationName: string;\n readonly accountHandoff?: IntegrationAccountHandoff | null;\n}) {\n const { sourceId, integrationName, accountHandoff } = props;\n const slug = IntegrationSlug.make(sourceId);\n const configResult = useAtomValue(openApiConfigAtom(slug));\n const doConfigure = useAtomSet(openapiConfigure, { mode: \"promiseExit\" });\n\n // The wire `getConfig` template is structurally an `Authentication[]` (the\n // `slug` is an unbranded string on the wire); treat it as such for the\n // plugin-side converters that brand the slug back.\n const existingTemplate = useMemo<readonly Authentication[]>(() => {\n if (!AsyncResult.isSuccess(configResult) || configResult.value == null) return [];\n return (configResult.value.authenticationTemplate ?? []) as readonly Authentication[];\n }, [configResult]);\n\n const methods = useMemo<readonly AuthMethod[]>(() => {\n const declared = authMethodsFromConfig(existingTemplate);\n return declared.length > 0 ? declared : [NO_AUTH_METHOD];\n }, [existingTemplate]);\n\n // Custom-method create/remove: the shared skeleton (merge-append → diff out\n // the created method; filter → replace) parameterized by the OpenAPI codec.\n // Stays plugin-side only where it touches the OpenAPI `Authentication` types.\n const configure = useCallback<ConfigureAuthMethods<Authentication>>(\n async (input) => {\n const exit = await doConfigure({\n params: { slug },\n payload: {\n authenticationTemplate: input.authenticationTemplate.map(openApiWireAuthInput),\n ...(input.mode ? { mode: input.mode } : {}),\n },\n reactivityKeys: integrationWriteKeys,\n });\n return Exit.map(exit, (result) => result.authenticationTemplate as readonly Authentication[]);\n },\n [doConfigure, slug],\n );\n\n const codec = useMemo<AuthMethodsCodec<Authentication>>(\n () => ({\n toAuthMethods: authMethodsFromConfig,\n // Slug omitted → backend backfills `custom_<id>`.\n templatesFromPlacements: (placements: readonly Placement[]) => [\n templateFromPlacements(placements),\n ],\n slugOf: (template: Authentication) => String(template.slug),\n }),\n [],\n );\n\n const { createCustomMethod, removeCustomMethod } = useCustomMethodActions({\n existing: existingTemplate,\n codec,\n configure,\n });\n\n return (\n <div className=\"mx-auto max-w-3xl space-y-8 px-6 py-8\">\n <AccountsSection\n integration={slug}\n integrationName={integrationName}\n methods={methods}\n accountHandoff={accountHandoff}\n createCustomMethod={createCustomMethod}\n removeCustomMethod={removeCustomMethod}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,aAAa,eAAe;AACrC,SAAS,cAAc,kBAAkB;AACzC,YAAY,UAAU;AACtB,YAAY,iBAAiB;AAC7B,SAAS,kBAAkB,uBAAuB;AAGlD,SAAS,uBAAuB;AAChC,SAAS,4BAA4B;AAErC;AAAA,EACE;AAAA,OAGK;AA6FD;AAnFN,IAAM,iBAA6B;AAAA,EACjC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU,iBAAiB,KAAK,MAAM;AAAA,EACtC,YAAY,CAAC;AACf;AAee,SAAR,qBAAsC,OAI1C;AACD,QAAM,EAAE,UAAU,iBAAiB,eAAe,IAAI;AACtD,QAAM,OAAO,gBAAgB,KAAK,QAAQ;AAC1C,QAAM,eAAe,aAAa,kBAAkB,IAAI,CAAC;AACzD,QAAM,cAAc,WAAW,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAKxE,QAAM,mBAAmB,QAAmC,MAAM;AAChE,QAAI,CAAa,sBAAU,YAAY,KAAK,aAAa,SAAS,KAAM,QAAO,CAAC;AAChF,WAAQ,aAAa,MAAM,0BAA0B,CAAC;AAAA,EACxD,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,UAAU,QAA+B,MAAM;AACnD,UAAM,WAAW,sBAAsB,gBAAgB;AACvD,WAAO,SAAS,SAAS,IAAI,WAAW,CAAC,cAAc;AAAA,EACzD,GAAG,CAAC,gBAAgB,CAAC;AAKrB,QAAM,YAAY;AAAA,IAChB,OAAO,UAAU;AACf,YAAM,OAAO,MAAM,YAAY;AAAA,QAC7B,QAAQ,EAAE,KAAK;AAAA,QACf,SAAS;AAAA,UACP,wBAAwB,MAAM,uBAAuB,IAAI,oBAAoB;AAAA,UAC7E,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,QAC3C;AAAA,QACA,gBAAgB;AAAA,MAClB,CAAC;AACD,aAAY,SAAI,MAAM,CAAC,WAAW,OAAO,sBAAmD;AAAA,IAC9F;AAAA,IACA,CAAC,aAAa,IAAI;AAAA,EACpB;AAEA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL,eAAe;AAAA;AAAA,MAEf,yBAAyB,CAAC,eAAqC;AAAA,QAC7D,uBAAuB,UAAU;AAAA,MACnC;AAAA,MACA,QAAQ,CAAC,aAA6B,OAAO,SAAS,IAAI;AAAA,IAC5D;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,EAAE,oBAAoB,mBAAmB,IAAI,uBAAuB;AAAA,IACxE,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE,oBAAC,SAAI,WAAU,yCACb;AAAA,IAAC;AAAA;AAAA,MACC,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACF,GACF;AAEJ;","names":[]}
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  openApiConfigAtom,
3
3
  updateOpenApiSpec
4
- } from "./chunk-NJ4Q3VF4.js";
5
- import "./chunk-O54VFSWE.js";
4
+ } from "./chunk-CPPTKUOW.js";
5
+ import "./chunk-KVPUDOJZ.js";
6
6
 
7
7
  // src/react/UpdateSpecSection.tsx
8
8
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -37,7 +37,7 @@ function UpdateSpecSection(props) {
37
37
  const [pasted, setPasted] = useState("");
38
38
  const [error, setError] = useState(null);
39
39
  const config = AsyncResult.isSuccess(configResult) && configResult.value ? configResult.value : null;
40
- const sourceUrl = config ? config.googleDiscoveryUrls?.length ? `${config.googleDiscoveryUrls.length} Google Discovery document${config.googleDiscoveryUrls.length !== 1 ? "s" : ""}` : config.sourceUrl : void 0;
40
+ const sourceUrl = config?.sourceUrl;
41
41
  const applyStaged = useCallback(async () => {
42
42
  const spec = pasted.trim().length > 0 ? { kind: "blob", value: pasted } : void 0;
43
43
  if (!spec && !refetchStaged) return { ok: true, summary: null };
@@ -101,4 +101,4 @@ function UpdateSpecSection(props) {
101
101
  export {
102
102
  UpdateSpecSection as default
103
103
  };
104
- //# sourceMappingURL=UpdateSpecSection-Z2DEEWZW.js.map
104
+ //# sourceMappingURL=UpdateSpecSection-PLCBUU4I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/UpdateSpecSection.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useAtomSet, useAtomValue } from \"@effect/atom-react\";\nimport * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\nimport * as Exit from \"effect/Exit\";\n\nimport { integrationWriteKeys } from \"@executor-js/react/api/reactivity-keys\";\nimport { messageFromExit } from \"@executor-js/react/api/error-reporting\";\nimport { FormErrorAlert } from \"@executor-js/react/lib/integration-add\";\nimport { IntegrationSlug } from \"@executor-js/sdk/shared\";\nimport type { EditSheetApplyResult, EditSheetSectionProps } from \"@executor-js/sdk/client\";\nimport { Checkbox } from \"@executor-js/react/components/checkbox\";\nimport { Label } from \"@executor-js/react/components/label\";\nimport { Textarea } from \"@executor-js/react/components/textarea\";\n\nimport { openApiConfigAtom, updateOpenApiSpec } from \"./atoms\";\n\n// ---------------------------------------------------------------------------\n// Update spec — the openapi plugin's section of the integration Edit sheet.\n// The user STAGES a spec update here (check re-fetch, or paste new content);\n// nothing runs until the sheet's Save. The staged apply re-resolves the spec\n// and rebuilds the tool catalog in place — connections, credentials, policies,\n// and the curated description all survive.\n// ---------------------------------------------------------------------------\n\ntype UpdateOutcome = {\n readonly toolCount: number;\n readonly addedTools: readonly string[];\n readonly removedTools: readonly string[];\n};\n\nconst outcomeSummary = (outcome: UpdateOutcome): string => {\n const added = outcome.addedTools.length;\n const removed = outcome.removedTools.length;\n if (added === 0 && removed === 0) {\n return `Spec updated — ${outcome.toolCount} tools, no changes to the tool list.`;\n }\n const parts = [\n ...(added > 0 ? [`+${added} tool${added !== 1 ? \"s\" : \"\"}`] : []),\n ...(removed > 0 ? [`−${removed} tool${removed !== 1 ? \"s\" : \"\"}`] : []),\n ];\n return `Spec updated — ${parts.join(\", \")} (${outcome.toolCount} total).`;\n};\n\nexport default function UpdateSpecSection(props: EditSheetSectionProps) {\n const slug = IntegrationSlug.make(props.sourceId);\n const configResult = useAtomValue(openApiConfigAtom(slug));\n const doUpdate = useAtomSet(updateOpenApiSpec, { mode: \"promiseExit\" });\n const [refetchStaged, setRefetchStaged] = useState(false);\n const [pasted, setPasted] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n const config =\n AsyncResult.isSuccess(configResult) && configResult.value ? configResult.value : null;\n\n const sourceUrl = config?.sourceUrl;\n\n // The staged apply, rebuilt whenever the staged inputs change. Reported to\n // the sheet through a ref-stable callback so Save can run it.\n const applyStaged = useCallback(async (): Promise<EditSheetApplyResult> => {\n const spec = pasted.trim().length > 0 ? { kind: \"blob\" as const, value: pasted } : undefined;\n if (!spec && !refetchStaged) return { ok: true, summary: null };\n setError(null);\n const exit = await doUpdate({\n params: { slug },\n payload: spec ? { spec } : {},\n reactivityKeys: integrationWriteKeys,\n });\n if (Exit.isFailure(exit)) {\n setError(messageFromExit(exit, \"Failed to update spec\"));\n return { ok: false };\n }\n setRefetchStaged(false);\n setPasted(\"\");\n return { ok: true, summary: outcomeSummary(exit.value) };\n }, [doUpdate, pasted, refetchStaged, slug]);\n\n const onPendingChangeRef = useRef(props.onPendingChange);\n onPendingChangeRef.current = props.onPendingChange;\n const hasStagedChange = refetchStaged || pasted.trim().length > 0;\n useEffect(() => {\n onPendingChangeRef.current?.(hasStagedChange ? applyStaged : null);\n // Clear the staged apply if this section unmounts mid-edit.\n return () => onPendingChangeRef.current?.(null);\n }, [hasStagedChange, applyStaged]);\n\n if (!config) return null;\n\n return (\n <div className=\"space-y-3 border-t border-border/60 pt-5\">\n <div className=\"space-y-1\">\n <p className=\"text-sm font-medium text-foreground\">Update spec</p>\n <p className=\"text-xs text-muted-foreground\">\n Rebuild this integration's tools from the latest spec when you save. Connections,\n credentials, and policies are kept.\n </p>\n </div>\n\n {sourceUrl ? (\n <div className=\"space-y-2\">\n <Label className=\"flex items-start gap-2 text-sm font-normal\">\n <Checkbox\n checked={refetchStaged}\n onCheckedChange={(checked: boolean | \"indeterminate\") =>\n setRefetchStaged(checked === true)\n }\n />\n <span className=\"space-y-0.5\">\n <span className=\"block\">Re-fetch the spec on save</span>\n <span className=\"block break-all font-mono text-xs text-muted-foreground\">\n {sourceUrl}\n </span>\n </span>\n </Label>\n </div>\n ) : (\n <div className=\"space-y-2\">\n <Label htmlFor=\"update-spec-content\" className=\"text-xs text-muted-foreground\">\n Updated spec content\n </Label>\n <Textarea\n id=\"update-spec-content\"\n value={pasted}\n onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setPasted(e.target.value)}\n placeholder=\"Paste the updated OpenAPI JSON/YAML — applied when you save. This spec was added inline, so there is no URL to re-fetch.\"\n rows={4}\n maxRows={10}\n className=\"font-mono text-xs\"\n />\n </div>\n )}\n\n {error && <FormErrorAlert message={error} />}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -18,14 +18,6 @@ export declare const OpenApiGroup: HttpApiGroup.HttpApiGroup<"openapi", HttpApiE
18
18
  }>>>;
19
19
  }>>;
20
20
  readonly operationCount: Schema.Number;
21
- readonly operations: Schema.$Array<Schema.Struct<{
22
- readonly operationId: Schema.String;
23
- readonly method: Schema.Literals<readonly ["get", "put", "post", "delete", "patch", "head", "options", "trace"]>;
24
- readonly path: Schema.String;
25
- readonly summary: Schema.OptionFromOptional<Schema.String>;
26
- readonly tags: Schema.$Array<Schema.String>;
27
- readonly deprecated: Schema.Boolean;
28
- }>>;
29
21
  readonly tags: Schema.$Array<Schema.String>;
30
22
  readonly securitySchemes: Schema.$Array<Schema.Struct<{
31
23
  readonly name: Schema.String;
@@ -75,12 +67,6 @@ export declare const OpenApiGroup: HttpApiGroup.HttpApiGroup<"openapi", HttpApiE
75
67
  }>, Schema.Struct<{
76
68
  readonly kind: Schema.Literal<"blob">;
77
69
  readonly value: Schema.String;
78
- }>, Schema.Struct<{
79
- readonly kind: Schema.Literal<"googleDiscovery">;
80
- readonly url: Schema.String;
81
- }>, Schema.Struct<{
82
- readonly kind: Schema.Literal<"googleDiscoveryBundle">;
83
- readonly urls: Schema.$Array<Schema.String>;
84
70
  }>]>;
85
71
  readonly slug: Schema.String;
86
72
  readonly name: Schema.optional<Schema.String>;
@@ -122,7 +108,6 @@ export declare const OpenApiGroup: HttpApiGroup.HttpApiGroup<"openapi", HttpApiE
122
108
  slug: Schema.String;
123
109
  }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.NullOr<Schema.Struct<{
124
110
  readonly sourceUrl: Schema.optional<Schema.String>;
125
- readonly googleDiscoveryUrls: Schema.optional<Schema.$Array<Schema.String>>;
126
111
  readonly baseUrl: Schema.optional<Schema.String>;
127
112
  readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
128
113
  readonly queryParams: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
@@ -195,12 +180,6 @@ export declare const OpenApiGroup: HttpApiGroup.HttpApiGroup<"openapi", HttpApiE
195
180
  }>, Schema.Struct<{
196
181
  readonly kind: Schema.Literal<"blob">;
197
182
  readonly value: Schema.String;
198
- }>, Schema.Struct<{
199
- readonly kind: Schema.Literal<"googleDiscovery">;
200
- readonly url: Schema.String;
201
- }>, Schema.Struct<{
202
- readonly kind: Schema.Literal<"googleDiscoveryBundle">;
203
- readonly urls: Schema.$Array<Schema.String>;
204
183
  }>]>>;
205
184
  }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
206
185
  readonly slug: Schema.brand<Schema.String, "IntegrationSlug">;