@executor-js/plugin-openapi 1.4.33 → 1.5.1

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 (81) hide show
  1. package/dist/AddOpenApiSource-7M52SRUX.js +893 -0
  2. package/dist/AddOpenApiSource-7M52SRUX.js.map +1 -0
  3. package/dist/EditOpenApiSource-WTAMRJUK.js +68 -0
  4. package/dist/EditOpenApiSource-WTAMRJUK.js.map +1 -0
  5. package/dist/OpenApiAccountsPanel-3VJJXNQF.js +112 -0
  6. package/dist/OpenApiAccountsPanel-3VJJXNQF.js.map +1 -0
  7. package/dist/api/group.d.ts +104 -225
  8. package/dist/api/index.d.ts +127 -271
  9. package/dist/{chunk-BB5IAKRG.js → chunk-AQ7JDDRM.js} +12 -2
  10. package/dist/chunk-AQ7JDDRM.js.map +1 -0
  11. package/dist/chunk-BSLE6HCE.js +181 -0
  12. package/dist/chunk-BSLE6HCE.js.map +1 -0
  13. package/dist/chunk-MZWZQ24W.js +226 -0
  14. package/dist/chunk-MZWZQ24W.js.map +1 -0
  15. package/dist/chunk-OSIFYIQP.js +623 -0
  16. package/dist/chunk-OSIFYIQP.js.map +1 -0
  17. package/dist/chunk-QSSRVK6M.js +139 -0
  18. package/dist/chunk-QSSRVK6M.js.map +1 -0
  19. package/dist/chunk-V7VCHYOY.js +1544 -0
  20. package/dist/chunk-V7VCHYOY.js.map +1 -0
  21. package/dist/{chunk-AN4HJFNP.js → chunk-YVRI7KRC.js} +162 -186
  22. package/dist/chunk-YVRI7KRC.js.map +1 -0
  23. package/dist/client.js +9 -8
  24. package/dist/client.js.map +1 -1
  25. package/dist/core.js +28 -11
  26. package/dist/index.js +11 -4
  27. package/dist/react/AddOpenApiSource.d.ts +2 -13
  28. package/dist/react/GoogleProductPicker.d.ts +9 -0
  29. package/dist/react/OpenApiAccountsPanel.d.ts +6 -0
  30. package/dist/react/OpenApiSourceDetailsFields.d.ts +3 -2
  31. package/dist/react/atoms.d.ts +177 -192
  32. package/dist/react/auth-method-config.d.ts +15 -0
  33. package/dist/react/client.d.ts +103 -224
  34. package/dist/react/index.d.ts +2 -2
  35. package/dist/react/source-plugin.d.ts +2 -2
  36. package/dist/sdk/config.d.ts +75 -0
  37. package/dist/sdk/configure.test.d.ts +1 -0
  38. package/dist/sdk/describe-auth-methods.test.d.ts +1 -0
  39. package/dist/sdk/errors.d.ts +4 -6
  40. package/dist/sdk/extract.d.ts +7 -5
  41. package/dist/sdk/google-bundle.test.d.ts +1 -0
  42. package/dist/sdk/google-discovery.d.ts +43 -0
  43. package/dist/sdk/google-discovery.test.d.ts +1 -0
  44. package/dist/sdk/google-oauth-batches.d.ts +13 -0
  45. package/dist/sdk/google-oauth-batches.test.d.ts +1 -0
  46. package/dist/sdk/google-oauth-scopes.d.ts +3 -0
  47. package/dist/sdk/google-oauth-scopes.test.d.ts +1 -0
  48. package/dist/sdk/google-presets.d.ts +16 -0
  49. package/dist/sdk/google-presets.test.d.ts +1 -0
  50. package/dist/sdk/google-product-picker-scopes.test.d.ts +1 -0
  51. package/dist/sdk/index.d.ts +7 -5
  52. package/dist/sdk/invoke.d.ts +6 -9
  53. package/dist/sdk/openapi-utils.d.ts +1 -0
  54. package/dist/sdk/plugin.d.ts +74 -231
  55. package/dist/sdk/presets.d.ts +2 -1
  56. package/dist/sdk/preview.d.ts +20 -15
  57. package/dist/sdk/query-serialization.test.d.ts +1 -0
  58. package/dist/sdk/store.d.ts +14 -41
  59. package/dist/sdk/types.d.ts +23 -51
  60. package/dist/testing/index.d.ts +49 -38
  61. package/dist/testing.js +46 -18
  62. package/dist/testing.js.map +1 -1
  63. package/package.json +6 -4
  64. package/dist/AddOpenApiSource-NSCULGTM.js +0 -19
  65. package/dist/AddOpenApiSource-NSCULGTM.js.map +0 -1
  66. package/dist/EditOpenApiSource-MV7NYTRP.js +0 -774
  67. package/dist/EditOpenApiSource-MV7NYTRP.js.map +0 -1
  68. package/dist/OpenApiSourceSummary-7JBS7PUV.js +0 -122
  69. package/dist/OpenApiSourceSummary-7JBS7PUV.js.map +0 -1
  70. package/dist/chunk-2ZKKZYZH.js +0 -1181
  71. package/dist/chunk-2ZKKZYZH.js.map +0 -1
  72. package/dist/chunk-AN4HJFNP.js.map +0 -1
  73. package/dist/chunk-BB5IAKRG.js.map +0 -1
  74. package/dist/chunk-PRVJDE43.js +0 -2101
  75. package/dist/chunk-PRVJDE43.js.map +0 -1
  76. package/dist/chunk-X5JX3KTA.js +0 -201
  77. package/dist/chunk-X5JX3KTA.js.map +0 -1
  78. package/dist/react/OpenApiSourceSummary.d.ts +0 -5
  79. package/dist/sdk/credential-status.d.ts +0 -23
  80. package/dist/sdk/source-contracts.d.ts +0 -55
  81. /package/dist/{sdk/credential-status.test.d.ts → react/auth-method-config.test.d.ts} +0 -0
@@ -0,0 +1,893 @@
1
+ import {
2
+ compactGoogleOAuthScopes,
3
+ isGoogleDiscoveryUrl
4
+ } from "./chunk-OSIFYIQP.js";
5
+ import {
6
+ openApiPresets
7
+ } from "./chunk-AQ7JDDRM.js";
8
+ import {
9
+ authenticationFromEditorValue,
10
+ editorValueFromAuthentication
11
+ } from "./chunk-QSSRVK6M.js";
12
+ import {
13
+ GOOGLE_BUNDLE_PRESET_ID,
14
+ googleOAuthConsentScopesForPreset,
15
+ googleOpenApiPresets
16
+ } from "./chunk-MZWZQ24W.js";
17
+ import {
18
+ addOpenApiSpec,
19
+ previewOpenApiSpec
20
+ } from "./chunk-BSLE6HCE.js";
21
+ import {
22
+ TOKEN_VARIABLE,
23
+ expandServerUrlOptions,
24
+ variable
25
+ } from "./chunk-YVRI7KRC.js";
26
+
27
+ // src/react/AddOpenApiSource.tsx
28
+ import { useCallback, useEffect, useMemo as useMemo2, useRef, useState as useState2 } from "react";
29
+ import { useAtomSet, useAtomValue } from "@effect/atom-react";
30
+ import { Link } from "@tanstack/react-router";
31
+ import * as Effect from "effect/Effect";
32
+ import * as Exit from "effect/Exit";
33
+ import * as Option from "effect/Option";
34
+ import * as Predicate from "effect/Predicate";
35
+ import * as Schema from "effect/Schema";
36
+ import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
37
+ import {
38
+ AuthTemplateSlug
39
+ } from "@executor-js/sdk/shared";
40
+ import { integrationsOptimisticAtom } from "@executor-js/react/api/atoms";
41
+ import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
42
+ import {
43
+ slugifyNamespace,
44
+ useIntegrationIdentity
45
+ } from "@executor-js/react/plugins/integration-identity";
46
+ import { Button as Button2 } from "@executor-js/react/components/button";
47
+ import {
48
+ AuthTemplateEditor
49
+ } from "@executor-js/react/components/auth-template-editor";
50
+ import { CardStack as CardStack2, CardStackContent as CardStackContent2 } from "@executor-js/react/components/card-stack";
51
+ import { FieldLabel as FieldLabel2 } from "@executor-js/react/components/field";
52
+ import { FloatActions } from "@executor-js/react/components/float-actions";
53
+ import { Textarea } from "@executor-js/react/components/textarea";
54
+ import { IOSSpinner, Spinner } from "@executor-js/react/components/spinner";
55
+ import { PlusIcon as PlusIcon2, XIcon as XIcon2 } from "lucide-react";
56
+
57
+ // src/react/OpenApiSourceDetailsFields.tsx
58
+ import {
59
+ CardStack,
60
+ CardStackContent,
61
+ CardStackEntry,
62
+ CardStackEntryContent,
63
+ CardStackEntryDescription,
64
+ CardStackEntryField,
65
+ CardStackEntryTitle
66
+ } from "@executor-js/react/components/card-stack";
67
+ import {
68
+ FreeformCombobox
69
+ } from "@executor-js/react/components/combobox";
70
+ import { Input } from "@executor-js/react/components/input";
71
+ import { IntegrationFavicon } from "@executor-js/react/components/integration-favicon";
72
+ import {
73
+ IntegrationIdentityFieldRows
74
+ } from "@executor-js/react/plugins/integration-identity";
75
+ import { jsx, jsxs } from "react/jsx-runtime";
76
+ function OpenApiSourceDetailsFields(props) {
77
+ const baseUrlOptions = props.baseUrlOptions ?? [];
78
+ return /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsxs(CardStackContent, { className: "border-t-0", children: [
79
+ /* @__PURE__ */ jsxs(CardStackEntry, { children: [
80
+ (props.faviconIcon || props.faviconUrl) && /* @__PURE__ */ jsx(IntegrationFavicon, { icon: props.faviconIcon, url: props.faviconUrl, size: 16 }),
81
+ /* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
82
+ /* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.title }),
83
+ props.description && /* @__PURE__ */ jsx(CardStackEntryDescription, { children: props.description })
84
+ ] }),
85
+ props.saveState && props.saveState !== "idle" && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: props.saveState === "saving" ? "Saving\u2026" : "Saved" })
86
+ ] }),
87
+ /* @__PURE__ */ jsx(
88
+ IntegrationIdentityFieldRows,
89
+ {
90
+ identity: props.identity,
91
+ namespaceReadOnly: props.namespaceReadOnly
92
+ }
93
+ ),
94
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2", children: [
95
+ /* @__PURE__ */ jsxs(CardStackEntryField, { label: "Base URL", children: [
96
+ baseUrlOptions.length > 0 ? /* @__PURE__ */ jsx(
97
+ FreeformCombobox,
98
+ {
99
+ value: props.baseUrl,
100
+ onValueChange: props.onBaseUrlChange,
101
+ options: baseUrlOptions,
102
+ placeholder: "https://api.example.com",
103
+ className: "w-full",
104
+ inputClassName: "font-mono text-sm"
105
+ }
106
+ ) : /* @__PURE__ */ jsx(
107
+ Input,
108
+ {
109
+ value: props.baseUrl,
110
+ onChange: (e) => props.onBaseUrlChange(e.target.value),
111
+ placeholder: "https://api.example.com",
112
+ className: "font-mono text-sm"
113
+ }
114
+ ),
115
+ props.baseUrlMissingMessage && !props.baseUrl && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-amber-600 dark:text-amber-400", children: props.baseUrlMissingMessage })
116
+ ] }),
117
+ props.specUrl !== void 0 && props.onSpecUrlChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Spec URL", children: /* @__PURE__ */ jsx(
118
+ Input,
119
+ {
120
+ value: props.specUrl,
121
+ onChange: (e) => props.onSpecUrlChange?.(e.target.value),
122
+ placeholder: "https://api.example.com/openapi.json",
123
+ className: "font-mono text-sm",
124
+ disabled: props.specUrlDisabled
125
+ }
126
+ ) })
127
+ ] }),
128
+ props.footer && /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsx(CardStackEntryContent, { children: /* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.footer }) }) })
129
+ ] }) });
130
+ }
131
+
132
+ // src/react/GoogleProductPicker.tsx
133
+ import { useMemo, useState } from "react";
134
+ import { ChevronDownIcon, PlusIcon, TriangleAlert, XIcon } from "lucide-react";
135
+ import { cn } from "@executor-js/react/lib/utils";
136
+ import { Badge } from "@executor-js/react/components/badge";
137
+ import { Button } from "@executor-js/react/components/button";
138
+ import { Checkbox } from "@executor-js/react/components/checkbox";
139
+ import {
140
+ Collapsible,
141
+ CollapsibleContent,
142
+ CollapsibleTrigger
143
+ } from "@executor-js/react/components/collapsible";
144
+ import { FieldLabel } from "@executor-js/react/components/field";
145
+ import { Input as Input2 } from "@executor-js/react/components/input";
146
+ import { IntegrationFavicon as IntegrationFavicon2 } from "@executor-js/react/components/integration-favicon";
147
+
148
+ // src/sdk/google-oauth-batches.ts
149
+ var GOOGLE_CLOUD_BATCH_IDS = /* @__PURE__ */ new Set(["google-bigquery", "google-cloud-resource-manager"]);
150
+ var googleOAuthConsentBatches = (items) => {
151
+ const standardScopes = [];
152
+ const cloudScopes = [];
153
+ const batches = [];
154
+ for (const item of items) {
155
+ if (item.scopes.length === 0) continue;
156
+ if (item.oauthAudience === "standard-user") {
157
+ standardScopes.push(...item.scopes);
158
+ continue;
159
+ }
160
+ if (GOOGLE_CLOUD_BATCH_IDS.has(item.id)) {
161
+ cloudScopes.push(...item.scopes);
162
+ continue;
163
+ }
164
+ batches.push({
165
+ id: item.id,
166
+ label: item.name,
167
+ apiScopes: item.scopes
168
+ });
169
+ }
170
+ const compactedStandardScopes = compactGoogleOAuthScopes(standardScopes);
171
+ const compactedCloudScopes = compactGoogleOAuthScopes(cloudScopes);
172
+ return [
173
+ ...compactedStandardScopes.length > 0 ? [
174
+ {
175
+ id: "google-core",
176
+ label: "Core Google services",
177
+ apiScopes: compactedStandardScopes
178
+ }
179
+ ] : [],
180
+ ...batches.map((batch) => ({
181
+ ...batch,
182
+ apiScopes: compactGoogleOAuthScopes(batch.apiScopes)
183
+ })),
184
+ ...compactedCloudScopes.length > 0 ? [
185
+ {
186
+ id: "google-cloud",
187
+ label: "Google Cloud services",
188
+ apiScopes: compactedCloudScopes
189
+ }
190
+ ] : []
191
+ ].filter((batch) => batch.apiScopes.length > 0);
192
+ };
193
+
194
+ // src/react/GoogleProductPicker.tsx
195
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
196
+ var AUDIENCE_ORDER = [
197
+ "standard-user",
198
+ "advanced-user",
199
+ "workspace-admin",
200
+ "unsupported-user"
201
+ ];
202
+ var AUDIENCE_LABEL = {
203
+ "standard-user": "Core Google services",
204
+ "advanced-user": "Advanced services",
205
+ "workspace-admin": "Workspace admin",
206
+ "unsupported-user": "Limited user consent"
207
+ };
208
+ var AUDIENCE_DESCRIPTION = {
209
+ "standard-user": "Connect with a normal Google account \u2014 one consent screen.",
210
+ "advanced-user": "Broader scopes that may need an unverified-app warning to be accepted.",
211
+ "workspace-admin": "Requires a Google Workspace admin account; not available on personal Gmail.",
212
+ "unsupported-user": "Google does not grant these scopes through standard user OAuth consent."
213
+ };
214
+ var audienceNeedsWarning = (audience) => audience === "workspace-admin" || audience === "unsupported-user";
215
+ var AudienceWarningChip = ({ audience }) => audience === "workspace-admin" ? /* @__PURE__ */ jsxs2(
216
+ Badge,
217
+ {
218
+ variant: "outline",
219
+ className: "shrink-0 border-amber-500/40 text-amber-700 dark:text-amber-400",
220
+ children: [
221
+ /* @__PURE__ */ jsx2(TriangleAlert, { className: "size-3" }),
222
+ "Admin only"
223
+ ]
224
+ }
225
+ ) : audience === "unsupported-user" ? /* @__PURE__ */ jsxs2(Badge, { variant: "outline", className: "shrink-0 border-destructive/40 text-destructive", children: [
226
+ /* @__PURE__ */ jsx2(TriangleAlert, { className: "size-3" }),
227
+ "Limited consent"
228
+ ] }) : null;
229
+ var ProductRow = ({
230
+ preset,
231
+ checked,
232
+ onToggle
233
+ }) => /* @__PURE__ */ jsxs2(
234
+ FieldLabel,
235
+ {
236
+ className: cn(
237
+ // `w-full` overrides FieldLabel's base `w-fit` (which would size the row to
238
+ // its content and overflow the column); `min-w-0` then lets the cell shrink
239
+ // to its track so the name/summary truncates instead of spilling over.
240
+ "flex w-full min-w-0 cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 transition-colors",
241
+ checked ? "bg-primary/5" : "hover:bg-muted/40"
242
+ ),
243
+ children: [
244
+ /* @__PURE__ */ jsx2(Checkbox, { checked, onCheckedChange: (next) => onToggle(next === true) }),
245
+ /* @__PURE__ */ jsx2("div", { className: "shrink-0", children: /* @__PURE__ */ jsx2(IntegrationFavicon2, { icon: preset.icon, url: preset.url, size: 16 }) }),
246
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1 truncate text-sm", children: [
247
+ /* @__PURE__ */ jsx2("span", { className: "font-medium text-foreground", children: preset.name }),
248
+ " ",
249
+ /* @__PURE__ */ jsx2("span", { className: "text-[11px] text-muted-foreground", children: preset.summary })
250
+ ] }),
251
+ /* @__PURE__ */ jsx2(AudienceWarningChip, { audience: preset.oauthAudience })
252
+ ]
253
+ }
254
+ );
255
+ var CustomUrlEscapeHatch = ({
256
+ customUrls,
257
+ onAddCustomUrl,
258
+ onRemoveCustomUrl
259
+ }) => {
260
+ const [draft, setDraft] = useState("");
261
+ const trimmed = draft.trim();
262
+ const isValid = isGoogleDiscoveryUrl(trimmed);
263
+ const isDuplicate = customUrls.includes(trimmed);
264
+ const commit = () => {
265
+ if (!isValid || isDuplicate) return;
266
+ onAddCustomUrl(trimmed);
267
+ setDraft("");
268
+ };
269
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
270
+ /* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px] font-medium text-muted-foreground", children: "Add a custom Google Discovery URL" }),
271
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
272
+ /* @__PURE__ */ jsx2(
273
+ Input2,
274
+ {
275
+ value: draft,
276
+ onChange: (event) => setDraft(event.target.value),
277
+ onKeyDown: (event) => {
278
+ if (event.key === "Enter") {
279
+ event.preventDefault();
280
+ commit();
281
+ }
282
+ },
283
+ placeholder: "https://www.googleapis.com/discovery/v1/apis/<service>/<version>/rest",
284
+ className: "font-mono text-[11px]"
285
+ }
286
+ ),
287
+ /* @__PURE__ */ jsxs2(
288
+ Button,
289
+ {
290
+ type: "button",
291
+ variant: "outline",
292
+ size: "sm",
293
+ disabled: !isValid || isDuplicate,
294
+ onClick: commit,
295
+ children: [
296
+ /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" }),
297
+ "Add"
298
+ ]
299
+ }
300
+ )
301
+ ] }),
302
+ trimmed.length > 0 && !isValid ? /* @__PURE__ */ jsx2("p", { className: "text-[11px] text-destructive", children: "Enter a Google Discovery document URL (a *.googleapis.com discovery/$discovery endpoint)." }) : null,
303
+ customUrls.length > 0 ? /* @__PURE__ */ jsx2("ul", { className: "space-y-1", children: customUrls.map((url) => /* @__PURE__ */ jsxs2(
304
+ "li",
305
+ {
306
+ className: "flex items-center justify-between gap-2 rounded-md border border-border bg-muted/20 px-2.5 py-1.5",
307
+ children: [
308
+ /* @__PURE__ */ jsx2("span", { className: "truncate font-mono text-[11px] text-foreground", children: url }),
309
+ /* @__PURE__ */ jsx2(
310
+ Button,
311
+ {
312
+ type: "button",
313
+ variant: "ghost",
314
+ size: "icon",
315
+ className: "size-6 shrink-0",
316
+ onClick: () => onRemoveCustomUrl(url),
317
+ "aria-label": `Remove ${url}`,
318
+ children: /* @__PURE__ */ jsx2(XIcon, { className: "size-3.5" })
319
+ }
320
+ )
321
+ ]
322
+ },
323
+ url
324
+ )) }) : null
325
+ ] });
326
+ };
327
+ function GoogleProductPicker({
328
+ selectedPresetIds,
329
+ onToggle,
330
+ customUrls,
331
+ onAddCustomUrl,
332
+ onRemoveCustomUrl
333
+ }) {
334
+ const [scopesOpen, setScopesOpen] = useState(false);
335
+ const groups = useMemo(
336
+ () => AUDIENCE_ORDER.flatMap((audience) => {
337
+ const presets = googleOpenApiPresets.filter(
338
+ (preset) => preset.oauthAudience === audience
339
+ );
340
+ return presets.length > 0 ? [{ audience, presets }] : [];
341
+ }),
342
+ []
343
+ );
344
+ const consentBatches = useMemo(
345
+ () => googleOAuthConsentBatches(
346
+ googleOpenApiPresets.filter((preset) => selectedPresetIds.has(preset.id)).map((preset) => ({
347
+ id: preset.id,
348
+ name: preset.name,
349
+ oauthAudience: preset.oauthAudience,
350
+ scopes: googleOAuthConsentScopesForPreset(preset.id)
351
+ }))
352
+ ),
353
+ [selectedPresetIds]
354
+ );
355
+ const selectedCount = selectedPresetIds.size + customUrls.length;
356
+ return /* @__PURE__ */ jsxs2("section", { className: "space-y-4", children: [
357
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
358
+ /* @__PURE__ */ jsx2(FieldLabel, { children: "Customize your Google connection" }),
359
+ /* @__PURE__ */ jsx2("p", { className: "text-[11px] text-muted-foreground", children: "Pick the Google APIs to bundle into one connection. They share a single OAuth consent and appear as merged tools under one Google integration." })
360
+ ] }),
361
+ groups.map(
362
+ ({
363
+ audience,
364
+ presets
365
+ }) => /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
366
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
367
+ /* @__PURE__ */ jsx2("span", { className: "text-[11px] font-semibold tracking-wide text-foreground uppercase", children: AUDIENCE_LABEL[audience] }),
368
+ audienceNeedsWarning(audience) ? /* @__PURE__ */ jsx2(AudienceWarningChip, { audience }) : null,
369
+ /* @__PURE__ */ jsx2(
370
+ Button,
371
+ {
372
+ type: "button",
373
+ variant: "ghost",
374
+ size: "sm",
375
+ className: "ml-auto h-auto px-1.5 py-0.5 text-[11px] font-normal text-muted-foreground hover:bg-transparent hover:text-foreground",
376
+ onClick: () => {
377
+ const allSelected = presets.every(
378
+ (preset) => selectedPresetIds.has(preset.id)
379
+ );
380
+ presets.forEach(
381
+ (preset) => onToggle(preset.id, !allSelected)
382
+ );
383
+ },
384
+ children: presets.every((preset) => selectedPresetIds.has(preset.id)) ? "Clear" : "Select all"
385
+ }
386
+ )
387
+ ] }),
388
+ /* @__PURE__ */ jsx2("p", { className: "text-[11px] text-muted-foreground", children: AUDIENCE_DESCRIPTION[audience] }),
389
+ /* @__PURE__ */ jsx2("div", { className: "grid grid-cols-1 gap-x-4 gap-y-0.5 sm:grid-cols-2", children: presets.map((preset) => /* @__PURE__ */ jsx2(
390
+ ProductRow,
391
+ {
392
+ preset,
393
+ checked: selectedPresetIds.has(preset.id),
394
+ onToggle: (checked) => onToggle(preset.id, checked)
395
+ },
396
+ preset.id
397
+ )) })
398
+ ] }, audience)
399
+ ),
400
+ /* @__PURE__ */ jsx2(
401
+ CustomUrlEscapeHatch,
402
+ {
403
+ customUrls,
404
+ onAddCustomUrl,
405
+ onRemoveCustomUrl
406
+ }
407
+ ),
408
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5 rounded-lg border border-border bg-muted/10 px-3 py-2.5", children: [
409
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
410
+ /* @__PURE__ */ jsx2("span", { className: "text-[11px] font-semibold tracking-wide text-foreground uppercase", children: "Authentication" }),
411
+ /* @__PURE__ */ jsx2(Badge, { variant: "secondary", children: "OAuth" })
412
+ ] }),
413
+ /* @__PURE__ */ jsx2("p", { className: "text-[11px] text-muted-foreground", children: "The selected Google APIs share one OAuth consent. Review the scopes below, then connect a Google account from the integration page after adding." })
414
+ ] }),
415
+ /* @__PURE__ */ jsxs2(Collapsible, { open: scopesOpen, onOpenChange: setScopesOpen, children: [
416
+ /* @__PURE__ */ jsx2(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(Button, { type: "button", variant: "outline", size: "sm", disabled: consentBatches.length === 0, children: [
417
+ /* @__PURE__ */ jsx2(
418
+ ChevronDownIcon,
419
+ {
420
+ className: cn("size-3.5 transition-transform", scopesOpen ? "rotate-180" : "")
421
+ }
422
+ ),
423
+ "View scopes",
424
+ selectedCount > 0 ? /* @__PURE__ */ jsx2(Badge, { variant: "secondary", className: "ml-1", children: selectedCount }) : null
425
+ ] }) }),
426
+ /* @__PURE__ */ jsx2(CollapsibleContent, { className: "pt-3", children: consentBatches.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "text-[11px] text-muted-foreground", children: "Select at least one Google API to preview the OAuth consent." }) : /* @__PURE__ */ jsx2("div", { className: "space-y-3", children: consentBatches.map((batch) => /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
427
+ /* @__PURE__ */ jsx2("span", { className: "text-[11px] font-semibold text-foreground", children: batch.label }),
428
+ /* @__PURE__ */ jsx2("ul", { className: "space-y-1", children: batch.apiScopes.map((scope) => /* @__PURE__ */ jsx2(
429
+ "li",
430
+ {
431
+ className: "rounded-md border border-border bg-muted/20 px-2.5 py-1 font-mono text-[11px] break-all text-muted-foreground",
432
+ children: scope
433
+ },
434
+ scope
435
+ )) })
436
+ ] }, batch.id)) }) })
437
+ ] })
438
+ ] });
439
+ }
440
+
441
+ // src/react/AddOpenApiSource.tsx
442
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
443
+ var GOOGLE_BUNDLE_BASE_URL = "https://www.googleapis.com/";
444
+ var GOOGLE_BUNDLE_FAVICON = "https://fonts.gstatic.com/s/i/productlogos/googleg/v6/192px.svg";
445
+ var googleBundleDefaultPresetIds = new Set(
446
+ googleOpenApiPresets.filter((preset) => preset.featured).map((preset) => preset.id)
447
+ );
448
+ var googleBundleUrls = (selectedPresetIds, customUrls) => {
449
+ const fromPresets = googleOpenApiPresets.flatMap(
450
+ (preset) => preset.url && selectedPresetIds.has(preset.id) ? [preset.url] : []
451
+ );
452
+ return [.../* @__PURE__ */ new Set([...fromPresets, ...customUrls])];
453
+ };
454
+ var ErrorMessage = Schema.Struct({ message: Schema.String });
455
+ var decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage);
456
+ var errorMessageFromExit = (exit, fallback) => Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), {
457
+ onNone: () => fallback,
458
+ onSome: ({ message }) => message
459
+ });
460
+ var isIntegrationAlreadyExistsExit = (exit) => Option.match(Exit.findErrorOption(exit), {
461
+ onNone: () => false,
462
+ onSome: Predicate.isTagged("IntegrationAlreadyExistsError")
463
+ });
464
+ var integrationExistsMessage = (slug) => `An integration named "${slug}" already exists. To add more authentication, update your existing integration.`;
465
+ function resolveOAuthUrl(url, baseUrl) {
466
+ if (!url) return url;
467
+ try {
468
+ new URL(url);
469
+ return url;
470
+ } catch {
471
+ if (!baseUrl) return url;
472
+ try {
473
+ return new URL(url, baseUrl).toString();
474
+ } catch {
475
+ return url;
476
+ }
477
+ }
478
+ }
479
+ var standardOidcIdentityScopes = ["openid", "email", "profile"];
480
+ var identityScopesForPreset = (identityScopes) => {
481
+ if (identityScopes === false) return [];
482
+ return identityScopes === "auto" ? standardOidcIdentityScopes : identityScopes;
483
+ };
484
+ var resolvedOAuthScopes = (apiScopes, identityScopes) => {
485
+ const merged = new Set(apiScopes);
486
+ for (const scope of identityScopesForPreset(identityScopes)) merged.add(scope);
487
+ return [...merged];
488
+ };
489
+ var isGoogleDiscoveryUrl2 = (url) => {
490
+ const trimmed = url.trim();
491
+ if (!URL.canParse(trimmed)) return false;
492
+ const parsed = new URL(trimmed);
493
+ const host = parsed.hostname.toLowerCase();
494
+ if (!host.endsWith("googleapis.com")) return false;
495
+ return parsed.pathname.includes("/discovery/") || parsed.pathname.includes("$discovery");
496
+ };
497
+ var normalizePresetUrl = (url) => {
498
+ const trimmed = url.trim();
499
+ if (!URL.canParse(trimmed)) return trimmed.replace(/\/$/, "");
500
+ const parsed = new URL(trimmed);
501
+ parsed.hash = "";
502
+ parsed.searchParams.sort();
503
+ return parsed.toString().replace(/\/$/, "");
504
+ };
505
+ var specInputForAdd = (input) => {
506
+ const value = input.trim();
507
+ const parsed = Effect.runSyncExit(
508
+ Effect.try({
509
+ try: () => new URL(value),
510
+ catch: () => null
511
+ })
512
+ );
513
+ return Exit.isSuccess(parsed) ? isGoogleDiscoveryUrl2(value) ? { kind: "googleDiscovery", url: value } : { kind: "url", url: value } : { kind: "blob", value };
514
+ };
515
+ var headerPrefix = (preset, headerName) => {
516
+ const label = preset.label.toLowerCase();
517
+ if (headerName.toLowerCase() === "authorization") {
518
+ if (label.includes("bearer")) return "Bearer ";
519
+ if (label.includes("basic")) return "Basic ";
520
+ }
521
+ return void 0;
522
+ };
523
+ var apiKeyTemplateFromHeaderPreset = (preset, slug) => {
524
+ const headers = {};
525
+ for (const headerName of preset.secretHeaders) {
526
+ const prefix = headerPrefix(preset, headerName);
527
+ headers[headerName] = prefix ? [prefix, variable(TOKEN_VARIABLE)] : [variable(TOKEN_VARIABLE)];
528
+ }
529
+ return { slug, type: "apiKey", headers };
530
+ };
531
+ var oauthTemplateFromPreset = (preset, baseUrl, slug, scopes) => ({
532
+ slug,
533
+ type: "oauth",
534
+ authorizationUrl: resolveOAuthUrl(
535
+ Option.getOrElse(preset.authorizationUrl, () => ""),
536
+ baseUrl
537
+ ),
538
+ tokenUrl: resolveOAuthUrl(preset.tokenUrl, baseUrl),
539
+ scopes: [...scopes]
540
+ });
541
+ var expandServerOptions = (server) => expandServerUrlOptions(server).map((value) => ({ value, label: value }));
542
+ var firstBaseUrlForPreview = (preview) => {
543
+ const firstServer = preview.servers[0];
544
+ return firstServer ? expandServerUrlOptions(firstServer)[0] ?? "" : "";
545
+ };
546
+ var detectedAuthenticationTemplates = (headerPresets, oauth2Presets, baseUrl) => {
547
+ const templates = [];
548
+ headerPresets.forEach((preset, index) => {
549
+ templates.push(
550
+ apiKeyTemplateFromHeaderPreset(preset, AuthTemplateSlug.make(`apikey-${index}`))
551
+ );
552
+ });
553
+ for (const preset of oauth2Presets) {
554
+ const scopes = resolvedOAuthScopes(Object.keys(preset.scopes), preset.identityScopes);
555
+ templates.push(
556
+ oauthTemplateFromPreset(
557
+ preset,
558
+ baseUrl,
559
+ AuthTemplateSlug.make(`oauth-${preset.securitySchemeName}`),
560
+ scopes
561
+ )
562
+ );
563
+ }
564
+ return templates;
565
+ };
566
+ function AddOpenApiSource(props) {
567
+ const isGoogleBundlePreset = props.initialPreset === GOOGLE_BUNDLE_PRESET_ID;
568
+ const [specUrl, setSpecUrl] = useState2(props.initialUrl ?? "");
569
+ const [selectedPresetIds, setSelectedPresetIds] = useState2(
570
+ googleBundleDefaultPresetIds
571
+ );
572
+ const [customDiscoveryUrls, setCustomDiscoveryUrls] = useState2([]);
573
+ const [analyzing, setAnalyzing] = useState2(false);
574
+ const [analyzeError, setAnalyzeError] = useState2(null);
575
+ const [preview, setPreview] = useState2(null);
576
+ const [baseUrl, setBaseUrl] = useState2(isGoogleBundlePreset ? GOOGLE_BUNDLE_BASE_URL : "");
577
+ const identityFallbackName = isGoogleBundlePreset ? "Google" : preview ? Option.getOrElse(preview.title, () => "") : "";
578
+ const identity = useIntegrationIdentity({
579
+ fallbackName: identityFallbackName,
580
+ fallbackNamespace: props.initialNamespace ?? (isGoogleBundlePreset ? "google" : void 0)
581
+ });
582
+ const bundleDiscoveryUrls = useMemo2(
583
+ () => googleBundleUrls(selectedPresetIds, customDiscoveryUrls),
584
+ [selectedPresetIds, customDiscoveryUrls]
585
+ );
586
+ const toggleBundlePreset = useCallback((presetId, checked) => {
587
+ setSelectedPresetIds((current) => {
588
+ const next = new Set(current);
589
+ if (checked) next.add(presetId);
590
+ else next.delete(presetId);
591
+ return next;
592
+ });
593
+ }, []);
594
+ const addCustomDiscoveryUrl = useCallback((url) => {
595
+ setCustomDiscoveryUrls(
596
+ (current) => current.includes(url) ? current : [...current, url]
597
+ );
598
+ }, []);
599
+ const removeCustomDiscoveryUrl = useCallback((url) => {
600
+ setCustomDiscoveryUrls(
601
+ (current) => current.filter((entry) => entry !== url)
602
+ );
603
+ }, []);
604
+ const [adding, setAdding] = useState2(false);
605
+ const [addError, setAddError] = useState2(null);
606
+ const doPreview = useAtomSet(previewOpenApiSpec, { mode: "promiseExit" });
607
+ const doAdd = useAtomSet(addOpenApiSpec, { mode: "promiseExit" });
608
+ const handleAnalyzeRef = useRef(() => {
609
+ });
610
+ useEffect(() => {
611
+ if (isGoogleBundlePreset) return;
612
+ const trimmed = specUrl.trim();
613
+ if (!trimmed) return;
614
+ if (preview) return;
615
+ const handle = setTimeout(() => {
616
+ handleAnalyzeRef.current();
617
+ }, 400);
618
+ return () => clearTimeout(handle);
619
+ }, [specUrl, preview, isGoogleBundlePreset]);
620
+ const servers = preview?.servers ?? [];
621
+ const baseUrlOptions = Array.from(
622
+ new Map(servers.flatMap(expandServerOptions).map((option) => [option.value, option])).values()
623
+ );
624
+ const previewPresetIcon = openApiPresets.find(
625
+ (preset) => preset.url && normalizePresetUrl(preset.url) === normalizePresetUrl(specUrl)
626
+ )?.icon ?? null;
627
+ const resolvedBaseUrl = baseUrl.trim();
628
+ const resolvedSourceId = slugifyNamespace(identity.namespace) || (preview ? Option.getOrElse(preview.title, () => "openapi") : "openapi");
629
+ const resolvedDisplayName = identity.name.trim() || (preview ? Option.getOrElse(preview.title, () => resolvedSourceId) : resolvedSourceId);
630
+ const authenticationTemplate = useMemo2(
631
+ () => detectedAuthenticationTemplates(
632
+ preview?.headerPresets ?? [],
633
+ preview?.oauth2Presets ?? [],
634
+ resolvedBaseUrl
635
+ ),
636
+ [preview, resolvedBaseUrl]
637
+ );
638
+ const detectedMethodLabels = useMemo2(
639
+ () => [
640
+ ...(preview?.headerPresets ?? []).map((preset) => preset.label),
641
+ ...(preview?.oauth2Presets ?? []).map((preset) => preset.label)
642
+ ],
643
+ [preview]
644
+ );
645
+ const [authMethods, setAuthMethods] = useState2([]);
646
+ const seededFromRef = useRef(null);
647
+ useEffect(() => {
648
+ if (seededFromRef.current === authenticationTemplate) return;
649
+ seededFromRef.current = authenticationTemplate;
650
+ setAuthMethods(
651
+ authenticationTemplate.map((template) => ({
652
+ value: editorValueFromAuthentication(template),
653
+ seedSlug: String(template.slug)
654
+ }))
655
+ );
656
+ }, [authenticationTemplate]);
657
+ const setAuthMethodAt = useCallback((index, next) => {
658
+ setAuthMethods(
659
+ (current) => current.map((row, i) => i === index ? { ...row, value: next } : row)
660
+ );
661
+ }, []);
662
+ const removeAuthMethodAt = useCallback((index) => {
663
+ setAuthMethods(
664
+ (current) => current.filter((_row, i) => i !== index)
665
+ );
666
+ }, []);
667
+ const addAuthMethod = useCallback(() => {
668
+ setAuthMethods((current) => [
669
+ ...current,
670
+ {
671
+ value: {
672
+ kind: "apikey",
673
+ placements: [{ carrier: "header", name: "Authorization", prefix: "" }]
674
+ }
675
+ }
676
+ ]);
677
+ }, []);
678
+ const editedAuthenticationTemplate = useMemo2(() => {
679
+ const templates = [];
680
+ authMethods.forEach((row, index) => {
681
+ const slug = row.seedSlug ?? (row.value.kind === "oauth" ? `oauth-${index}` : `apikey-${index}`);
682
+ const template = authenticationFromEditorValue(row.value, slug);
683
+ if (template !== null) templates.push(template);
684
+ });
685
+ return templates;
686
+ }, [authMethods]);
687
+ const integrationsResult = useAtomValue(integrationsOptimisticAtom);
688
+ const slugAlreadyExists = useMemo2(
689
+ () => AsyncResult.isSuccess(integrationsResult) && integrationsResult.value.some((integration) => integration.slug === resolvedSourceId),
690
+ [integrationsResult, resolvedSourceId]
691
+ );
692
+ const hasPreviewOrBundle = isGoogleBundlePreset ? bundleDiscoveryUrls.length > 0 : preview !== null;
693
+ const canAdd = hasPreviewOrBundle && resolvedBaseUrl.length > 0 && !slugAlreadyExists;
694
+ const handleAnalyze = async () => {
695
+ setAnalyzing(true);
696
+ setAnalyzeError(null);
697
+ setAddError(null);
698
+ const exit = await doPreview({ payload: { spec: specUrl } });
699
+ if (Exit.isFailure(exit)) {
700
+ setAnalyzeError(errorMessageFromExit(exit, "Failed to parse spec"));
701
+ setAnalyzing(false);
702
+ return;
703
+ }
704
+ const result = exit.value;
705
+ setPreview(result);
706
+ setBaseUrl(firstBaseUrlForPreview(result));
707
+ setAnalyzing(false);
708
+ };
709
+ handleAnalyzeRef.current = handleAnalyze;
710
+ const ensureIntegration = useCallback(async () => {
711
+ const specForAdd = isGoogleBundlePreset ? { kind: "googleDiscoveryBundle", urls: [...bundleDiscoveryUrls] } : specInputForAdd(specUrl);
712
+ const exit = await doAdd({
713
+ payload: {
714
+ spec: specForAdd,
715
+ slug: resolvedSourceId,
716
+ description: resolvedDisplayName,
717
+ baseUrl: resolvedBaseUrl,
718
+ ...!isGoogleBundlePreset && editedAuthenticationTemplate.length > 0 ? {
719
+ authenticationTemplate: editedAuthenticationTemplate.map((entry) => ({
720
+ ...entry,
721
+ slug: String(entry.slug)
722
+ }))
723
+ } : {}
724
+ },
725
+ reactivityKeys: integrationWriteKeys
726
+ });
727
+ if (Exit.isFailure(exit)) {
728
+ setAddError(
729
+ isIntegrationAlreadyExistsExit(exit) ? integrationExistsMessage(resolvedSourceId) : errorMessageFromExit(exit, "Failed to add integration")
730
+ );
731
+ return null;
732
+ }
733
+ return exit.value.slug;
734
+ }, [
735
+ isGoogleBundlePreset,
736
+ bundleDiscoveryUrls,
737
+ specUrl,
738
+ doAdd,
739
+ resolvedSourceId,
740
+ resolvedDisplayName,
741
+ resolvedBaseUrl,
742
+ editedAuthenticationTemplate
743
+ ]);
744
+ const handleAdd = async () => {
745
+ setAdding(true);
746
+ setAddError(null);
747
+ const integration = await ensureIntegration();
748
+ if (!integration) {
749
+ setAdding(false);
750
+ return;
751
+ }
752
+ props.onComplete(String(integration));
753
+ };
754
+ return /* @__PURE__ */ jsxs3("div", { className: "flex flex-1 flex-col gap-6", children: [
755
+ /* @__PURE__ */ jsxs3("div", { children: [
756
+ /* @__PURE__ */ jsx3("h1", { className: "text-xl font-semibold text-foreground", children: isGoogleBundlePreset ? "Add Google" : "Add OpenAPI Integration" }),
757
+ isGoogleBundlePreset ? /* @__PURE__ */ jsx3("p", { className: "mt-1 text-[13px] text-muted-foreground", children: "Bundle Google APIs into one integration from their Discovery documents and register their methods as tools under a single shared OAuth consent." }) : null
758
+ ] }),
759
+ isGoogleBundlePreset ? /* @__PURE__ */ jsx3(
760
+ GoogleProductPicker,
761
+ {
762
+ selectedPresetIds,
763
+ onToggle: toggleBundlePreset,
764
+ customUrls: customDiscoveryUrls,
765
+ onAddCustomUrl: addCustomDiscoveryUrl,
766
+ onRemoveCustomUrl: removeCustomDiscoveryUrl
767
+ }
768
+ ) : !preview ? /* @__PURE__ */ jsx3(CardStack2, { children: /* @__PURE__ */ jsx3(CardStackContent2, { className: "border-t-0", children: /* @__PURE__ */ jsxs3("div", { className: "space-y-2 p-3", children: [
769
+ /* @__PURE__ */ jsx3(FieldLabel2, { children: "OpenAPI Spec" }),
770
+ /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
771
+ /* @__PURE__ */ jsx3(
772
+ Textarea,
773
+ {
774
+ value: specUrl,
775
+ onChange: (e) => setSpecUrl(e.target.value),
776
+ placeholder: "https://api.example.com/openapi.json",
777
+ rows: 3,
778
+ maxRows: 10,
779
+ className: "font-mono text-sm"
780
+ }
781
+ ),
782
+ analyzing && /* @__PURE__ */ jsx3("div", { className: "pointer-events-none absolute right-2 top-2", children: /* @__PURE__ */ jsx3(IOSSpinner, { className: "size-4" }) })
783
+ ] }),
784
+ /* @__PURE__ */ jsx3("p", { className: "text-[11px] text-muted-foreground", children: "Paste a URL or raw JSON/YAML content." })
785
+ ] }) }) }) : null,
786
+ isGoogleBundlePreset ? /* @__PURE__ */ jsx3(
787
+ OpenApiSourceDetailsFields,
788
+ {
789
+ title: "Google",
790
+ description: `${bundleDiscoveryUrls.length} Google API${bundleDiscoveryUrls.length !== 1 ? "s" : ""} \xB7 one shared OAuth consent`,
791
+ identity,
792
+ baseUrl: resolvedBaseUrl,
793
+ onBaseUrlChange: setBaseUrl,
794
+ faviconIcon: GOOGLE_BUNDLE_FAVICON,
795
+ faviconUrl: resolvedBaseUrl,
796
+ baseUrlMissingMessage: "A base URL is required to make requests."
797
+ }
798
+ ) : preview ? /* @__PURE__ */ jsx3(
799
+ OpenApiSourceDetailsFields,
800
+ {
801
+ title: Option.getOrElse(preview.title, () => "API"),
802
+ description: `${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" : ""}` : ""}`,
803
+ identity,
804
+ baseUrl: resolvedBaseUrl,
805
+ onBaseUrlChange: setBaseUrl,
806
+ baseUrlOptions,
807
+ specUrl,
808
+ onSpecUrlChange: (value) => {
809
+ setSpecUrl(value);
810
+ setPreview(null);
811
+ setBaseUrl("");
812
+ },
813
+ faviconIcon: previewPresetIcon,
814
+ faviconUrl: resolvedBaseUrl,
815
+ baseUrlMissingMessage: "A base URL is required to make requests."
816
+ }
817
+ ) : null,
818
+ analyzeError && /* @__PURE__ */ jsx3("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx3("p", { className: "text-[12px] text-destructive", children: analyzeError }) }),
819
+ preview && !isGoogleBundlePreset && /* @__PURE__ */ jsxs3("section", { className: "space-y-3", children: [
820
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-3", children: [
821
+ /* @__PURE__ */ jsx3(FieldLabel2, { children: "How does this API authenticate?" }),
822
+ /* @__PURE__ */ jsxs3(Button2, { type: "button", variant: "outline", size: "sm", onClick: addAuthMethod, children: [
823
+ /* @__PURE__ */ jsx3(PlusIcon2, {}),
824
+ "Add method"
825
+ ] })
826
+ ] }),
827
+ authMethods.length === 0 ? /* @__PURE__ */ jsx3("p", { className: "text-[11px] text-muted-foreground", children: "No authentication detected. Add a method, or add the integration without auth and connect an account from the integration page later." }) : /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-3", children: authMethods.map((row, index) => /* @__PURE__ */ jsxs3(
828
+ "div",
829
+ {
830
+ className: "space-y-2 rounded-lg border border-border/60 bg-muted/20 p-3",
831
+ children: [
832
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between", children: [
833
+ /* @__PURE__ */ jsxs3("span", { className: "text-xs font-medium text-muted-foreground", children: [
834
+ "Method ",
835
+ index + 1,
836
+ detectedMethodLabels[index] ? ` \xB7 ${detectedMethodLabels[index]}` : ""
837
+ ] }),
838
+ /* @__PURE__ */ jsx3(
839
+ Button2,
840
+ {
841
+ type: "button",
842
+ variant: "ghost",
843
+ size: "icon-sm",
844
+ "aria-label": "Remove method",
845
+ className: "text-muted-foreground hover:text-foreground",
846
+ onClick: () => removeAuthMethodAt(index),
847
+ children: /* @__PURE__ */ jsx3(XIcon2, {})
848
+ }
849
+ )
850
+ ] }),
851
+ /* @__PURE__ */ jsx3(
852
+ AuthTemplateEditor,
853
+ {
854
+ value: row.value,
855
+ onChange: (next) => setAuthMethodAt(index, next)
856
+ }
857
+ )
858
+ ]
859
+ },
860
+ index
861
+ )) }),
862
+ /* @__PURE__ */ jsx3("p", { className: "text-[11px] text-muted-foreground", children: "Every method here is registered with the integration. Connect an account from the integration page after adding." })
863
+ ] }),
864
+ hasPreviewOrBundle && slugAlreadyExists && !adding && /* @__PURE__ */ jsx3("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsxs3("p", { className: "text-[12px] text-destructive", children: [
865
+ 'An integration named "',
866
+ resolvedSourceId,
867
+ '" already exists. To add more authentication, update your existing integration.',
868
+ " ",
869
+ /* @__PURE__ */ jsx3(
870
+ Link,
871
+ {
872
+ to: "/integrations/$namespace",
873
+ params: { namespace: resolvedSourceId },
874
+ className: "font-medium underline underline-offset-2",
875
+ children: "Open it"
876
+ }
877
+ )
878
+ ] }) }),
879
+ addError && /* @__PURE__ */ jsx3("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx3("p", { className: "text-[12px] text-destructive", children: addError }) }),
880
+ /* @__PURE__ */ jsxs3(FloatActions, { children: [
881
+ /* @__PURE__ */ jsx3(Button2, { variant: "ghost", onClick: () => props.onCancel(), disabled: adding, children: "Cancel" }),
882
+ (hasPreviewOrBundle || isGoogleBundlePreset) && /* @__PURE__ */ jsxs3(Button2, { onClick: () => void handleAdd(), disabled: !canAdd || adding, children: [
883
+ adding && /* @__PURE__ */ jsx3(Spinner, { className: "size-3.5" }),
884
+ adding ? "Adding\u2026" : isGoogleBundlePreset ? "Connect Google" : "Add integration"
885
+ ] })
886
+ ] })
887
+ ] });
888
+ }
889
+ export {
890
+ AddOpenApiSource as default,
891
+ resolveOAuthUrl
892
+ };
893
+ //# sourceMappingURL=AddOpenApiSource-7M52SRUX.js.map