@executor-js/plugin-openapi 1.4.33 → 1.5.0

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
@@ -1,1181 +0,0 @@
1
- import {
2
- addOpenApiSpecOptimistic,
3
- previewOpenApiSpec
4
- } from "./chunk-X5JX3KTA.js";
5
- import {
6
- OAuth2SourceConfig,
7
- expandServerUrlOptions,
8
- headerBindingSlot,
9
- oauth2ClientIdSlot,
10
- oauth2ClientSecretSlot,
11
- oauth2ConnectionSlot,
12
- queryParamBindingSlot,
13
- specFetchHeaderBindingSlot,
14
- specFetchQueryParamBindingSlot
15
- } from "./chunk-AN4HJFNP.js";
16
-
17
- // src/react/AddOpenApiSource.tsx
18
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
19
- import { useAtomSet } from "@effect/atom-react";
20
- import * as Effect from "effect/Effect";
21
- import * as Exit from "effect/Exit";
22
- import * as Match from "effect/Match";
23
- import * as Option from "effect/Option";
24
- import * as Schema from "effect/Schema";
25
- import {
26
- ConnectionId,
27
- ScopeId,
28
- SecretId,
29
- SetSourceCredentialBindingInput
30
- } from "@executor-js/sdk/shared";
31
- import { setSourceCredentialBinding, startOAuth } from "@executor-js/react/api/atoms";
32
- import { useScope, useScopeStack } from "@executor-js/react/api/scope-context";
33
- import { connectionWriteKeys, sourceWriteKeys } from "@executor-js/react/api/reactivity-keys";
34
- import { HeadersList } from "@executor-js/react/plugins/headers-list";
35
- import {
36
- HttpCredentialsEditor,
37
- emptyHttpCredentials,
38
- serializeHttpCredentials
39
- } from "@executor-js/react/plugins/http-credentials";
40
- import {
41
- oauthCallbackUrl,
42
- useOAuthPopupFlow
43
- } from "@executor-js/react/plugins/oauth-sign-in";
44
- import {
45
- CreatableSecretPicker,
46
- matchPresetKey
47
- } from "@executor-js/react/plugins/secret-header-auth";
48
- import { CredentialScopeDropdown } from "@executor-js/react/plugins/credential-target-scope";
49
- import { slugifyNamespace, useSourceIdentity } from "@executor-js/react/plugins/source-identity";
50
- import { useSecretPickerSecrets } from "@executor-js/react/plugins/use-secret-picker-secrets";
51
- import { Button } from "@executor-js/react/components/button";
52
- import { CopyButton } from "@executor-js/react/components/copy-button";
53
- import {
54
- Collapsible,
55
- CollapsibleContent,
56
- CollapsibleTrigger
57
- } from "@executor-js/react/components/collapsible";
58
- import {
59
- CardStack as CardStack2,
60
- CardStackContent as CardStackContent2,
61
- CardStackEntryField as CardStackEntryField2
62
- } from "@executor-js/react/components/card-stack";
63
- import { FieldLabel } from "@executor-js/react/components/field";
64
- import { FloatActions } from "@executor-js/react/components/float-actions";
65
- import { HelpTooltip } from "@executor-js/react/components/help-tooltip";
66
- import { Label } from "@executor-js/react/components/label";
67
- import { Textarea } from "@executor-js/react/components/textarea";
68
- import { Checkbox } from "@executor-js/react/components/checkbox";
69
- import { RadioGroup, RadioGroupItem } from "@executor-js/react/components/radio-group";
70
- import { IOSSpinner, Spinner } from "@executor-js/react/components/spinner";
71
-
72
- // src/react/OpenApiSourceDetailsFields.tsx
73
- import {
74
- CardStack,
75
- CardStackContent,
76
- CardStackEntry,
77
- CardStackEntryContent,
78
- CardStackEntryDescription,
79
- CardStackEntryField,
80
- CardStackEntryTitle
81
- } from "@executor-js/react/components/card-stack";
82
- import {
83
- FreeformCombobox
84
- } from "@executor-js/react/components/combobox";
85
- import { Input } from "@executor-js/react/components/input";
86
- import { SourceFavicon } from "@executor-js/react/components/source-favicon";
87
- import {
88
- SourceIdentityFieldRows
89
- } from "@executor-js/react/plugins/source-identity";
90
- import { jsx, jsxs } from "react/jsx-runtime";
91
- function OpenApiSourceDetailsFields(props) {
92
- const baseUrlOptions = props.baseUrlOptions ?? [];
93
- return /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsxs(CardStackContent, { className: "border-t-0", children: [
94
- /* @__PURE__ */ jsxs(CardStackEntry, { children: [
95
- props.faviconUrl && /* @__PURE__ */ jsx(SourceFavicon, { url: props.faviconUrl, size: 16 }),
96
- /* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
97
- /* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.title }),
98
- props.description && /* @__PURE__ */ jsx(CardStackEntryDescription, { children: props.description })
99
- ] }),
100
- props.saveState && props.saveState !== "idle" && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: props.saveState === "saving" ? "Saving\u2026" : "Saved" })
101
- ] }),
102
- /* @__PURE__ */ jsx(
103
- SourceIdentityFieldRows,
104
- {
105
- identity: props.identity,
106
- namespaceReadOnly: props.namespaceReadOnly
107
- }
108
- ),
109
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2", children: [
110
- /* @__PURE__ */ jsxs(CardStackEntryField, { label: "Base URL", children: [
111
- baseUrlOptions.length > 0 ? /* @__PURE__ */ jsx(
112
- FreeformCombobox,
113
- {
114
- value: props.baseUrl,
115
- onValueChange: props.onBaseUrlChange,
116
- options: baseUrlOptions,
117
- placeholder: "https://api.example.com",
118
- className: "w-full",
119
- inputClassName: "font-mono text-sm"
120
- }
121
- ) : /* @__PURE__ */ jsx(
122
- Input,
123
- {
124
- value: props.baseUrl,
125
- onChange: (e) => props.onBaseUrlChange(e.target.value),
126
- placeholder: "https://api.example.com",
127
- className: "font-mono text-sm"
128
- }
129
- ),
130
- props.baseUrlMissingMessage && !props.baseUrl && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-amber-600 dark:text-amber-400", children: props.baseUrlMissingMessage })
131
- ] }),
132
- props.specUrl !== void 0 && props.onSpecUrlChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Spec URL", children: /* @__PURE__ */ jsx(
133
- Input,
134
- {
135
- value: props.specUrl,
136
- onChange: (e) => props.onSpecUrlChange?.(e.target.value),
137
- placeholder: "https://api.example.com/openapi.json",
138
- className: "font-mono text-sm",
139
- disabled: props.specUrlDisabled
140
- }
141
- ) })
142
- ] }),
143
- props.footer && /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsx(CardStackEntryContent, { children: /* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.footer }) }) })
144
- ] }) });
145
- }
146
-
147
- // src/react/AddOpenApiSource.tsx
148
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
149
- var addSpecWriteKeys = [...sourceWriteKeys, ...connectionWriteKeys];
150
- var bindingWriteKeys = [...sourceWriteKeys, ...connectionWriteKeys];
151
- var OPENAPI_OAUTH_POPUP_NAME = "openapi-oauth";
152
- var OPENAPI_OAUTH_CALLBACK_PATH = "/api/oauth/callback";
153
- var ErrorMessage = Schema.Struct({ message: Schema.String });
154
- var decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage);
155
- var errorMessageFromExit = (exit, fallback) => Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), {
156
- onNone: () => fallback,
157
- onSome: ({ message }) => message
158
- });
159
- var openApiOAuthConnectionId = (namespaceSlug, flow) => flow === "clientCredentials" ? `openapi-oauth2-app-${namespaceSlug || "default"}` : `openapi-oauth2-user-${namespaceSlug || "default"}`;
160
- function resolveOAuthUrl(url, baseUrl) {
161
- if (!url) return url;
162
- try {
163
- new URL(url);
164
- return url;
165
- } catch {
166
- if (!baseUrl) return url;
167
- try {
168
- return new URL(url, baseUrl).toString();
169
- } catch {
170
- return url;
171
- }
172
- }
173
- }
174
- function inferOAuthIssuerUrl(authorizationUrl) {
175
- try {
176
- return new URL(authorizationUrl).origin;
177
- } catch {
178
- return null;
179
- }
180
- }
181
- var specInputForAdd = (input) => {
182
- const value2 = input.trim();
183
- const parsed = Effect.runSyncExit(
184
- Effect.try({
185
- try: () => new URL(value2),
186
- catch: () => null
187
- })
188
- );
189
- return Exit.isSuccess(parsed) ? { kind: "url", url: value2 } : { kind: "blob", value: value2 };
190
- };
191
- var serializeStrategy = (s) => Match.value(s).pipe(
192
- Match.when({ kind: "none" }, () => "none"),
193
- Match.when({ kind: "custom" }, () => "custom"),
194
- Match.when({ kind: "header" }, (sel) => `header:${sel.presetIndex}`),
195
- Match.when({ kind: "oauth2" }, (sel) => `oauth2:${sel.presetIndex}`),
196
- Match.exhaustive
197
- );
198
- var parseStrategy = (value2) => {
199
- if (value2 === "none") return { kind: "none" };
200
- if (value2 === "custom") return { kind: "custom" };
201
- if (value2.startsWith("header:")) {
202
- return {
203
- kind: "header",
204
- presetIndex: Number(value2.slice("header:".length))
205
- };
206
- }
207
- if (value2.startsWith("oauth2:")) {
208
- return {
209
- kind: "oauth2",
210
- presetIndex: Number(value2.slice("oauth2:".length))
211
- };
212
- }
213
- return { kind: "none" };
214
- };
215
- function prefixForHeader(preset, headerName) {
216
- const label = preset.label.toLowerCase();
217
- if (headerName.toLowerCase() === "authorization") {
218
- if (label.includes("bearer")) return "Bearer ";
219
- if (label.includes("basic")) return "Basic ";
220
- }
221
- return void 0;
222
- }
223
- function entriesFromSpecPreset(preset) {
224
- return preset.secretHeaders.map((headerName) => {
225
- const prefix = prefixForHeader(preset, headerName);
226
- return {
227
- name: headerName,
228
- secretId: null,
229
- prefix,
230
- presetKey: matchPresetKey(headerName, prefix),
231
- fromPreset: true
232
- };
233
- });
234
- }
235
- var secretStorageDescription = (label) => label === "Personal" ? "Only you can use this secret." : "Everyone in the organization can use this secret.";
236
- function AddOpenApiSource(props) {
237
- const [specUrl, setSpecUrl] = useState(props.initialUrl ?? "");
238
- const [analyzing, setAnalyzing] = useState(false);
239
- const [analyzeError, setAnalyzeError] = useState(null);
240
- const [preview, setPreview] = useState(null);
241
- const [baseUrl, setBaseUrl] = useState("");
242
- const identity = useSourceIdentity({
243
- fallbackName: preview ? Option.getOrElse(preview.title, () => "") : "",
244
- fallbackNamespace: props.initialNamespace
245
- });
246
- const [strategy, setStrategy] = useState({ kind: "none" });
247
- const [customHeaders, setCustomHeaders] = useState([]);
248
- const [specFetchCredentials, setSpecFetchCredentials] = useState(
249
- () => emptyHttpCredentials()
250
- );
251
- const [specFetchCredentialsOpen, setSpecFetchCredentialsOpen] = useState(false);
252
- const [runtimeCredentials, setRuntimeCredentials] = useState(
253
- () => emptyHttpCredentials()
254
- );
255
- const [oauth2ClientIdSecretId, setOauth2ClientIdSecretId] = useState(null);
256
- const [oauth2ClientSecretSecretId, setOauth2ClientSecretSecretId] = useState(null);
257
- const [oauth2ClientIdScope, setOauth2ClientIdScope] = useState(null);
258
- const [oauth2ClientSecretScope, setOauth2ClientSecretScope] = useState(null);
259
- const [oauth2SelectedScopes, setOauth2SelectedScopes] = useState(/* @__PURE__ */ new Set());
260
- const [oauth2AuthState, setOauth2AuthState] = useState(null);
261
- const [startingOAuth, setStartingOAuth] = useState(false);
262
- const [oauth2Error, setOauth2Error] = useState(null);
263
- const [adding, setAdding] = useState(false);
264
- const [addError, setAddError] = useState(null);
265
- const scopeId = useScope();
266
- const scopeStack = useScopeStack();
267
- const credentialScopeOptions = useMemo(
268
- () => scopeStack.map((entry, index) => ({
269
- scopeId: entry.id,
270
- label: index === 0 ? "Personal" : entry.name || "Organization",
271
- description: secretStorageDescription(
272
- index === 0 ? "Personal" : entry.name || "Organization"
273
- )
274
- })),
275
- [scopeStack]
276
- );
277
- const defaultOAuthTokenTargetScope = credentialScopeOptions[0]?.scopeId ?? scopeId;
278
- const [oauthTokenTargetScope, setOAuthTokenTargetScope] = useState(
279
- defaultOAuthTokenTargetScope
280
- );
281
- useEffect(() => {
282
- if (!credentialScopeOptions.some((option) => option.scopeId === oauthTokenTargetScope)) {
283
- setOAuthTokenTargetScope(defaultOAuthTokenTargetScope);
284
- }
285
- }, [credentialScopeOptions, defaultOAuthTokenTargetScope, oauthTokenTargetScope]);
286
- useEffect(() => {
287
- if (oauth2ClientIdScope && !credentialScopeOptions.some((option) => option.scopeId === oauth2ClientIdScope)) {
288
- setOauth2ClientIdScope(null);
289
- setOauth2ClientIdSecretId(null);
290
- setOauth2AuthState(null);
291
- }
292
- if (oauth2ClientSecretScope && !credentialScopeOptions.some((option) => option.scopeId === oauth2ClientSecretScope)) {
293
- setOauth2ClientSecretScope(null);
294
- setOauth2ClientSecretSecretId(null);
295
- setOauth2AuthState(null);
296
- }
297
- }, [credentialScopeOptions, oauth2ClientIdScope, oauth2ClientSecretScope]);
298
- const doPreview = useAtomSet(previewOpenApiSpec, { mode: "promiseExit" });
299
- const doAdd = useAtomSet(addOpenApiSpecOptimistic(scopeId), {
300
- mode: "promiseExit"
301
- });
302
- const doStartOAuth = useAtomSet(startOAuth, { mode: "promiseExit" });
303
- const doSetBinding = useAtomSet(setSourceCredentialBinding, {
304
- mode: "promiseExit"
305
- });
306
- const secretList = useSecretPickerSecrets();
307
- const oauth = useOAuthPopupFlow({
308
- popupName: OPENAPI_OAUTH_POPUP_NAME,
309
- popupBlockedMessage: "OAuth popup was blocked by the browser",
310
- popupClosedMessage: "OAuth cancelled - popup was closed before completing the flow.",
311
- startErrorMessage: "Failed to start OAuth"
312
- });
313
- const handleAnalyzeRef = useRef(() => {
314
- });
315
- useEffect(() => {
316
- const trimmed = specUrl.trim();
317
- if (!trimmed) return;
318
- if (preview) return;
319
- const handle = setTimeout(() => {
320
- handleAnalyzeRef.current();
321
- }, 400);
322
- return () => clearTimeout(handle);
323
- }, [specUrl, preview]);
324
- const expandServerOptions = (server) => {
325
- return expandServerUrlOptions(server).map((value2) => ({
326
- value: value2,
327
- label: value2
328
- }));
329
- };
330
- const servers = preview?.servers ?? [];
331
- const baseUrlOptions = Array.from(
332
- new Map(servers.flatMap(expandServerOptions).map((option) => [option.value, option])).values()
333
- );
334
- const resolvedBaseUrl = baseUrl.trim();
335
- const sourceScope = ScopeId.make(scopeId);
336
- const configuredHeaders = {};
337
- const headerBindings = [];
338
- const configuredQueryParams = {};
339
- const queryParamBindings = [];
340
- for (const ch of customHeaders) {
341
- if (!ch.name.trim()) continue;
342
- const slot = headerBindingSlot(ch.name.trim());
343
- configuredHeaders[ch.name.trim()] = { kind: "secret", prefix: ch.prefix };
344
- if (ch.secretId) {
345
- const targetScope = ch.targetScope ?? sourceScope;
346
- headerBindings.push({
347
- slot,
348
- secretId: ch.secretId,
349
- scope: targetScope,
350
- secretScope: ch.secretScope ?? targetScope
351
- });
352
- }
353
- }
354
- for (const param of runtimeCredentials.queryParams) {
355
- const name = param.name.trim();
356
- if (!name) continue;
357
- if (param.secretId) {
358
- const slot = queryParamBindingSlot(name);
359
- const targetScope = param.targetScope ?? sourceScope;
360
- configuredQueryParams[name] = { kind: "secret", prefix: param.prefix };
361
- queryParamBindings.push({
362
- slot,
363
- secretId: param.secretId,
364
- scope: targetScope,
365
- secretScope: param.secretScope ?? targetScope
366
- });
367
- continue;
368
- }
369
- if (param.literalValue?.trim()) {
370
- configuredQueryParams[name] = param.literalValue.trim();
371
- }
372
- }
373
- const configuredSpecFetchHeaders = {};
374
- const configuredSpecFetchQueryParams = {};
375
- const specFetchBindings = [];
376
- for (const header of specFetchCredentials.headers) {
377
- const name = header.name.trim();
378
- if (!name || !header.secretId) continue;
379
- const targetScope = header.targetScope ?? sourceScope;
380
- configuredSpecFetchHeaders[name] = { kind: "secret", prefix: header.prefix };
381
- specFetchBindings.push({
382
- slot: specFetchHeaderBindingSlot(name),
383
- secretId: header.secretId,
384
- scope: targetScope,
385
- secretScope: header.secretScope ?? targetScope
386
- });
387
- }
388
- for (const param of specFetchCredentials.queryParams) {
389
- const name = param.name.trim();
390
- if (!name) continue;
391
- if (param.secretId) {
392
- const targetScope = param.targetScope ?? sourceScope;
393
- configuredSpecFetchQueryParams[name] = { kind: "secret", prefix: param.prefix };
394
- specFetchBindings.push({
395
- slot: specFetchQueryParamBindingSlot(name),
396
- secretId: param.secretId,
397
- scope: targetScope,
398
- secretScope: param.secretScope ?? targetScope
399
- });
400
- continue;
401
- }
402
- if (param.literalValue?.trim()) {
403
- configuredSpecFetchQueryParams[name] = param.literalValue.trim();
404
- }
405
- }
406
- const configuredSpecFetchCredentials = Object.keys(configuredSpecFetchHeaders).length > 0 || Object.keys(configuredSpecFetchQueryParams).length > 0 ? {
407
- ...Object.keys(configuredSpecFetchHeaders).length > 0 ? { headers: configuredSpecFetchHeaders } : {},
408
- ...Object.keys(configuredSpecFetchQueryParams).length > 0 ? { queryParams: configuredSpecFetchQueryParams } : {}
409
- } : null;
410
- const oauth2Presets = preview?.oauth2Presets ?? [];
411
- const oauth2RedirectUrl = oauthCallbackUrl(OPENAPI_OAUTH_CALLBACK_PATH);
412
- const resolvedSourceId = slugifyNamespace(identity.namespace) || (preview ? Option.getOrElse(preview.title, () => "openapi") : "openapi");
413
- const selectedOAuth2Preset = strategy.kind === "oauth2" ? oauth2Presets[strategy.presetIndex] ?? null : null;
414
- const selectedOAuth2Fingerprint = selectedOAuth2Preset ? [
415
- resolvedSourceId,
416
- resolvedBaseUrl,
417
- selectedOAuth2Preset.securitySchemeName,
418
- selectedOAuth2Preset.flow,
419
- selectedOAuth2Preset.tokenUrl,
420
- Option.getOrElse(selectedOAuth2Preset.authorizationUrl, () => "")
421
- ].join("\n") : "";
422
- const oauth2Auth = oauth2AuthState?.fingerprint === selectedOAuth2Fingerprint ? oauth2AuthState.auth : null;
423
- const configuredOAuth2 = strategy.kind === "oauth2" && selectedOAuth2Preset ? OAuth2SourceConfig.make({
424
- kind: "oauth2",
425
- securitySchemeName: selectedOAuth2Preset.securitySchemeName,
426
- flow: selectedOAuth2Preset.flow,
427
- tokenUrl: resolveOAuthUrl(selectedOAuth2Preset.tokenUrl, resolvedBaseUrl),
428
- authorizationUrl: selectedOAuth2Preset.flow === "authorizationCode" ? resolveOAuthUrl(
429
- Option.getOrElse(selectedOAuth2Preset.authorizationUrl, () => ""),
430
- resolvedBaseUrl
431
- ) || null : null,
432
- clientIdSlot: oauth2ClientIdSlot(selectedOAuth2Preset.securitySchemeName),
433
- // Authorization-code specs can still be confidential clients
434
- // (Spotify is one example). Persist the slot even when the value is
435
- // deferred so the edit screen can collect the secret later.
436
- clientSecretSlot: oauth2ClientSecretSlot(selectedOAuth2Preset.securitySchemeName),
437
- connectionSlot: oauth2ConnectionSlot(selectedOAuth2Preset.securitySchemeName),
438
- scopes: [...oauth2SelectedScopes]
439
- }) : null;
440
- const hasHeaders = Object.keys(configuredHeaders).length > 0;
441
- const oauth2Busy = startingOAuth || oauth.busy;
442
- const canConnectOAuth2 = Boolean(oauth2ClientIdSecretId) && resolvedBaseUrl.length > 0;
443
- const hasIncompleteHeaderCredentials = strategy.kind !== "none" && strategy.kind !== "oauth2" && customHeaders.some((header) => header.name.trim() && !header.secretId);
444
- const hasIncompleteQueryCredentials = runtimeCredentials.queryParams.some(
445
- (param) => param.name.trim() && !param.secretId && !param.literalValue?.trim()
446
- );
447
- const hasIncompleteSpecFetchCredentials = specFetchCredentials.headers.some((header) => header.name.trim() && !header.secretId) || specFetchCredentials.queryParams.some(
448
- (param) => param.name.trim() && !param.secretId && !param.literalValue?.trim()
449
- );
450
- const willAddWithoutInitialCredentials = Boolean(selectedOAuth2Preset && !oauth2Auth) || hasIncompleteSpecFetchCredentials || hasIncompleteHeaderCredentials || hasIncompleteQueryCredentials;
451
- const canAdd = preview !== null && resolvedBaseUrl.length > 0;
452
- const handleAnalyze = async () => {
453
- setAnalyzing(true);
454
- setAnalyzeError(null);
455
- setAddError(null);
456
- const credentials = serializeHttpCredentials(specFetchCredentials);
457
- const exit = await doPreview({
458
- params: { scopeId },
459
- payload: {
460
- spec: specUrl,
461
- specFetchCredentials: credentials
462
- }
463
- });
464
- if (Exit.isFailure(exit)) {
465
- setAnalyzeError(errorMessageFromExit(exit, "Failed to parse spec"));
466
- setAnalyzing(false);
467
- return;
468
- }
469
- const result = exit.value;
470
- setPreview(result);
471
- const firstServer = result.servers[0];
472
- setBaseUrl(firstServer ? expandServerOptions(firstServer)[0]?.value ?? "" : "");
473
- const firstPreset = result.headerPresets[0];
474
- if (firstPreset) {
475
- setStrategy({ kind: "header", presetIndex: 0 });
476
- setCustomHeaders(entriesFromSpecPreset(firstPreset));
477
- } else if (result.oauth2Presets[0]) {
478
- setStrategy({ kind: "oauth2", presetIndex: 0 });
479
- setCustomHeaders([]);
480
- setOauth2SelectedScopes(new Set(Object.keys(result.oauth2Presets[0].scopes)));
481
- } else {
482
- setStrategy({ kind: "custom" });
483
- setCustomHeaders([]);
484
- }
485
- setAnalyzing(false);
486
- };
487
- handleAnalyzeRef.current = handleAnalyze;
488
- const selectStrategy = (next) => {
489
- setStrategy(next);
490
- if (next.kind !== "oauth2") {
491
- setOauth2AuthState(null);
492
- setOauth2Error(null);
493
- }
494
- Match.value(next).pipe(
495
- Match.when({ kind: "none" }, () => {
496
- setCustomHeaders([]);
497
- }),
498
- Match.when({ kind: "custom" }, () => {
499
- const userHeaders = customHeaders.filter((h) => !h.fromPreset);
500
- setCustomHeaders(userHeaders.length > 0 ? userHeaders : []);
501
- }),
502
- Match.when({ kind: "header" }, (n) => {
503
- const preset = preview?.headerPresets[n.presetIndex];
504
- if (!preset) return;
505
- const userHeaders = customHeaders.filter((h) => !h.fromPreset);
506
- setCustomHeaders([...entriesFromSpecPreset(preset), ...userHeaders]);
507
- }),
508
- Match.when({ kind: "oauth2" }, (n) => {
509
- setCustomHeaders([]);
510
- const preset = preview?.oauth2Presets[n.presetIndex];
511
- if (preset) {
512
- setOauth2SelectedScopes(new Set(Object.keys(preset.scopes)));
513
- }
514
- }),
515
- Match.exhaustive
516
- );
517
- };
518
- const handleHeadersChange = (next) => {
519
- setCustomHeaders(next);
520
- if (strategy.kind === "header" && next.every((h) => !h.fromPreset)) {
521
- setStrategy(next.length === 0 ? { kind: "none" } : { kind: "custom" });
522
- }
523
- };
524
- const toggleOAuth2Scope = (scope) => {
525
- setOauth2SelectedScopes((prev) => {
526
- const copy = new Set(prev);
527
- if (copy.has(scope)) copy.delete(scope);
528
- else copy.add(scope);
529
- return copy;
530
- });
531
- setOauth2AuthState(null);
532
- };
533
- const handleConnectOAuth2 = useCallback(async () => {
534
- if (!selectedOAuth2Preset || !oauth2ClientIdSecretId || !preview) return;
535
- oauth.cancel();
536
- setOauth2Error(null);
537
- const displayName = identity.name.trim() || selectedOAuth2Preset.securitySchemeName;
538
- const tokenUrl = resolveOAuthUrl(selectedOAuth2Preset.tokenUrl, resolvedBaseUrl);
539
- const clientIdSecretScope = oauth2ClientIdScope ?? sourceScope;
540
- const clientSecretSecretScope = oauth2ClientSecretScope ?? sourceScope;
541
- if (selectedOAuth2Preset.flow === "clientCredentials") {
542
- if (!oauth2ClientSecretSecretId) {
543
- setOauth2Error("client_credentials requires a client secret");
544
- return;
545
- }
546
- setStartingOAuth(true);
547
- const connectionId = openApiOAuthConnectionId(resolvedSourceId, selectedOAuth2Preset.flow);
548
- const exit = await doStartOAuth({
549
- params: { scopeId: oauthTokenTargetScope },
550
- payload: {
551
- endpoint: tokenUrl,
552
- redirectUrl: tokenUrl,
553
- connectionId,
554
- tokenScope: oauthTokenTargetScope,
555
- strategy: {
556
- kind: "client-credentials",
557
- tokenEndpoint: tokenUrl,
558
- clientIdSecretId: oauth2ClientIdSecretId,
559
- clientIdSecretScopeId: String(clientIdSecretScope),
560
- clientSecretSecretId: oauth2ClientSecretSecretId,
561
- clientSecretSecretScopeId: String(clientSecretSecretScope),
562
- scopes: [...oauth2SelectedScopes]
563
- },
564
- pluginId: "openapi",
565
- identityLabel: `${displayName} OAuth`
566
- }
567
- });
568
- setStartingOAuth(false);
569
- if (Exit.isFailure(exit)) {
570
- setOauth2Error(errorMessageFromExit(exit, "Failed to start OAuth"));
571
- return;
572
- }
573
- const response = exit.value;
574
- if (!response.completedConnection) {
575
- setOauth2Error("client_credentials flow did not mint a connection");
576
- return;
577
- }
578
- setOauth2AuthState({
579
- fingerprint: selectedOAuth2Fingerprint,
580
- auth: { connectionId: response.completedConnection.connectionId }
581
- });
582
- setOauth2Error(null);
583
- return;
584
- }
585
- const authorizationUrl = resolveOAuthUrl(
586
- Option.getOrElse(selectedOAuth2Preset.authorizationUrl, () => ""),
587
- resolvedBaseUrl
588
- );
589
- const issuerUrl = inferOAuthIssuerUrl(authorizationUrl);
590
- await oauth.openAuthorization({
591
- tokenScope: oauthTokenTargetScope,
592
- run: async () => {
593
- const exit = await doStartOAuth({
594
- params: { scopeId: oauthTokenTargetScope },
595
- payload: {
596
- endpoint: authorizationUrl,
597
- connectionId: openApiOAuthConnectionId(resolvedSourceId, selectedOAuth2Preset.flow),
598
- tokenScope: oauthTokenTargetScope,
599
- redirectUrl: oauth2RedirectUrl,
600
- strategy: {
601
- kind: "authorization-code",
602
- authorizationEndpoint: authorizationUrl,
603
- tokenEndpoint: tokenUrl,
604
- issuerUrl,
605
- clientIdSecretId: oauth2ClientIdSecretId,
606
- clientIdSecretScopeId: String(clientIdSecretScope),
607
- clientSecretSecretId: oauth2ClientSecretSecretId ?? null,
608
- clientSecretSecretScopeId: oauth2ClientSecretSecretId ? String(clientSecretSecretScope) : null,
609
- scopes: [...oauth2SelectedScopes]
610
- },
611
- pluginId: "openapi",
612
- identityLabel: `${displayName} OAuth`
613
- }
614
- });
615
- if (Exit.isFailure(exit)) {
616
- throw new Error(errorMessageFromExit(exit, "Failed to start OAuth"));
617
- }
618
- const response = exit.value;
619
- if (response.authorizationUrl === null) {
620
- throw new Error("Unexpected response flow from server");
621
- }
622
- return {
623
- sessionId: response.sessionId,
624
- authorizationUrl: response.authorizationUrl
625
- };
626
- },
627
- onSuccess: (result) => {
628
- setOauth2AuthState({
629
- fingerprint: selectedOAuth2Fingerprint,
630
- auth: { connectionId: result.connectionId }
631
- });
632
- setOauth2Error(null);
633
- },
634
- onError: (message) => {
635
- setStartingOAuth(false);
636
- setOauth2Error(message);
637
- }
638
- });
639
- }, [
640
- selectedOAuth2Preset,
641
- oauth2ClientIdSecretId,
642
- oauth2ClientSecretSecretId,
643
- oauth2SelectedScopes,
644
- oauth2RedirectUrl,
645
- resolvedBaseUrl,
646
- preview,
647
- doStartOAuth,
648
- identity.name,
649
- resolvedSourceId,
650
- selectedOAuth2Fingerprint,
651
- oauth,
652
- oauthTokenTargetScope,
653
- oauth2ClientIdScope,
654
- oauth2ClientSecretScope,
655
- sourceScope
656
- ]);
657
- const handleCancelOAuth2 = useCallback(() => {
658
- oauth.cancel();
659
- setStartingOAuth(false);
660
- setOauth2Error(null);
661
- }, [oauth]);
662
- const handleAdd = async () => {
663
- setAdding(true);
664
- setAddError(null);
665
- const namespace = resolvedSourceId;
666
- const displayName = identity.name.trim() || (preview ? Option.getOrElse(preview.title, () => namespace) : namespace);
667
- const exit = await doAdd({
668
- params: { scopeId },
669
- payload: {
670
- spec: specInputForAdd(specUrl),
671
- name: displayName,
672
- namespace,
673
- baseUrl: resolvedBaseUrl,
674
- ...configuredSpecFetchCredentials ? { specFetchCredentials: configuredSpecFetchCredentials } : {},
675
- ...hasHeaders ? { headers: configuredHeaders } : {},
676
- ...Object.keys(configuredQueryParams).length > 0 ? { queryParams: configuredQueryParams } : {},
677
- ...configuredOAuth2 ? { oauth2: configuredOAuth2 } : {}
678
- },
679
- reactivityKeys: addSpecWriteKeys
680
- });
681
- if (Exit.isFailure(exit)) {
682
- setAddError(errorMessageFromExit(exit, "Failed to add source"));
683
- setAdding(false);
684
- return;
685
- }
686
- const sourceId = exit.value.namespace;
687
- const oauthTokenBindingScope = ScopeId.make(oauthTokenTargetScope);
688
- const clientIdBindingScope = oauth2ClientIdScope ?? sourceScope;
689
- const clientSecretBindingScope = oauth2ClientSecretScope ?? sourceScope;
690
- for (const binding of headerBindings) {
691
- const bindingExit = await doSetBinding({
692
- params: { scopeId },
693
- payload: SetSourceCredentialBindingInput.make({
694
- source: { id: sourceId, scope: sourceScope },
695
- scope: binding.scope,
696
- slotKey: binding.slot,
697
- value: {
698
- kind: "secret",
699
- secretId: SecretId.make(binding.secretId),
700
- secretScopeId: binding.secretScope
701
- }
702
- }),
703
- reactivityKeys: bindingWriteKeys
704
- });
705
- if (Exit.isFailure(bindingExit)) {
706
- setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
707
- setAdding(false);
708
- return;
709
- }
710
- }
711
- for (const binding of queryParamBindings) {
712
- const bindingExit = await doSetBinding({
713
- params: { scopeId },
714
- payload: SetSourceCredentialBindingInput.make({
715
- source: { id: sourceId, scope: sourceScope },
716
- scope: binding.scope,
717
- slotKey: binding.slot,
718
- value: {
719
- kind: "secret",
720
- secretId: SecretId.make(binding.secretId),
721
- secretScopeId: binding.secretScope
722
- }
723
- }),
724
- reactivityKeys: bindingWriteKeys
725
- });
726
- if (Exit.isFailure(bindingExit)) {
727
- setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
728
- setAdding(false);
729
- return;
730
- }
731
- }
732
- for (const binding of specFetchBindings) {
733
- const bindingExit = await doSetBinding({
734
- params: { scopeId },
735
- payload: SetSourceCredentialBindingInput.make({
736
- source: { id: sourceId, scope: sourceScope },
737
- scope: binding.scope,
738
- slotKey: binding.slot,
739
- value: {
740
- kind: "secret",
741
- secretId: SecretId.make(binding.secretId),
742
- secretScopeId: binding.secretScope
743
- }
744
- }),
745
- reactivityKeys: bindingWriteKeys
746
- });
747
- if (Exit.isFailure(bindingExit)) {
748
- setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
749
- setAdding(false);
750
- return;
751
- }
752
- }
753
- if (configuredOAuth2 && oauth2ClientIdSecretId) {
754
- const bindingExit = await doSetBinding({
755
- params: { scopeId },
756
- payload: SetSourceCredentialBindingInput.make({
757
- source: { id: sourceId, scope: sourceScope },
758
- scope: clientIdBindingScope,
759
- slotKey: configuredOAuth2.clientIdSlot,
760
- value: {
761
- kind: "secret",
762
- secretId: SecretId.make(oauth2ClientIdSecretId),
763
- secretScopeId: clientIdBindingScope
764
- }
765
- }),
766
- reactivityKeys: bindingWriteKeys
767
- });
768
- if (Exit.isFailure(bindingExit)) {
769
- setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
770
- setAdding(false);
771
- return;
772
- }
773
- }
774
- if (configuredOAuth2?.clientSecretSlot && oauth2ClientSecretSecretId) {
775
- const bindingExit = await doSetBinding({
776
- params: { scopeId },
777
- payload: SetSourceCredentialBindingInput.make({
778
- source: { id: sourceId, scope: sourceScope },
779
- scope: clientSecretBindingScope,
780
- slotKey: configuredOAuth2.clientSecretSlot,
781
- value: {
782
- kind: "secret",
783
- secretId: SecretId.make(oauth2ClientSecretSecretId),
784
- secretScopeId: clientSecretBindingScope
785
- }
786
- }),
787
- reactivityKeys: bindingWriteKeys
788
- });
789
- if (Exit.isFailure(bindingExit)) {
790
- setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
791
- setAdding(false);
792
- return;
793
- }
794
- }
795
- if (configuredOAuth2 && oauth2Auth) {
796
- const bindingExit = await doSetBinding({
797
- params: { scopeId },
798
- payload: SetSourceCredentialBindingInput.make({
799
- source: { id: sourceId, scope: sourceScope },
800
- scope: oauthTokenBindingScope,
801
- slotKey: configuredOAuth2.connectionSlot,
802
- value: {
803
- kind: "connection",
804
- connectionId: ConnectionId.make(oauth2Auth.connectionId)
805
- }
806
- }),
807
- reactivityKeys: bindingWriteKeys
808
- });
809
- if (Exit.isFailure(bindingExit)) {
810
- setAddError(errorMessageFromExit(bindingExit, "Failed to add source"));
811
- setAdding(false);
812
- return;
813
- }
814
- }
815
- props.onComplete();
816
- };
817
- return /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-6", children: [
818
- /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2("h1", { className: "text-xl font-semibold text-foreground", children: "Add OpenAPI Source" }) }),
819
- !preview && /* @__PURE__ */ jsxs2(Fragment, { children: [
820
- /* @__PURE__ */ jsx2(CardStack2, { children: /* @__PURE__ */ jsx2(CardStackContent2, { className: "border-t-0", children: /* @__PURE__ */ jsx2(
821
- CardStackEntryField2,
822
- {
823
- label: "OpenAPI Spec",
824
- hint: "Paste a URL or raw JSON/YAML content.",
825
- children: /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
826
- /* @__PURE__ */ jsx2(
827
- Textarea,
828
- {
829
- value: specUrl,
830
- onChange: (e) => {
831
- setSpecUrl(e.target.value);
832
- },
833
- placeholder: "https://api.example.com/openapi.json",
834
- rows: 3,
835
- maxRows: 10,
836
- className: "font-mono text-sm"
837
- }
838
- ),
839
- analyzing && /* @__PURE__ */ jsx2("div", { className: "pointer-events-none absolute right-2 top-2", children: /* @__PURE__ */ jsx2(IOSSpinner, { className: "size-4" }) })
840
- ] })
841
- }
842
- ) }) }),
843
- /* @__PURE__ */ jsxs2(
844
- Collapsible,
845
- {
846
- open: specFetchCredentialsOpen,
847
- onOpenChange: setSpecFetchCredentialsOpen,
848
- className: "space-y-3",
849
- children: [
850
- /* @__PURE__ */ jsx2(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(Button, { variant: "outline", size: "sm", className: "self-start", children: specFetchCredentialsOpen ? "Hide spec credentials" : "Add spec credentials" }) }),
851
- /* @__PURE__ */ jsx2(CollapsibleContent, { children: /* @__PURE__ */ jsx2(
852
- HttpCredentialsEditor,
853
- {
854
- credentials: specFetchCredentials,
855
- onChange: setSpecFetchCredentials,
856
- existingSecrets: secretList,
857
- sourceName: identity.name,
858
- targetScope: sourceScope,
859
- credentialScopeOptions,
860
- bindingScopeOptions: credentialScopeOptions,
861
- labels: {
862
- headers: "Spec fetch headers",
863
- queryParams: "Spec fetch query parameters"
864
- }
865
- }
866
- ) })
867
- ]
868
- }
869
- )
870
- ] }),
871
- preview ? /* @__PURE__ */ jsx2(
872
- OpenApiSourceDetailsFields,
873
- {
874
- title: Option.getOrElse(preview.title, () => "API"),
875
- 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" : ""}` : ""}`,
876
- identity,
877
- baseUrl: resolvedBaseUrl,
878
- onBaseUrlChange: setBaseUrl,
879
- baseUrlOptions,
880
- specUrl,
881
- onSpecUrlChange: (value2) => {
882
- setSpecUrl(value2);
883
- setPreview(null);
884
- setBaseUrl("");
885
- setCustomHeaders([]);
886
- setStrategy({ kind: "none" });
887
- setOauth2AuthState(null);
888
- setOauth2Error(null);
889
- },
890
- faviconUrl: resolvedBaseUrl,
891
- baseUrlMissingMessage: "A base URL is required to make requests."
892
- }
893
- ) : null,
894
- analyzeError && /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx2("p", { className: "text-[12px] text-destructive", children: analyzeError }) }),
895
- preview && /* @__PURE__ */ jsxs2(Fragment, { children: [
896
- /* @__PURE__ */ jsxs2("section", { className: "space-y-2.5", children: [
897
- /* @__PURE__ */ jsx2(FieldLabel, { children: "Authentication method" }),
898
- /* @__PURE__ */ jsxs2(
899
- RadioGroup,
900
- {
901
- value: serializeStrategy(strategy),
902
- onValueChange: (value2) => selectStrategy(parseStrategy(value2)),
903
- className: "gap-1.5",
904
- children: [
905
- preview.headerPresets.map((preset, i) => {
906
- const selected = strategy.kind === "header" && strategy.presetIndex === i;
907
- return /* @__PURE__ */ jsxs2(
908
- Label,
909
- {
910
- className: `flex items-start gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${selected ? "border-primary/50 bg-primary/[0.03]" : "border-border hover:bg-accent/50"}`,
911
- children: [
912
- /* @__PURE__ */ jsx2(RadioGroupItem, { value: `header:${i}`, className: "mt-0.5" }),
913
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
914
- /* @__PURE__ */ jsx2("div", { className: "text-xs font-medium text-foreground", children: preset.label }),
915
- preset.secretHeaders.length > 0 && /* @__PURE__ */ jsx2("div", { className: "mt-0.5 font-mono text-[10px] text-muted-foreground", children: preset.secretHeaders.join(" \xB7 ") })
916
- ] })
917
- ]
918
- },
919
- `header-${i}`
920
- );
921
- }),
922
- oauth2Presets.map((preset, i) => {
923
- const selected = strategy.kind === "oauth2" && strategy.presetIndex === i;
924
- const scopeCount = Object.keys(preset.scopes).length;
925
- return /* @__PURE__ */ jsxs2(
926
- Label,
927
- {
928
- className: `flex items-start gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${selected ? "border-primary/50 bg-primary/[0.03]" : "border-border hover:bg-accent/50"}`,
929
- children: [
930
- /* @__PURE__ */ jsx2(RadioGroupItem, { value: `oauth2:${i}`, className: "mt-0.5" }),
931
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
932
- /* @__PURE__ */ jsx2("div", { className: "text-xs font-medium text-foreground", children: preset.label }),
933
- /* @__PURE__ */ jsxs2("div", { className: "mt-0.5 text-[10px] text-muted-foreground", children: [
934
- scopeCount,
935
- " scope",
936
- scopeCount === 1 ? "" : "s"
937
- ] })
938
- ] })
939
- ]
940
- },
941
- `oauth2-${i}`
942
- );
943
- }),
944
- /* @__PURE__ */ jsxs2(
945
- Label,
946
- {
947
- className: `flex items-center gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${strategy.kind === "custom" ? "border-primary/50 bg-primary/[0.03]" : "border-border hover:bg-accent/50"}`,
948
- children: [
949
- /* @__PURE__ */ jsx2(RadioGroupItem, { value: "custom" }),
950
- /* @__PURE__ */ jsx2("span", { className: "text-xs font-medium text-foreground", children: "Custom" })
951
- ]
952
- }
953
- ),
954
- /* @__PURE__ */ jsxs2(
955
- Label,
956
- {
957
- className: `flex items-center gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${strategy.kind === "none" ? "border-primary/50 bg-primary/[0.03]" : "border-border hover:bg-accent/50"}`,
958
- children: [
959
- /* @__PURE__ */ jsx2(RadioGroupItem, { value: "none" }),
960
- /* @__PURE__ */ jsx2("span", { className: "text-xs font-medium text-foreground", children: "None" })
961
- ]
962
- }
963
- )
964
- ]
965
- }
966
- ),
967
- strategy.kind !== "none" && strategy.kind !== "oauth2" && /* @__PURE__ */ jsx2("div", { className: "space-y-3", children: /* @__PURE__ */ jsx2(
968
- HeadersList,
969
- {
970
- headers: customHeaders,
971
- onHeadersChange: handleHeadersChange,
972
- existingSecrets: secretList,
973
- sourceName: identity.name,
974
- targetScope: sourceScope,
975
- credentialScopeOptions,
976
- bindingScopeOptions: credentialScopeOptions,
977
- emptyLabel: "No credentials yet. Add the header value this method should use."
978
- }
979
- ) }),
980
- /* @__PURE__ */ jsx2(
981
- HttpCredentialsEditor,
982
- {
983
- credentials: runtimeCredentials,
984
- onChange: setRuntimeCredentials,
985
- existingSecrets: secretList,
986
- sourceName: identity.name,
987
- targetScope: sourceScope,
988
- credentialScopeOptions,
989
- bindingScopeOptions: credentialScopeOptions,
990
- sections: { headers: false, queryParams: true },
991
- labels: { queryParams: "Runtime query parameters" }
992
- }
993
- ),
994
- selectedOAuth2Preset && /* @__PURE__ */ jsx2("div", { className: "space-y-3 rounded-lg border border-border/60 bg-muted/10 p-3", children: /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
995
- /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
996
- /* @__PURE__ */ jsxs2(FieldLabel, { className: "text-[11px]", children: [
997
- "Redirect URL",
998
- " ",
999
- /* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "\xB7 add this to your OAuth app's allowed redirects" })
1000
- ] }),
1001
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1 rounded-md border border-border bg-background/50 px-2.5 py-1.5 font-mono text-[11px]", children: [
1002
- /* @__PURE__ */ jsx2("span", { className: "truncate flex-1 text-foreground", children: oauth2RedirectUrl }),
1003
- /* @__PURE__ */ jsx2(CopyButton, { value: oauth2RedirectUrl })
1004
- ] })
1005
- ] }),
1006
- /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
1007
- /* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "Client ID secret" }),
1008
- /* @__PURE__ */ jsxs2("div", { className: "grid gap-2 md:grid-cols-2", children: [
1009
- /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
1010
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5", children: [
1011
- /* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "Secret" }),
1012
- /* @__PURE__ */ jsx2(HelpTooltip, { label: "Client ID secret", children: "Select or create the OAuth client ID secret." })
1013
- ] }),
1014
- /* @__PURE__ */ jsx2(
1015
- CreatableSecretPicker,
1016
- {
1017
- value: oauth2ClientIdSecretId,
1018
- onSelect: (id, secretScopeId) => {
1019
- setOauth2ClientIdSecretId(id);
1020
- setOauth2ClientIdScope(secretScopeId ?? sourceScope);
1021
- setOauth2AuthState(null);
1022
- },
1023
- secrets: secretList,
1024
- sourceName: identity.name,
1025
- secretLabel: "Client ID",
1026
- targetScope: oauth2ClientIdScope ?? sourceScope,
1027
- credentialScopeOptions,
1028
- onCreatedScope: setOauth2ClientIdScope
1029
- }
1030
- )
1031
- ] }),
1032
- /* @__PURE__ */ jsx2(
1033
- CredentialScopeDropdown,
1034
- {
1035
- value: oauth2ClientIdScope ?? sourceScope,
1036
- options: credentialScopeOptions,
1037
- onChange: (targetScope) => {
1038
- setOauth2ClientIdScope(targetScope);
1039
- setOauth2ClientIdSecretId(null);
1040
- setOauth2AuthState(null);
1041
- },
1042
- label: "Used by",
1043
- help: "Choose where this OAuth client ID credential lives."
1044
- }
1045
- )
1046
- ] })
1047
- ] }),
1048
- /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
1049
- /* @__PURE__ */ jsxs2(FieldLabel, { className: "text-[11px]", children: [
1050
- "Client secret",
1051
- " ",
1052
- /* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "\xB7 optional for public clients with PKCE" })
1053
- ] }),
1054
- /* @__PURE__ */ jsxs2("div", { className: "grid gap-2 md:grid-cols-2", children: [
1055
- /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
1056
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5", children: [
1057
- /* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "Secret" }),
1058
- /* @__PURE__ */ jsx2(HelpTooltip, { label: "Client secret", children: "Select or create the OAuth client secret." })
1059
- ] }),
1060
- /* @__PURE__ */ jsx2(
1061
- CreatableSecretPicker,
1062
- {
1063
- value: oauth2ClientSecretSecretId,
1064
- onSelect: (id, secretScopeId) => {
1065
- setOauth2ClientSecretSecretId(id);
1066
- setOauth2ClientSecretScope(secretScopeId ?? sourceScope);
1067
- setOauth2AuthState(null);
1068
- },
1069
- secrets: secretList,
1070
- sourceName: identity.name,
1071
- secretLabel: "Client Secret",
1072
- targetScope: oauth2ClientSecretScope ?? sourceScope,
1073
- credentialScopeOptions,
1074
- onCreatedScope: setOauth2ClientSecretScope
1075
- }
1076
- )
1077
- ] }),
1078
- /* @__PURE__ */ jsx2(
1079
- CredentialScopeDropdown,
1080
- {
1081
- value: oauth2ClientSecretScope ?? sourceScope,
1082
- options: credentialScopeOptions,
1083
- onChange: (targetScope) => {
1084
- setOauth2ClientSecretScope(targetScope);
1085
- setOauth2ClientSecretSecretId(null);
1086
- setOauth2AuthState(null);
1087
- },
1088
- label: "Used by",
1089
- help: "Choose where this OAuth client secret credential lives."
1090
- }
1091
- )
1092
- ] })
1093
- ] }),
1094
- /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
1095
- /* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "Scopes" }),
1096
- /* @__PURE__ */ jsx2("div", { className: "space-y-1 rounded-md border border-border/50 bg-background/50 p-2", children: Object.keys(selectedOAuth2Preset.scopes).length === 0 ? /* @__PURE__ */ jsx2("div", { className: "text-[11px] italic text-muted-foreground", children: "No scopes declared by the spec." }) : Object.entries(selectedOAuth2Preset.scopes).map(([scope, description]) => /* @__PURE__ */ jsxs2(Label, { className: "flex items-start gap-2 cursor-pointer py-1", children: [
1097
- /* @__PURE__ */ jsx2(
1098
- Checkbox,
1099
- {
1100
- checked: oauth2SelectedScopes.has(scope),
1101
- onCheckedChange: () => toggleOAuth2Scope(scope)
1102
- }
1103
- ),
1104
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
1105
- /* @__PURE__ */ jsx2("div", { className: "font-mono text-[11px] text-foreground", children: scope }),
1106
- description && /* @__PURE__ */ jsx2("div", { className: "text-[10px] text-muted-foreground", children: description })
1107
- ] })
1108
- ] }, scope)) })
1109
- ] }),
1110
- oauth2Auth ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between rounded-md border border-green-500/30 bg-green-500/5 px-3 py-2", children: [
1111
- /* @__PURE__ */ jsxs2("div", { className: "text-[11px] text-green-700 dark:text-green-400", children: [
1112
- "Connected \xB7 ",
1113
- oauth2SelectedScopes.size,
1114
- " scope",
1115
- oauth2SelectedScopes.size === 1 ? "" : "s",
1116
- " granted"
1117
- ] }),
1118
- /* @__PURE__ */ jsx2(Button, { variant: "ghost", size: "sm", onClick: () => setOauth2AuthState(null), children: "Disconnect" })
1119
- ] }) : oauth2Busy ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
1120
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 items-center gap-2 rounded-md border border-border/60 bg-background/50 px-3 py-2 text-[11px] text-muted-foreground", children: [
1121
- /* @__PURE__ */ jsx2(Spinner, { className: "size-3.5" }),
1122
- "Waiting for OAuth\u2026 complete the flow in the popup, or cancel to retry."
1123
- ] }),
1124
- /* @__PURE__ */ jsx2(Button, { variant: "ghost", size: "sm", onClick: handleCancelOAuth2, children: "Cancel" }),
1125
- /* @__PURE__ */ jsx2(Button, { variant: "secondary", size: "sm", onClick: handleConnectOAuth2, children: "Retry" })
1126
- ] }) : /* @__PURE__ */ jsx2("div", { className: "flex flex-col gap-1.5", children: /* @__PURE__ */ jsxs2("div", { className: "grid gap-2 md:grid-cols-2", children: [
1127
- /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5", children: [
1128
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5", children: [
1129
- /* @__PURE__ */ jsx2(FieldLabel, { className: "text-[11px]", children: "OAuth sign-in" }),
1130
- /* @__PURE__ */ jsx2(HelpTooltip, { label: "OAuth sign-in", children: "Start the provider OAuth flow." })
1131
- ] }),
1132
- /* @__PURE__ */ jsx2(
1133
- Button,
1134
- {
1135
- variant: "secondary",
1136
- onClick: handleConnectOAuth2,
1137
- disabled: !canConnectOAuth2,
1138
- className: canConnectOAuth2 ? "w-full border border-green-500/30 bg-green-600 text-white hover:bg-green-700 focus-visible:ring-green-500/30 dark:bg-green-500 dark:text-white dark:hover:bg-green-600" : "w-full",
1139
- children: "Connect via OAuth"
1140
- }
1141
- )
1142
- ] }),
1143
- /* @__PURE__ */ jsx2(
1144
- CredentialScopeDropdown,
1145
- {
1146
- value: oauthTokenTargetScope,
1147
- options: credentialScopeOptions,
1148
- onChange: (targetScope) => {
1149
- setOAuthTokenTargetScope(targetScope);
1150
- setOauth2AuthState(null);
1151
- },
1152
- label: "Token saved to",
1153
- help: "Choose who can use the signed-in OAuth token."
1154
- }
1155
- )
1156
- ] }) }),
1157
- oauth2Error && /* @__PURE__ */ jsx2("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx2("p", { className: "text-[11px] text-destructive", children: oauth2Error }) })
1158
- ] }) })
1159
- ] }),
1160
- addError && /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx2("p", { className: "text-[12px] text-destructive", children: addError }) })
1161
- ] }),
1162
- /* @__PURE__ */ jsxs2(FloatActions, { children: [
1163
- /* @__PURE__ */ jsx2(Button, { variant: "ghost", onClick: props.onCancel, disabled: adding, children: "Cancel" }),
1164
- preview && /* @__PURE__ */ jsxs2(Button, { onClick: handleAdd, disabled: !canAdd || adding, children: [
1165
- adding && /* @__PURE__ */ jsx2(Spinner, { className: "size-3.5" }),
1166
- adding ? "Adding\u2026" : willAddWithoutInitialCredentials ? "Add without credentials" : "Add source"
1167
- ] })
1168
- ] })
1169
- ] });
1170
- }
1171
-
1172
- export {
1173
- OpenApiSourceDetailsFields,
1174
- OPENAPI_OAUTH_POPUP_NAME,
1175
- OPENAPI_OAUTH_CALLBACK_PATH,
1176
- openApiOAuthConnectionId,
1177
- resolveOAuthUrl,
1178
- inferOAuthIssuerUrl,
1179
- AddOpenApiSource
1180
- };
1181
- //# sourceMappingURL=chunk-2ZKKZYZH.js.map