@executor-js/plugin-openapi 1.4.29 → 1.4.30

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 (45) hide show
  1. package/dist/{AddOpenApiSource-FLMNI742.js → AddOpenApiSource-KSOMPQ2R.js} +4 -4
  2. package/dist/{EditOpenApiSource-I4NIGIIJ.js → EditOpenApiSource-AOA7APR2.js} +301 -192
  3. package/dist/EditOpenApiSource-AOA7APR2.js.map +1 -0
  4. package/dist/{OpenApiSourceSummary-CM46DB4L.js → OpenApiSourceSummary-Y3S6ZBOZ.js} +4 -4
  5. package/dist/OpenApiSourceSummary-Y3S6ZBOZ.js.map +1 -0
  6. package/dist/api/group.d.ts +124 -118
  7. package/dist/api/index.d.ts +148 -369
  8. package/dist/chunk-BB5IAKRG.js +136 -0
  9. package/dist/chunk-BB5IAKRG.js.map +1 -0
  10. package/dist/{chunk-E7PZ2QGD.js → chunk-EOXXE5DG.js} +17 -455
  11. package/dist/chunk-EOXXE5DG.js.map +1 -0
  12. package/dist/{chunk-OZ67JNID.js → chunk-NIKLYJ3X.js} +830 -319
  13. package/dist/chunk-NIKLYJ3X.js.map +1 -0
  14. package/dist/{chunk-TGDT6QCH.js → chunk-YJMXYKYX.js} +178 -117
  15. package/dist/chunk-YJMXYKYX.js.map +1 -0
  16. package/dist/{chunk-GFQUEZUW.js → chunk-ZZBTLFTA.js} +78 -93
  17. package/dist/chunk-ZZBTLFTA.js.map +1 -0
  18. package/dist/client.js +7 -137
  19. package/dist/client.js.map +1 -1
  20. package/dist/core.js +5 -10
  21. package/dist/index.js +3 -2
  22. package/dist/react/atoms.d.ts +83 -223
  23. package/dist/react/client.d.ts +123 -117
  24. package/dist/sdk/credential-status.d.ts +3 -3
  25. package/dist/sdk/extract.d.ts +19 -19
  26. package/dist/sdk/index.d.ts +2 -2
  27. package/dist/sdk/invoke.d.ts +7 -7
  28. package/dist/sdk/parse.d.ts +2 -3
  29. package/dist/sdk/plugin.d.ts +181 -275
  30. package/dist/sdk/preview.d.ts +12 -12
  31. package/dist/sdk/source-contracts.d.ts +55 -0
  32. package/dist/sdk/store.d.ts +6 -269
  33. package/dist/sdk/types.d.ts +16 -65
  34. package/dist/testing/index.d.ts +149 -11
  35. package/dist/testing.js +419 -33
  36. package/dist/testing.js.map +1 -1
  37. package/dist/testing.test.d.ts +1 -0
  38. package/package.json +3 -4
  39. package/dist/EditOpenApiSource-I4NIGIIJ.js.map +0 -1
  40. package/dist/OpenApiSourceSummary-CM46DB4L.js.map +0 -1
  41. package/dist/chunk-E7PZ2QGD.js.map +0 -1
  42. package/dist/chunk-GFQUEZUW.js.map +0 -1
  43. package/dist/chunk-OZ67JNID.js.map +0 -1
  44. package/dist/chunk-TGDT6QCH.js.map +0 -1
  45. /package/dist/{AddOpenApiSource-FLMNI742.js.map → AddOpenApiSource-KSOMPQ2R.js.map} +0 -0
@@ -4,19 +4,15 @@ import {
4
4
  OpenApiSourceDetailsFields,
5
5
  inferOAuthIssuerUrl,
6
6
  resolveOAuthUrl
7
- } from "./chunk-TGDT6QCH.js";
7
+ } from "./chunk-YJMXYKYX.js";
8
8
  import {
9
9
  openApiSourceAtom,
10
- openApiSourceBindingsAtom,
11
- removeOpenApiSourceBinding,
12
- setOpenApiSourceBinding,
13
- updateOpenApiSource
14
- } from "./chunk-GFQUEZUW.js";
10
+ openApiSourceBindingsAtom
11
+ } from "./chunk-ZZBTLFTA.js";
15
12
  import {
16
13
  OAuth2SourceConfig,
17
- OpenApiSourceBindingInput,
18
14
  oauth2ClientSecretSlot
19
- } from "./chunk-E7PZ2QGD.js";
15
+ } from "./chunk-EOXXE5DG.js";
20
16
 
21
17
  // src/react/EditOpenApiSource.tsx
22
18
  import { useEffect, useMemo, useRef, useState } from "react";
@@ -25,8 +21,14 @@ import * as Exit from "effect/Exit";
25
21
  import * as Option from "effect/Option";
26
22
  import * as Schema from "effect/Schema";
27
23
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
28
- import { connectionsAtom, sourceAtom, startOAuth } from "@executor-js/react/api/atoms";
29
- import { useScope, useScopeStack, useUserScope } from "@executor-js/react/api/scope-context";
24
+ import {
25
+ connectionsAtom,
26
+ configureSource,
27
+ setSourceCredentialBinding,
28
+ sourceAtom,
29
+ startOAuth
30
+ } from "@executor-js/react/api/atoms";
31
+ import { useScope, useUserScope } from "@executor-js/react/api/scope-context";
30
32
  import { connectionWriteKeys, sourceWriteKeys } from "@executor-js/react/api/reactivity-keys";
31
33
  import { Button } from "@executor-js/react/components/button";
32
34
  import { CopyButton } from "@executor-js/react/components/copy-button";
@@ -36,18 +38,28 @@ import {
36
38
  CardStackEntry,
37
39
  CardStackEntryContent,
38
40
  CardStackEntryDescription,
39
- CardStackEntryField,
40
- CardStackEntryTitle
41
+ CardStackEntryTitle,
42
+ CardStackEntryField
41
43
  } from "@executor-js/react/components/card-stack";
42
- import { FilterTabs } from "@executor-js/react/components/filter-tabs";
44
+ import { FieldLabel } from "@executor-js/react/components/field";
45
+ import { HelpTooltip } from "@executor-js/react/components/help-tooltip";
43
46
  import { Input } from "@executor-js/react/components/input";
44
47
  import { sourceWriteKeys as openApiWriteKeys } from "@executor-js/react/api/reactivity-keys";
45
- import { ConnectionId, ScopeId, SecretId } from "@executor-js/sdk/core";
48
+ import {
49
+ ConnectionId,
50
+ ScopeId,
51
+ SetSourceCredentialBindingInput
52
+ } from "@executor-js/sdk/shared";
46
53
  import { useSecretPickerSecrets } from "@executor-js/react/plugins/use-secret-picker-secrets";
47
54
  import {
48
55
  oauthCallbackUrl,
49
56
  useOAuthPopupFlow
50
57
  } from "@executor-js/react/plugins/oauth-sign-in";
58
+ import {
59
+ CredentialControlField,
60
+ CredentialScopeDropdown,
61
+ CredentialUsageRow
62
+ } from "@executor-js/react/plugins/credential-target-scope";
51
63
  import {
52
64
  effectiveCredentialBindingForScope,
53
65
  exactCredentialBindingForScope,
@@ -55,6 +67,11 @@ import {
55
67
  isSecretCredentialBindingValue
56
68
  } from "@executor-js/react/plugins/credential-bindings";
57
69
  import { SecretCredentialSlotBindings } from "@executor-js/react/plugins/credential-slot-bindings";
70
+ import {
71
+ useSourceCredentialBindingScopes,
72
+ useSourceCredentialBindingWriter
73
+ } from "@executor-js/react/plugins/source-credential-bindings";
74
+ import { CreatableSecretPicker } from "@executor-js/react/plugins/secret-header-auth";
58
75
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
59
76
  var ErrorMessage = Schema.Struct({ message: Schema.String });
60
77
  var decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage);
@@ -77,27 +94,26 @@ var openApiOAuthConnectionId = (sourceId, securitySchemeName, targetScope) => Co
77
94
  var effectiveClientSecretSlot = (oauth2) => oauth2.clientSecretSlot ?? oauth2ClientSecretSlot(oauth2.securitySchemeName);
78
95
  function EditOpenApiSource(props) {
79
96
  const displayScope = useScope();
80
- const scopeStack = useScopeStack();
81
97
  const userScope = useUserScope();
82
98
  const sourceSummaryResult = useAtomValue(sourceAtom(props.sourceId, displayScope));
83
99
  const sourceSummary = AsyncResult.isSuccess(sourceSummaryResult) && sourceSummaryResult.value ? sourceSummaryResult.value : null;
84
100
  const sourceScopeId = sourceSummary?.scopeId ?? displayScope;
85
101
  const sourceScope = ScopeId.make(sourceScopeId);
86
- const scopeRanks = useMemo(
87
- () => new Map(scopeStack.map((scope, index) => [scope.id, index])),
88
- [scopeStack]
89
- );
102
+ const {
103
+ credentialScopes,
104
+ credentialScopeOptions,
105
+ organizationCredentialScope,
106
+ secretBindingScopes,
107
+ scopeRanks
108
+ } = useSourceCredentialBindingScopes({ sourceScope });
90
109
  const sourceResult = useAtomValue(openApiSourceAtom(sourceScope, props.sourceId));
91
110
  const bindingsResult = useAtomValue(
92
111
  openApiSourceBindingsAtom(displayScope, props.sourceId, sourceScope)
93
112
  );
94
113
  const connectionsResult = useAtomValue(connectionsAtom(displayScope));
95
114
  const secretList = useSecretPickerSecrets();
96
- const doUpdate = useAtomSet(updateOpenApiSource, { mode: "promiseExit" });
97
- const doSetBinding = useAtomSet(setOpenApiSourceBinding, {
98
- mode: "promiseExit"
99
- });
100
- const doRemoveBinding = useAtomSet(removeOpenApiSourceBinding, {
115
+ const doConfigure = useAtomSet(configureSource, { mode: "promiseExit" });
116
+ const doSetBinding = useAtomSet(setSourceCredentialBinding, {
101
117
  mode: "promiseExit"
102
118
  });
103
119
  const doStartOAuth = useAtomSet(startOAuth, { mode: "promiseExit" });
@@ -115,11 +131,20 @@ function EditOpenApiSource(props) {
115
131
  const [sourceSaveState, setSourceSaveState] = useState("idle");
116
132
  const [error, setError] = useState(null);
117
133
  const [busyKey, setBusyKey] = useState(null);
134
+ const sourceBindingWriter = useSourceCredentialBindingWriter({
135
+ displayScope,
136
+ source: { id: props.sourceId, scope: sourceScope },
137
+ onError: setError,
138
+ errorMessageFromExit
139
+ });
118
140
  const [pendingOAuthConnection, setPendingOAuthConnection] = useState(null);
119
141
  const [loadedSourceKey, setLoadedSourceKey] = useState(null);
120
142
  const [selectedOAuthTokenScope, setSelectedOAuthTokenScope] = useState(
121
143
  userScope !== sourceScopeId ? userScope : sourceScopeId
122
144
  );
145
+ const [selectedOAuthClientIdScope, setSelectedOAuthClientIdScope] = useState(sourceScopeId);
146
+ const [selectedOAuthClientSecretScope, setSelectedOAuthClientSecretScope] = useState(sourceScopeId);
147
+ const [oauthEndpointsOpen, setOAuthEndpointsOpen] = useState(false);
123
148
  const [oauth2AuthorizationUrl, setOAuth2AuthorizationUrl] = useState(
124
149
  source?.config.oauth2?.authorizationUrl ?? ""
125
150
  );
@@ -141,6 +166,8 @@ function EditOpenApiSource(props) {
141
166
  const oauth2EndpointsSaveSeq = useRef(0);
142
167
  useEffect(() => {
143
168
  setSelectedOAuthTokenScope(userScope !== sourceScopeId ? userScope : sourceScopeId);
169
+ setSelectedOAuthClientIdScope(sourceScopeId);
170
+ setSelectedOAuthClientSecretScope(sourceScopeId);
144
171
  }, [sourceScopeId, userScope]);
145
172
  useEffect(() => {
146
173
  if (!source) return;
@@ -151,6 +178,7 @@ function EditOpenApiSource(props) {
151
178
  setOAuth2AuthorizationUrl(source.config.oauth2?.authorizationUrl ?? "");
152
179
  setOAuth2TokenUrl(source.config.oauth2?.tokenUrl ?? "");
153
180
  setOAuth2EndpointsSaveState("idle");
181
+ setOAuthEndpointsOpen(false);
154
182
  setSourceSaveState("idle");
155
183
  setLoadedSourceKey(sourceKey);
156
184
  }, [loadedSourceKey, source, sourceScopeId]);
@@ -170,12 +198,17 @@ function EditOpenApiSource(props) {
170
198
  setSourceSaveState("saving");
171
199
  setError(null);
172
200
  void (async () => {
173
- const exit = await doUpdate({
174
- params: { scopeId: displayScope, namespace: props.sourceId },
201
+ const exit = await doConfigure({
202
+ params: { scopeId: displayScope },
175
203
  payload: {
176
- sourceScope,
177
- name: nextName || void 0,
178
- baseUrl: nextBaseUrl || void 0
204
+ source: { id: props.sourceId, scope: sourceScope },
205
+ scope: sourceScope,
206
+ type: "openapi",
207
+ config: {
208
+ scope: sourceScope,
209
+ name: nextName || void 0,
210
+ baseUrl: nextBaseUrl || void 0
211
+ }
179
212
  },
180
213
  reactivityKeys: openApiWriteKeys
181
214
  });
@@ -195,7 +228,7 @@ function EditOpenApiSource(props) {
195
228
  }, [
196
229
  baseUrl,
197
230
  displayScope,
198
- doUpdate,
231
+ doConfigure,
199
232
  loadedSourceKey,
200
233
  name,
201
234
  props.sourceId,
@@ -236,29 +269,6 @@ function EditOpenApiSource(props) {
236
269
  }
237
270
  return slots;
238
271
  }, [source]);
239
- const credentialScopes = useMemo(() => {
240
- const entries = [{ scopeId: ScopeId.make(sourceScopeId), label: "Organization" }];
241
- if (userScope !== sourceScopeId) {
242
- entries.unshift({ scopeId: ScopeId.make(userScope), label: "Personal" });
243
- } else {
244
- entries[0] = {
245
- scopeId: ScopeId.make(sourceScopeId),
246
- label: "Credentials"
247
- };
248
- }
249
- return entries;
250
- }, [sourceScopeId, userScope]);
251
- const credentialScopeOptions = useMemo(
252
- () => credentialScopes.map((entry) => ({
253
- scopeId: entry.scopeId,
254
- label: entry.label,
255
- description: entry.label === "Personal" ? "Saved only for your account." : "Shared with everyone who can use this source."
256
- })),
257
- [credentialScopes]
258
- );
259
- const organizationCredentialScope = credentialScopes.find((entry) => entry.label === "Organization") ?? credentialScopes[0];
260
- const personalCredentialScope = credentialScopes.find((entry) => entry.label === "Personal") ?? null;
261
- const secretBindingScopes = personalCredentialScope && personalCredentialScope.scopeId !== organizationCredentialScope.scopeId ? [organizationCredentialScope, personalCredentialScope] : [organizationCredentialScope];
262
272
  const activeOAuthTokenScope = credentialScopes.find((entry) => entry.scopeId === selectedOAuthTokenScope) ?? credentialScopes[0];
263
273
  const activeOAuthTokenScopeId = activeOAuthTokenScope.scopeId;
264
274
  const activeOAuthTokenScopeLabel = activeOAuthTokenScope.label;
@@ -268,50 +278,10 @@ function EditOpenApiSource(props) {
268
278
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading configuration\u2026" })
269
279
  ] });
270
280
  }
271
- const setSecretBinding = async (targetScope, slot, secretId, secretScope) => {
272
- const inputKey = `${targetScope}:${slot}`;
273
- const trimmed = secretId.trim();
274
- if (!trimmed) return;
275
- setBusyKey(inputKey);
276
- setError(null);
277
- const exit = await doSetBinding({
278
- params: { scopeId: displayScope },
279
- payload: OpenApiSourceBindingInput.make({
280
- sourceId: props.sourceId,
281
- sourceScope,
282
- scope: targetScope,
283
- slot,
284
- value: {
285
- kind: "secret",
286
- secretId: SecretId.make(trimmed),
287
- secretScopeId: secretScope
288
- }
289
- }),
290
- reactivityKeys: sourceWriteKeys
291
- });
292
- if (Exit.isFailure(exit)) {
293
- setError(errorMessageFromExit(exit, "Failed to save credential binding"));
294
- }
295
- setBusyKey(null);
296
- };
297
- const clearBinding = async (targetScope, slot) => {
298
- setBusyKey(`${targetScope}:${slot}:clear`);
299
- setError(null);
300
- const exit = await doRemoveBinding({
301
- params: { scopeId: displayScope },
302
- payload: {
303
- sourceId: props.sourceId,
304
- sourceScope,
305
- slot,
306
- scope: targetScope
307
- },
308
- reactivityKeys: sourceWriteKeys
309
- });
310
- if (Exit.isFailure(exit)) {
311
- setError(errorMessageFromExit(exit, "Failed to clear credential binding"));
312
- }
313
- setBusyKey(null);
314
- };
281
+ const oauthClientSecretSlot = source.config.oauth2 ? effectiveClientSecretSlot(source.config.oauth2) : null;
282
+ const nonOAuthSecretSlots = secretSlots.filter(
283
+ (slot) => slot.kind === "secret" && (!source.config.oauth2 || slot.slot !== source.config.oauth2.clientIdSlot && slot.slot !== oauthClientSecretSlot)
284
+ );
315
285
  const connectOAuth = async (targetScope) => {
316
286
  const oauth2 = source.config.oauth2;
317
287
  if (!oauth2) return;
@@ -333,11 +303,13 @@ function EditOpenApiSource(props) {
333
303
  return;
334
304
  }
335
305
  const clientIdSecretId = clientIdBinding.value.secretId;
306
+ const clientIdSecretScopeId = clientIdBinding.value.secretScopeId ?? clientIdBinding.scopeId;
336
307
  if (oauth2.flow === "clientCredentials" && (!clientSecretBinding || !isSecretCredentialBindingValue(clientSecretBinding.value))) {
337
308
  setError("Client secret must be bound before connecting");
338
309
  return;
339
310
  }
340
311
  const clientSecretValue = oauth2.flow === "clientCredentials" && clientSecretBinding && isSecretCredentialBindingValue(clientSecretBinding.value) ? clientSecretBinding.value : null;
312
+ const clientSecretSecretScopeId = clientSecretBinding && isSecretCredentialBindingValue(clientSecretBinding.value) ? clientSecretBinding.value.secretScopeId ?? clientSecretBinding.scopeId : null;
341
313
  const existingConnection = exactCredentialBindingForScope(
342
314
  bindingRows,
343
315
  oauth2.connectionSlot,
@@ -370,7 +342,9 @@ function EditOpenApiSource(props) {
370
342
  kind: "client-credentials",
371
343
  tokenEndpoint: tokenUrl,
372
344
  clientIdSecretId,
345
+ clientIdSecretScopeId: String(clientIdSecretScopeId),
373
346
  clientSecretSecretId: clientSecretValue.secretId,
347
+ clientSecretSecretScopeId: clientSecretSecretScopeId ? String(clientSecretSecretScopeId) : null,
374
348
  scopes: [...oauth2.scopes]
375
349
  },
376
350
  pluginId: "openapi",
@@ -388,11 +362,10 @@ function EditOpenApiSource(props) {
388
362
  }
389
363
  const setBindingExit = await doSetBinding({
390
364
  params: { scopeId: displayScope },
391
- payload: OpenApiSourceBindingInput.make({
392
- sourceId: props.sourceId,
393
- sourceScope,
365
+ payload: SetSourceCredentialBindingInput.make({
366
+ source: { id: props.sourceId, scope: sourceScope },
394
367
  scope: targetScope,
395
- slot: oauth2.connectionSlot,
368
+ slotKey: oauth2.connectionSlot,
396
369
  value: {
397
370
  kind: "connection",
398
371
  connectionId: ConnectionId.make(response2.completedConnection.connectionId)
@@ -426,7 +399,9 @@ function EditOpenApiSource(props) {
426
399
  tokenEndpoint: tokenUrl,
427
400
  issuerUrl,
428
401
  clientIdSecretId,
402
+ clientIdSecretScopeId: String(clientIdSecretScopeId),
429
403
  clientSecretSecretId: clientSecretBinding && isSecretCredentialBindingValue(clientSecretBinding.value) ? clientSecretBinding.value.secretId : null,
404
+ clientSecretSecretScopeId: clientSecretSecretScopeId ? String(clientSecretSecretScopeId) : null,
430
405
  scopes: [...oauth2.scopes]
431
406
  },
432
407
  pluginId: "openapi",
@@ -451,11 +426,10 @@ function EditOpenApiSource(props) {
451
426
  onSuccess: async (result) => {
452
427
  const setBindingExit = await doSetBinding({
453
428
  params: { scopeId: displayScope },
454
- payload: OpenApiSourceBindingInput.make({
455
- sourceId: props.sourceId,
456
- sourceScope,
429
+ payload: SetSourceCredentialBindingInput.make({
430
+ source: { id: props.sourceId, scope: sourceScope },
457
431
  scope: targetScope,
458
- slot: oauth2.connectionSlot,
432
+ slotKey: oauth2.connectionSlot,
459
433
  value: {
460
434
  kind: "connection",
461
435
  connectionId: ConnectionId.make(result.connectionId)
@@ -497,23 +471,28 @@ function EditOpenApiSource(props) {
497
471
  }
498
472
  ),
499
473
  /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsxs(CardStackContent, { className: "border-t-0", children: [
500
- /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsx(CardStackEntryContent, { children: /* @__PURE__ */ jsx(CardStackEntryTitle, { children: "Secrets" }) }) }),
501
- /* @__PURE__ */ jsx(
502
- SecretCredentialSlotBindings,
503
- {
504
- slots: secretSlots.filter((slot) => slot.kind === "secret"),
505
- bindingScopes: secretBindingScopes,
506
- bindingRows,
507
- scopeRanks,
508
- secrets: secretList,
509
- sourceId: props.sourceId,
510
- sourceName: source.name,
511
- credentialScopeOptions,
512
- busyKey,
513
- onSetSecretBinding: setSecretBinding,
514
- onClearBinding: clearBinding
515
- }
516
- ),
474
+ nonOAuthSecretSlots.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
475
+ /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
476
+ /* @__PURE__ */ jsx(CardStackEntryTitle, { children: "Request credentials" }),
477
+ /* @__PURE__ */ jsx(CardStackEntryDescription, { children: "Headers and query parameters sent with every API request." })
478
+ ] }) }),
479
+ /* @__PURE__ */ jsx(
480
+ SecretCredentialSlotBindings,
481
+ {
482
+ slots: nonOAuthSecretSlots,
483
+ bindingScopes: secretBindingScopes,
484
+ bindingRows,
485
+ scopeRanks,
486
+ secrets: secretList,
487
+ sourceId: props.sourceId,
488
+ sourceName: source.name,
489
+ credentialScopeOptions,
490
+ busyKey: sourceBindingWriter.busyKey,
491
+ onSetSecretBinding: sourceBindingWriter.setSecretBinding,
492
+ onClearBinding: sourceBindingWriter.clearBinding
493
+ }
494
+ )
495
+ ] }),
517
496
  source.config.oauth2 && (() => {
518
497
  const oauth2 = source.config.oauth2;
519
498
  const trimmedAuthUrl = oauth2AuthorizationUrl.trim();
@@ -529,22 +508,27 @@ function EditOpenApiSource(props) {
529
508
  const seq = ++oauth2EndpointsSaveSeq.current;
530
509
  setOAuth2EndpointsSaveState("saving");
531
510
  setError(null);
532
- const exit = await doUpdate({
533
- params: { scopeId: displayScope, namespace: props.sourceId },
511
+ const exit = await doConfigure({
512
+ params: { scopeId: displayScope },
534
513
  payload: {
535
- sourceScope,
536
- oauth2: OAuth2SourceConfig.make({
537
- kind: "oauth2",
538
- securitySchemeName: oauth2.securitySchemeName,
539
- flow: oauth2.flow,
540
- tokenUrl: trimmedTokenUrl,
541
- authorizationUrl: isAuthCode ? trimmedAuthUrl || null : null,
542
- issuerUrl: oauth2.issuerUrl ?? null,
543
- clientIdSlot: oauth2.clientIdSlot,
544
- clientSecretSlot: oauth2.clientSecretSlot,
545
- connectionSlot: oauth2.connectionSlot,
546
- scopes: [...oauth2.scopes]
547
- })
514
+ source: { id: props.sourceId, scope: sourceScope },
515
+ scope: sourceScope,
516
+ type: "openapi",
517
+ config: {
518
+ scope: sourceScope,
519
+ oauth2Source: OAuth2SourceConfig.make({
520
+ kind: "oauth2",
521
+ securitySchemeName: oauth2.securitySchemeName,
522
+ flow: oauth2.flow,
523
+ tokenUrl: trimmedTokenUrl,
524
+ authorizationUrl: isAuthCode ? trimmedAuthUrl || null : null,
525
+ issuerUrl: oauth2.issuerUrl ?? null,
526
+ clientIdSlot: oauth2.clientIdSlot,
527
+ clientSecretSlot: oauth2.clientSecretSlot,
528
+ connectionSlot: oauth2.connectionSlot,
529
+ scopes: [...oauth2.scopes]
530
+ })
531
+ }
548
532
  },
549
533
  reactivityKeys: openApiWriteKeys
550
534
  });
@@ -580,78 +564,203 @@ function EditOpenApiSource(props) {
580
564
  const isConnected = connection !== null && connection !== void 0;
581
565
  const statusText = isConnecting || isPendingOAuthConnection ? "Saving OAuth connection..." : connectionBinding && bindingScopeId ? connection ? bindingScopeId === activeOAuthTokenScopeId ? `Connected in ${activeOAuthTokenScopeLabel.toLowerCase()} as ${connection.identityLabel ?? connection.id}` : `Using organization connection ${connection.identityLabel ?? connection.id}` : bindingScopeId === activeOAuthTokenScopeId ? `Saved connection is missing in ${activeOAuthTokenScopeLabel.toLowerCase()}` : "Organization connection is missing" : `No ${activeOAuthTokenScopeLabel.toLowerCase()} connection`;
582
566
  const connectDisabled = isConnecting || endpointsDirty || saving;
567
+ const clientSecretSlot = effectiveClientSecretSlot(oauth2);
568
+ const renderAppSecret = (input) => {
569
+ const activeScope = credentialScopes.find((entry) => entry.scopeId === input.scopeId) ?? organizationCredentialScope;
570
+ const exactSecret = exactCredentialBindingForScope(
571
+ bindingRows,
572
+ input.slot,
573
+ activeScope.scopeId
574
+ );
575
+ const effectiveSecret = effectiveCredentialBindingForScope(
576
+ bindingRows,
577
+ input.slot,
578
+ activeScope.scopeId,
579
+ scopeRanks
580
+ );
581
+ const exactSecretId = exactSecret && isSecretCredentialBindingValue(exactSecret.value) ? exactSecret.value.secretId : null;
582
+ const inheritedSecret = !exactSecretId && effectiveSecret && effectiveSecret.scopeId !== activeScope.scopeId && isSecretCredentialBindingValue(effectiveSecret.value) ? effectiveSecret : null;
583
+ const status = exactSecretId ? `${activeScope.label} credential set` : inheritedSecret ? "Using organization credential" : "Not set";
584
+ const inputKey = `${activeScope.scopeId}:${input.slot}`;
585
+ const clearKey = `${activeScope.scopeId}:${input.slot}:clear`;
586
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
587
+ /* @__PURE__ */ jsxs(FieldLabel, { className: "text-[11px]", children: [
588
+ input.label,
589
+ " ",
590
+ input.hint && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
591
+ "\xB7 ",
592
+ input.hint
593
+ ] })
594
+ ] }),
595
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
596
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
597
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
598
+ /* @__PURE__ */ jsx(FieldLabel, { className: "text-[11px]", children: "Secret" }),
599
+ /* @__PURE__ */ jsxs(HelpTooltip, { label: `${input.label} secret`, children: [
600
+ "Select or create the OAuth ",
601
+ input.label.toLowerCase(),
602
+ " secret."
603
+ ] }),
604
+ /* @__PURE__ */ jsx("span", { className: "ml-auto truncate text-xs text-muted-foreground", children: status })
605
+ ] }),
606
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
607
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
608
+ CreatableSecretPicker,
609
+ {
610
+ value: exactSecretId,
611
+ onSelect: (secretId, secretScopeId) => void sourceBindingWriter.setSecretBinding(
612
+ activeScope.scopeId,
613
+ input.slot,
614
+ secretId,
615
+ secretScopeId ?? activeScope.scopeId
616
+ ),
617
+ secrets: secretList,
618
+ placeholder: "Select or create a secret",
619
+ targetScope: activeScope.scopeId,
620
+ credentialScopeOptions,
621
+ suggestedId: `source-binding-${slugify(props.sourceId)}-${slugify(
622
+ input.slot
623
+ )}-${slugify(activeScope.scopeId)}`,
624
+ sourceName: source.name,
625
+ secretLabel: input.label
626
+ }
627
+ ) }),
628
+ exactSecretId && /* @__PURE__ */ jsx(
629
+ Button,
630
+ {
631
+ variant: "outline",
632
+ size: "sm",
633
+ onClick: () => void sourceBindingWriter.clearBinding(
634
+ activeScope.scopeId,
635
+ input.slot
636
+ ),
637
+ disabled: sourceBindingWriter.busyKey === clearKey,
638
+ children: "Clear"
639
+ }
640
+ ),
641
+ sourceBindingWriter.busyKey === inputKey && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Saving\u2026" })
642
+ ] })
643
+ ] }),
644
+ /* @__PURE__ */ jsx(
645
+ CredentialScopeDropdown,
646
+ {
647
+ value: activeScope.scopeId,
648
+ options: credentialScopeOptions,
649
+ onChange: input.onScopeChange,
650
+ label: "Used by",
651
+ help: `Choose where this OAuth ${input.label.toLowerCase()} credential lives.`
652
+ }
653
+ )
654
+ ] })
655
+ ] });
656
+ };
583
657
  return /* @__PURE__ */ jsxs(Fragment, { children: [
658
+ /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
659
+ /* @__PURE__ */ jsx(CardStackEntryTitle, { children: "OAuth" }),
660
+ /* @__PURE__ */ jsx(CardStackEntryDescription, { children: "Configure app credentials and connect accounts for this source." })
661
+ ] }) }),
662
+ /* @__PURE__ */ jsx(CardStackEntryField, { label: "OAuth app credentials", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4 rounded-lg border border-border/60 bg-muted/10 p-3", children: [
663
+ renderAppSecret({
664
+ slot: oauth2.clientIdSlot,
665
+ label: "Client ID",
666
+ scopeId: ScopeId.make(selectedOAuthClientIdScope),
667
+ onScopeChange: setSelectedOAuthClientIdScope
668
+ }),
669
+ renderAppSecret({
670
+ slot: clientSecretSlot,
671
+ label: "Client secret",
672
+ hint: oauth2.flow === "authorizationCode" ? "Optional for public clients with PKCE" : void 0,
673
+ scopeId: ScopeId.make(selectedOAuthClientSecretScope),
674
+ onScopeChange: setSelectedOAuthClientSecretScope
675
+ })
676
+ ] }) }),
677
+ /* @__PURE__ */ jsx(CardStackEntryField, { label: "Account connection", children: /* @__PURE__ */ jsx(
678
+ CredentialUsageRow,
679
+ {
680
+ value: activeOAuthTokenScopeId,
681
+ options: credentialScopeOptions,
682
+ onChange: setSelectedOAuthTokenScope,
683
+ label: "Connection saved to",
684
+ help: "Choose where the signed-in OAuth token is saved.",
685
+ children: /* @__PURE__ */ jsxs(
686
+ CredentialControlField,
687
+ {
688
+ label: "OAuth connection",
689
+ help: "Start the provider OAuth flow.",
690
+ children: [
691
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-9 items-center gap-2 rounded-md border border-border bg-muted/30 px-3 py-2", children: [
692
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-xs text-muted-foreground", children: statusText }),
693
+ /* @__PURE__ */ jsx(
694
+ Button,
695
+ {
696
+ size: "sm",
697
+ onClick: () => void connectOAuth(activeOAuthTokenScopeId),
698
+ disabled: connectDisabled,
699
+ children: isConnecting ? "Connecting\u2026" : isConnected ? "Reconnect" : "Connect"
700
+ }
701
+ )
702
+ ] }),
703
+ endpointsDirty && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Save endpoint changes before reconnecting." })
704
+ ]
705
+ }
706
+ )
707
+ }
708
+ ) }),
584
709
  /* @__PURE__ */ jsxs(CardStackEntry, { children: [
585
710
  /* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
586
- /* @__PURE__ */ jsx(CardStackEntryTitle, { children: "OAuth Endpoints" }),
587
- /* @__PURE__ */ jsx(CardStackEntryDescription, { children: "Override the URLs from the OpenAPI spec when a provider publishes the wrong values." })
711
+ /* @__PURE__ */ jsx(CardStackEntryTitle, { children: "Advanced endpoints" }),
712
+ /* @__PURE__ */ jsx(CardStackEntryDescription, { children: "Override provider URLs only when the OpenAPI spec is wrong." })
588
713
  ] }),
589
714
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
590
715
  oauth2EndpointsSaveState !== "idle" && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: saving ? "Saving\u2026" : "Saved" }),
591
716
  /* @__PURE__ */ jsx(
592
717
  Button,
593
718
  {
719
+ variant: "outline",
594
720
  size: "sm",
595
- onClick: () => void saveOAuth2Endpoints(),
596
- disabled: !canSave,
597
- children: "Save"
721
+ onClick: () => setOAuthEndpointsOpen((open) => !open),
722
+ children: oauthEndpointsOpen ? "Hide" : endpointsDirty ? "Review" : "Show"
598
723
  }
599
724
  )
600
725
  ] })
601
726
  ] }),
602
- isAuthCode && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Authorization URL", children: /* @__PURE__ */ jsx(
603
- Input,
604
- {
605
- value: oauth2AuthorizationUrl,
606
- onChange: (e) => setOAuth2AuthorizationUrl(e.target.value),
607
- className: "font-mono text-sm"
608
- }
609
- ) }),
610
- /* @__PURE__ */ jsx(CardStackEntryField, { label: "Token URL", children: /* @__PURE__ */ jsx(
611
- Input,
612
- {
613
- value: oauth2TokenUrl,
614
- onChange: (e) => setOAuth2TokenUrl(e.target.value),
615
- className: "font-mono text-sm"
616
- }
617
- ) }),
618
- /* @__PURE__ */ jsx(CardStackEntryField, { label: "Redirect URL", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
619
- /* @__PURE__ */ jsxs("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: [
620
- /* @__PURE__ */ jsx("span", { className: "truncate flex-1 text-foreground", children: oauth2RedirectUrl }),
621
- /* @__PURE__ */ jsx(CopyButton, { value: oauth2RedirectUrl })
622
- ] }),
623
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Add this to your OAuth app's allowed redirects." })
624
- ] }) }),
625
- credentialScopes.length > 1 && /* @__PURE__ */ jsxs(CardStackEntry, { children: [
626
- /* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
627
- /* @__PURE__ */ jsx(CardStackEntryTitle, { children: "OAuth token" }),
628
- /* @__PURE__ */ jsx(CardStackEntryDescription, { children: "Choose where the signed-in OAuth token is saved." })
629
- ] }),
630
- /* @__PURE__ */ jsx(
631
- FilterTabs,
727
+ oauthEndpointsOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
728
+ isAuthCode && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Authorization URL", children: /* @__PURE__ */ jsx(
729
+ Input,
632
730
  {
633
- tabs: credentialScopes.map((entry) => ({
634
- value: entry.scopeId,
635
- label: entry.label
636
- })),
637
- value: activeOAuthTokenScopeId,
638
- onChange: setSelectedOAuthTokenScope
731
+ value: oauth2AuthorizationUrl,
732
+ onChange: (e) => setOAuth2AuthorizationUrl(e.target.value),
733
+ className: "font-mono text-sm"
639
734
  }
640
- )
641
- ] }),
642
- /* @__PURE__ */ jsx(CardStackEntryField, { label: "OAuth Connection", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
643
- /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: statusText }),
644
- /* @__PURE__ */ jsx(
645
- Button,
735
+ ) }),
736
+ /* @__PURE__ */ jsx(CardStackEntryField, { label: "Token URL", children: /* @__PURE__ */ jsx(
737
+ Input,
646
738
  {
647
- size: "sm",
648
- onClick: () => void connectOAuth(activeOAuthTokenScopeId),
649
- disabled: connectDisabled,
650
- children: isConnecting ? "Connecting\u2026" : isConnected ? "Reconnect" : "Connect"
739
+ value: oauth2TokenUrl,
740
+ onChange: (e) => setOAuth2TokenUrl(e.target.value),
741
+ className: "font-mono text-sm"
651
742
  }
652
- ),
653
- endpointsDirty && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Save endpoint changes before reconnecting." })
654
- ] }) })
743
+ ) }),
744
+ /* @__PURE__ */ jsx(CardStackEntryField, { label: "Redirect URL", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
745
+ /* @__PURE__ */ jsxs("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: [
746
+ /* @__PURE__ */ jsx("span", { className: "truncate flex-1 text-foreground", children: oauth2RedirectUrl }),
747
+ /* @__PURE__ */ jsx(CopyButton, { value: oauth2RedirectUrl })
748
+ ] }),
749
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Add this to your OAuth app's allowed redirects." })
750
+ ] }) }),
751
+ /* @__PURE__ */ jsxs(CardStackEntry, { children: [
752
+ /* @__PURE__ */ jsx(CardStackEntryContent, { children: /* @__PURE__ */ jsx(CardStackEntryDescription, { children: "Save endpoint changes before reconnecting." }) }),
753
+ /* @__PURE__ */ jsx(
754
+ Button,
755
+ {
756
+ size: "sm",
757
+ onClick: () => void saveOAuth2Endpoints(),
758
+ disabled: !canSave,
759
+ children: "Save endpoints"
760
+ }
761
+ )
762
+ ] })
763
+ ] })
655
764
  ] });
656
765
  })()
657
766
  ] }) }),
@@ -662,4 +771,4 @@ function EditOpenApiSource(props) {
662
771
  export {
663
772
  EditOpenApiSource as default
664
773
  };
665
- //# sourceMappingURL=EditOpenApiSource-I4NIGIIJ.js.map
774
+ //# sourceMappingURL=EditOpenApiSource-AOA7APR2.js.map