@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.
- package/dist/AddOpenApiSource-7M52SRUX.js +893 -0
- package/dist/AddOpenApiSource-7M52SRUX.js.map +1 -0
- package/dist/EditOpenApiSource-WTAMRJUK.js +68 -0
- package/dist/EditOpenApiSource-WTAMRJUK.js.map +1 -0
- package/dist/OpenApiAccountsPanel-3VJJXNQF.js +112 -0
- package/dist/OpenApiAccountsPanel-3VJJXNQF.js.map +1 -0
- package/dist/api/group.d.ts +104 -225
- package/dist/api/index.d.ts +127 -271
- package/dist/{chunk-BB5IAKRG.js → chunk-AQ7JDDRM.js} +12 -2
- package/dist/chunk-AQ7JDDRM.js.map +1 -0
- package/dist/chunk-BSLE6HCE.js +181 -0
- package/dist/chunk-BSLE6HCE.js.map +1 -0
- package/dist/chunk-MZWZQ24W.js +226 -0
- package/dist/chunk-MZWZQ24W.js.map +1 -0
- package/dist/chunk-OSIFYIQP.js +623 -0
- package/dist/chunk-OSIFYIQP.js.map +1 -0
- package/dist/chunk-QSSRVK6M.js +139 -0
- package/dist/chunk-QSSRVK6M.js.map +1 -0
- package/dist/chunk-V7VCHYOY.js +1544 -0
- package/dist/chunk-V7VCHYOY.js.map +1 -0
- package/dist/{chunk-AN4HJFNP.js → chunk-YVRI7KRC.js} +162 -186
- package/dist/chunk-YVRI7KRC.js.map +1 -0
- package/dist/client.js +9 -8
- package/dist/client.js.map +1 -1
- package/dist/core.js +28 -11
- package/dist/index.js +11 -4
- package/dist/react/AddOpenApiSource.d.ts +2 -13
- package/dist/react/GoogleProductPicker.d.ts +9 -0
- package/dist/react/OpenApiAccountsPanel.d.ts +6 -0
- package/dist/react/OpenApiSourceDetailsFields.d.ts +3 -2
- package/dist/react/atoms.d.ts +177 -192
- package/dist/react/auth-method-config.d.ts +15 -0
- package/dist/react/client.d.ts +103 -224
- package/dist/react/index.d.ts +2 -2
- package/dist/react/source-plugin.d.ts +2 -2
- package/dist/sdk/config.d.ts +75 -0
- package/dist/sdk/configure.test.d.ts +1 -0
- package/dist/sdk/describe-auth-methods.test.d.ts +1 -0
- package/dist/sdk/errors.d.ts +4 -6
- package/dist/sdk/extract.d.ts +7 -5
- package/dist/sdk/google-bundle.test.d.ts +1 -0
- package/dist/sdk/google-discovery.d.ts +43 -0
- package/dist/sdk/google-discovery.test.d.ts +1 -0
- package/dist/sdk/google-oauth-batches.d.ts +13 -0
- package/dist/sdk/google-oauth-batches.test.d.ts +1 -0
- package/dist/sdk/google-oauth-scopes.d.ts +3 -0
- package/dist/sdk/google-oauth-scopes.test.d.ts +1 -0
- package/dist/sdk/google-presets.d.ts +16 -0
- package/dist/sdk/google-presets.test.d.ts +1 -0
- package/dist/sdk/google-product-picker-scopes.test.d.ts +1 -0
- package/dist/sdk/index.d.ts +7 -5
- package/dist/sdk/invoke.d.ts +6 -9
- package/dist/sdk/openapi-utils.d.ts +1 -0
- package/dist/sdk/plugin.d.ts +74 -231
- package/dist/sdk/presets.d.ts +2 -1
- package/dist/sdk/preview.d.ts +20 -15
- package/dist/sdk/query-serialization.test.d.ts +1 -0
- package/dist/sdk/store.d.ts +14 -41
- package/dist/sdk/types.d.ts +23 -51
- package/dist/testing/index.d.ts +49 -38
- package/dist/testing.js +46 -18
- package/dist/testing.js.map +1 -1
- package/package.json +6 -4
- package/dist/AddOpenApiSource-NSCULGTM.js +0 -19
- package/dist/AddOpenApiSource-NSCULGTM.js.map +0 -1
- package/dist/EditOpenApiSource-MV7NYTRP.js +0 -774
- package/dist/EditOpenApiSource-MV7NYTRP.js.map +0 -1
- package/dist/OpenApiSourceSummary-7JBS7PUV.js +0 -122
- package/dist/OpenApiSourceSummary-7JBS7PUV.js.map +0 -1
- package/dist/chunk-2ZKKZYZH.js +0 -1181
- package/dist/chunk-2ZKKZYZH.js.map +0 -1
- package/dist/chunk-AN4HJFNP.js.map +0 -1
- package/dist/chunk-BB5IAKRG.js.map +0 -1
- package/dist/chunk-PRVJDE43.js +0 -2101
- package/dist/chunk-PRVJDE43.js.map +0 -1
- package/dist/chunk-X5JX3KTA.js +0 -201
- package/dist/chunk-X5JX3KTA.js.map +0 -1
- package/dist/react/OpenApiSourceSummary.d.ts +0 -5
- package/dist/sdk/credential-status.d.ts +0 -23
- package/dist/sdk/source-contracts.d.ts +0 -55
- /package/dist/{sdk/credential-status.test.d.ts → react/auth-method-config.test.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/AddOpenApiSource.tsx","../src/react/OpenApiSourceDetailsFields.tsx","../src/react/GoogleProductPicker.tsx","../src/sdk/google-oauth-batches.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAtomSet, useAtomValue } from \"@effect/atom-react\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as Option from \"effect/Option\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Schema from \"effect/Schema\";\nimport * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\n\nimport {\n AuthTemplateSlug,\n IntegrationSlug,\n type OAuthAuthentication,\n} from \"@executor-js/sdk/shared\";\nimport { integrationsOptimisticAtom } from \"@executor-js/react/api/atoms\";\nimport { integrationWriteKeys } from \"@executor-js/react/api/reactivity-keys\";\nimport {\n slugifyNamespace,\n useIntegrationIdentity,\n} from \"@executor-js/react/plugins/integration-identity\";\nimport { Button } from \"@executor-js/react/components/button\";\nimport {\n AuthTemplateEditor,\n type AuthTemplateEditorValue,\n} from \"@executor-js/react/components/auth-template-editor\";\nimport { CardStack, CardStackContent } from \"@executor-js/react/components/card-stack\";\nimport { FieldLabel } from \"@executor-js/react/components/field\";\nimport { FloatActions } from \"@executor-js/react/components/float-actions\";\nimport { Textarea } from \"@executor-js/react/components/textarea\";\nimport { IOSSpinner, Spinner } from \"@executor-js/react/components/spinner\";\nimport { PlusIcon, XIcon } from \"lucide-react\";\n\nimport { authenticationFromEditorValue, editorValueFromAuthentication } from \"./auth-method-config\";\nimport { addOpenApiSpec, previewOpenApiSpec } from \"./atoms\";\nimport { OpenApiSourceDetailsFields } from \"./OpenApiSourceDetailsFields\";\nimport { GoogleProductPicker } from \"./GoogleProductPicker\";\nimport { openApiPresets } from \"../sdk/presets\";\nimport {\n GOOGLE_BUNDLE_PRESET_ID,\n googleOpenApiPresets,\n type GoogleOpenApiPreset,\n} from \"../sdk/google-presets\";\nimport type { SpecPreview, HeaderPreset, OAuth2Preset } from \"../sdk/preview\";\nimport {\n type APIKeyAuthentication,\n type Authentication,\n type ServerInfo,\n TOKEN_VARIABLE,\n variable,\n} from \"../sdk/types\";\nimport { expandServerUrlOptions } from \"../sdk/openapi-utils\";\n\nconst GOOGLE_BUNDLE_BASE_URL = \"https://www.googleapis.com/\";\nconst GOOGLE_BUNDLE_FAVICON = \"https://fonts.gstatic.com/s/i/productlogos/googleg/v6/192px.svg\";\n\n// The bundle picker opens with the featured Google APIs pre-checked.\nconst googleBundleDefaultPresetIds: ReadonlySet<string> = new Set(\n googleOpenApiPresets\n .filter((preset: GoogleOpenApiPreset) => preset.featured)\n .map((preset: GoogleOpenApiPreset) => preset.id),\n);\n\nconst googleBundleUrls = (\n selectedPresetIds: ReadonlySet<string>,\n customUrls: readonly string[],\n): readonly string[] => {\n const fromPresets = googleOpenApiPresets.flatMap((preset: GoogleOpenApiPreset) =>\n preset.url && selectedPresetIds.has(preset.id) ? [preset.url] : [],\n );\n // Preset URLs first (stable order), then any custom Discovery URLs, de-duped.\n return [...new Set([...fromPresets, ...customUrls])];\n};\n\nconst ErrorMessage = Schema.Struct({ message: Schema.String });\nconst decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage);\n\nconst errorMessageFromExit = (exit: Exit.Exit<unknown, unknown>, fallback: string): string =>\n Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), {\n onNone: () => fallback,\n onSome: ({ message }) => message,\n });\n\nconst isIntegrationAlreadyExistsExit = (exit: Exit.Exit<unknown, unknown>): boolean =>\n Option.match(Exit.findErrorOption(exit), {\n onNone: () => false,\n onSome: Predicate.isTagged(\"IntegrationAlreadyExistsError\"),\n });\n\nconst integrationExistsMessage = (slug: string): string =>\n `An integration named \"${slug}\" already exists. To add more authentication, update your existing integration.`;\n\n// ---------------------------------------------------------------------------\n// OpenAPI url helpers — specs sometimes ship relative OAuth endpoints; resolve\n// them against the chosen base URL so the stored auth template is absolute.\n// ---------------------------------------------------------------------------\n\nexport function resolveOAuthUrl(url: string, baseUrl: string): string {\n if (!url) return url;\n // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: URL constructor normalizes provider metadata URLs\n try {\n new URL(url);\n return url;\n } catch {\n if (!baseUrl) return url;\n // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: URL constructor resolves relative provider metadata URLs\n try {\n return new URL(url, baseUrl).toString();\n } catch {\n return url;\n }\n }\n}\n\nconst standardOidcIdentityScopes = [\"openid\", \"email\", \"profile\"] as const;\n\nconst identityScopesForPreset = (\n identityScopes: OAuth2Preset[\"identityScopes\"],\n): readonly string[] => {\n if (identityScopes === false) return [];\n return identityScopes === \"auto\" ? standardOidcIdentityScopes : identityScopes;\n};\n\nconst resolvedOAuthScopes = (\n apiScopes: Iterable<string>,\n identityScopes: OAuth2Preset[\"identityScopes\"],\n): string[] => {\n const merged = new Set(apiScopes);\n for (const scope of identityScopesForPreset(identityScopes)) merged.add(scope);\n return [...merged];\n};\n\nconst isGoogleDiscoveryUrl = (url: string): boolean => {\n const trimmed = url.trim();\n if (!URL.canParse(trimmed)) return false;\n const parsed = new URL(trimmed);\n const host = parsed.hostname.toLowerCase();\n if (!host.endsWith(\"googleapis.com\")) return false;\n return parsed.pathname.includes(\"/discovery/\") || parsed.pathname.includes(\"$discovery\");\n};\n\nconst normalizePresetUrl = (url: string): string => {\n const trimmed = url.trim();\n if (!URL.canParse(trimmed)) return trimmed.replace(/\\/$/, \"\");\n const parsed = new URL(trimmed);\n parsed.hash = \"\";\n parsed.searchParams.sort();\n return parsed.toString().replace(/\\/$/, \"\");\n};\n\nconst specInputForAdd = (input: string) => {\n const value = input.trim();\n const parsed = Effect.runSyncExit(\n Effect.try({\n try: () => new URL(value),\n catch: () => null,\n }),\n );\n return Exit.isSuccess(parsed)\n ? isGoogleDiscoveryUrl(value)\n ? { kind: \"googleDiscovery\" as const, url: value }\n : { kind: \"url\" as const, url: value }\n : { kind: \"blob\" as const, value };\n};\n\n// ---------------------------------------------------------------------------\n// Auth-template builders — turn a preview preset into the integration's stored\n// `Authentication` template (v2). The header preset becomes an `apiKey` template\n// whose secret header value renders the resolved credential via `variable(token)`;\n// the oauth2 preset becomes an `oauth` template carrying the provider endpoints.\n//\n// Post-redesign the add flow no longer asks the user to pick ONE method: every\n// spec-detected method is registered so the integration's detail hub can list\n// them and Add-account can choose among them (P6: add without auth, connect\n// later).\n// ---------------------------------------------------------------------------\n\nconst headerPrefix = (preset: HeaderPreset, headerName: string): string | undefined => {\n const label = preset.label.toLowerCase();\n if (headerName.toLowerCase() === \"authorization\") {\n if (label.includes(\"bearer\")) return \"Bearer \";\n if (label.includes(\"basic\")) return \"Basic \";\n }\n return undefined;\n};\n\nconst apiKeyTemplateFromHeaderPreset = (\n preset: HeaderPreset,\n slug: AuthTemplateSlug,\n): APIKeyAuthentication => {\n const headers: Record<string, (string | ReturnType<typeof variable>)[]> = {};\n for (const headerName of preset.secretHeaders) {\n const prefix = headerPrefix(preset, headerName);\n headers[headerName] = prefix ? [prefix, variable(TOKEN_VARIABLE)] : [variable(TOKEN_VARIABLE)];\n }\n return { slug, type: \"apiKey\", headers };\n};\n\nconst oauthTemplateFromPreset = (\n preset: OAuth2Preset,\n baseUrl: string,\n slug: AuthTemplateSlug,\n scopes: readonly string[],\n): OAuthAuthentication => ({\n slug,\n type: \"oauth\",\n authorizationUrl: resolveOAuthUrl(\n Option.getOrElse(preset.authorizationUrl, () => \"\"),\n baseUrl,\n ),\n tokenUrl: resolveOAuthUrl(preset.tokenUrl, baseUrl),\n scopes: [...scopes],\n});\n\nconst expandServerOptions = (server: ServerInfo) =>\n expandServerUrlOptions(server).map((value) => ({ value, label: value }));\n\nconst firstBaseUrlForPreview = (preview: SpecPreview): string => {\n const firstServer = preview.servers[0];\n return firstServer ? (expandServerUrlOptions(firstServer)[0] ?? \"\") : \"\";\n};\n\n// ---------------------------------------------------------------------------\n// All spec-detected auth methods → the union of stored `Authentication`\n// templates. Header presets become apiKey templates; each oauth2 preset becomes\n// an oauth template (with its declared API scopes plus, for auth-code flows,\n// the standard identity scopes). Slugs stay deterministic per method so the\n// stored template is stable across previews of the same spec. Adding an\n// integration whose slug already exists is blocked (see the existing-slug\n// guard below); to add more auth, update the existing integration instead.\n// ---------------------------------------------------------------------------\n\nconst detectedAuthenticationTemplates = (\n headerPresets: readonly HeaderPreset[],\n oauth2Presets: readonly OAuth2Preset[],\n baseUrl: string,\n): readonly Authentication[] => {\n const templates: Authentication[] = [];\n headerPresets.forEach((preset, index) => {\n templates.push(\n apiKeyTemplateFromHeaderPreset(preset, AuthTemplateSlug.make(`apikey-${index}`)),\n );\n });\n for (const preset of oauth2Presets) {\n const scopes = resolvedOAuthScopes(Object.keys(preset.scopes), preset.identityScopes);\n templates.push(\n oauthTemplateFromPreset(\n preset,\n baseUrl,\n AuthTemplateSlug.make(`oauth-${preset.securitySchemeName}`),\n scopes,\n ),\n );\n }\n return templates;\n};\n\n// ---------------------------------------------------------------------------\n// Component — single progressive form. Post-redesign: preview → addSpec\n// (register the integration catalog entry with ALL detected auth methods) →\n// route to the integration's detail hub, where the user adds accounts. The add\n// flow no longer creates a connection.\n// ---------------------------------------------------------------------------\n\nexport default function AddOpenApiSource(props: {\n onComplete: (slug?: string) => void;\n onCancel: () => void;\n initialUrl?: string;\n initialPreset?: string;\n initialNamespace?: string;\n}) {\n const isGoogleBundlePreset = props.initialPreset === GOOGLE_BUNDLE_PRESET_ID;\n\n // Spec input. For the Google BUNDLE preset the input is a product picker (a set\n // of selected Discovery URLs), not a single spec URL/blob — the merge happens\n // server-side via `{ kind: \"googleDiscoveryBundle\", urls }`, so the textarea\n // preview path is bypassed entirely.\n const [specUrl, setSpecUrl] = useState(props.initialUrl ?? \"\");\n const [selectedPresetIds, setSelectedPresetIds] = useState<ReadonlySet<string>>(\n googleBundleDefaultPresetIds,\n );\n const [customDiscoveryUrls, setCustomDiscoveryUrls] = useState<readonly string[]>([]);\n const [analyzing, setAnalyzing] = useState(false);\n const [analyzeError, setAnalyzeError] = useState<string | null>(null);\n\n // After analysis\n const [preview, setPreview] = useState<SpecPreview | null>(null);\n const [baseUrl, setBaseUrl] = useState(isGoogleBundlePreset ? GOOGLE_BUNDLE_BASE_URL : \"\");\n const identityFallbackName = isGoogleBundlePreset\n ? \"Google\"\n : preview\n ? Option.getOrElse(preview.title, () => \"\")\n : \"\";\n const identity = useIntegrationIdentity({\n fallbackName: identityFallbackName,\n fallbackNamespace: props.initialNamespace ?? (isGoogleBundlePreset ? \"google\" : undefined),\n });\n\n const bundleDiscoveryUrls = useMemo(\n () => googleBundleUrls(selectedPresetIds, customDiscoveryUrls),\n [selectedPresetIds, customDiscoveryUrls],\n );\n\n const toggleBundlePreset = useCallback((presetId: string, checked: boolean) => {\n setSelectedPresetIds((current: ReadonlySet<string>) => {\n const next = new Set(current);\n if (checked) next.add(presetId);\n else next.delete(presetId);\n return next;\n });\n }, []);\n\n const addCustomDiscoveryUrl = useCallback((url: string) => {\n setCustomDiscoveryUrls((current: readonly string[]) =>\n current.includes(url) ? current : [...current, url],\n );\n }, []);\n\n const removeCustomDiscoveryUrl = useCallback((url: string) => {\n setCustomDiscoveryUrls((current: readonly string[]) =>\n current.filter((entry: string) => entry !== url),\n );\n }, []);\n\n // Submit\n const [adding, setAdding] = useState(false);\n const [addError, setAddError] = useState<string | null>(null);\n\n const doPreview = useAtomSet(previewOpenApiSpec, { mode: \"promiseExit\" });\n const doAdd = useAtomSet(addOpenApiSpec, { mode: \"promiseExit\" });\n\n // Keep the latest handleAnalyze in a ref so the debounced effect doesn't need\n // it as a dependency (it closes over fresh state).\n const handleAnalyzeRef = useRef<() => void>(() => {});\n\n useEffect(() => {\n // The bundle preset never analyzes a single spec — its input is the picker.\n if (isGoogleBundlePreset) return;\n const trimmed = specUrl.trim();\n if (!trimmed) return;\n if (preview) return;\n const handle = setTimeout(() => {\n handleAnalyzeRef.current();\n }, 400);\n return () => clearTimeout(handle);\n }, [specUrl, preview, isGoogleBundlePreset]);\n\n // ---- Derived state ----\n\n const servers: readonly ServerInfo[] = preview?.servers ?? [];\n const baseUrlOptions = Array.from(\n new Map(servers.flatMap(expandServerOptions).map((option) => [option.value, option])).values(),\n );\n const previewPresetIcon =\n openApiPresets.find(\n (preset) => preset.url && normalizePresetUrl(preset.url) === normalizePresetUrl(specUrl),\n )?.icon ?? null;\n\n const resolvedBaseUrl = baseUrl.trim();\n const resolvedSourceId =\n slugifyNamespace(identity.namespace) ||\n (preview ? Option.getOrElse(preview.title, () => \"openapi\") : \"openapi\");\n const resolvedDisplayName =\n identity.name.trim() ||\n (preview ? Option.getOrElse(preview.title, () => resolvedSourceId) : resolvedSourceId);\n\n // Register EVERY spec-detected auth method, not just a single selected one.\n // Keyed off `preview` (stable per analysis) so the memo doesn't re-run on the\n // freshly-allocated `?? []` fallback arrays.\n const authenticationTemplate: readonly Authentication[] = useMemo(\n () =>\n detectedAuthenticationTemplates(\n preview?.headerPresets ?? [],\n preview?.oauth2Presets ?? [],\n resolvedBaseUrl,\n ),\n [preview, resolvedBaseUrl],\n );\n\n const detectedMethodLabels: readonly string[] = useMemo(\n () => [\n ...(preview?.headerPresets ?? []).map((preset) => preset.label),\n ...(preview?.oauth2Presets ?? []).map((preset) => preset.label),\n ],\n [preview],\n );\n\n // Editable auth methods, seeded from the spec-detected templates. The add flow\n // registers EVERY method (P6) — so this is a LIST, preserving multi-method\n // specs (e.g. apiKey + OAuth). Each row carries its editor value plus the\n // detected template's original `seedSlug`, so an unedited detected method\n // submits with its EXACT original slug (preserving behavior); added methods\n // (no seed) get a deterministic fresh slug. The user can edit, add, or remove\n // a method before submitting; on submit the list converts back to\n // `Authentication[]`. Re-seeded whenever a fresh detection arrives (keyed on\n // the detected templates, which are stable per analysis + base URL).\n type AuthMethodRow = {\n readonly value: AuthTemplateEditorValue;\n readonly seedSlug?: string;\n };\n const [authMethods, setAuthMethods] = useState<readonly AuthMethodRow[]>([]);\n const seededFromRef = useRef<readonly Authentication[] | null>(null);\n useEffect(() => {\n if (seededFromRef.current === authenticationTemplate) return;\n seededFromRef.current = authenticationTemplate;\n setAuthMethods(\n authenticationTemplate.map((template: Authentication) => ({\n value: editorValueFromAuthentication(template),\n seedSlug: String(template.slug),\n })),\n );\n }, [authenticationTemplate]);\n\n const setAuthMethodAt = useCallback((index: number, next: AuthTemplateEditorValue) => {\n setAuthMethods((current: readonly AuthMethodRow[]) =>\n current.map((row: AuthMethodRow, i: number) => (i === index ? { ...row, value: next } : row)),\n );\n }, []);\n\n const removeAuthMethodAt = useCallback((index: number) => {\n setAuthMethods((current: readonly AuthMethodRow[]) =>\n current.filter((_row: AuthMethodRow, i: number) => i !== index),\n );\n }, []);\n\n const addAuthMethod = useCallback(() => {\n setAuthMethods((current: readonly AuthMethodRow[]) => [\n ...current,\n {\n value: {\n kind: \"apikey\",\n placements: [{ carrier: \"header\", name: \"Authorization\", prefix: \"\" }],\n },\n },\n ]);\n }, []);\n\n // The methods to register, mapped back to stored `Authentication[]`. Drops\n // `none` rows (nothing to register). An unedited detected method keeps its\n // original `seedSlug`; an added method gets a deterministic fresh slug.\n const editedAuthenticationTemplate: readonly Authentication[] = useMemo(() => {\n const templates: Authentication[] = [];\n authMethods.forEach((row: AuthMethodRow, index: number) => {\n const slug =\n row.seedSlug ?? (row.value.kind === \"oauth\" ? `oauth-${index}` : `apikey-${index}`);\n const template = authenticationFromEditorValue(row.value, slug);\n if (template !== null) templates.push(template);\n });\n return templates;\n }, [authMethods]);\n\n // Pre-empt the API's `IntegrationAlreadyExistsError`: adding an integration\n // whose slug already exists clobbers the existing one's connections/policies,\n // so the API blocks it. Surface that here from the tenant-scoped catalog list.\n const integrationsResult = useAtomValue(integrationsOptimisticAtom);\n const slugAlreadyExists = useMemo(\n () =>\n AsyncResult.isSuccess(integrationsResult) &&\n integrationsResult.value.some((integration) => integration.slug === resolvedSourceId),\n [integrationsResult, resolvedSourceId],\n );\n\n // The bundle path is ready once at least one Google API is selected (no\n // network preview gates it); the single/custom-spec path still requires a\n // successful preview. Both require a base URL and a free slug.\n const hasPreviewOrBundle = isGoogleBundlePreset\n ? bundleDiscoveryUrls.length > 0\n : preview !== null;\n const canAdd = hasPreviewOrBundle && resolvedBaseUrl.length > 0 && !slugAlreadyExists;\n\n // ---- Handlers ----\n\n const handleAnalyze = async () => {\n setAnalyzing(true);\n setAnalyzeError(null);\n setAddError(null);\n const exit = await doPreview({ payload: { spec: specUrl } });\n if (Exit.isFailure(exit)) {\n setAnalyzeError(errorMessageFromExit(exit, \"Failed to parse spec\"));\n setAnalyzing(false);\n return;\n }\n const result = exit.value;\n setPreview(result);\n setBaseUrl(firstBaseUrlForPreview(result));\n setAnalyzing(false);\n };\n\n handleAnalyzeRef.current = handleAnalyze;\n\n // Persist the integration and return its slug. Registers the catalog entry\n // with every detected auth method. Adding a slug that already exists is\n // rejected by the API (`IntegrationAlreadyExistsError`) — surfaced inline.\n const ensureIntegration = useCallback(async (): Promise<IntegrationSlug | null> => {\n // The Google BUNDLE preset emits the multi-service bundle input; the server\n // merges the selected Discovery documents into one `google` spec and stores\n // the unioned `googleOAuth2` auth template (so no client template is sent).\n // Every other preset/custom input keeps the single-spec url/blob/discovery\n // branch unchanged.\n const specForAdd = isGoogleBundlePreset\n ? ({ kind: \"googleDiscoveryBundle\" as const, urls: [...bundleDiscoveryUrls] } satisfies {\n readonly kind: \"googleDiscoveryBundle\";\n readonly urls: readonly string[];\n })\n : specInputForAdd(specUrl);\n const exit = await doAdd({\n payload: {\n spec: specForAdd,\n slug: resolvedSourceId,\n description: resolvedDisplayName,\n baseUrl: resolvedBaseUrl,\n ...(!isGoogleBundlePreset && editedAuthenticationTemplate.length > 0\n ? {\n authenticationTemplate: editedAuthenticationTemplate.map((entry) => ({\n ...entry,\n slug: String(entry.slug),\n })),\n }\n : {}),\n },\n reactivityKeys: integrationWriteKeys,\n });\n if (Exit.isFailure(exit)) {\n setAddError(\n isIntegrationAlreadyExistsExit(exit)\n ? integrationExistsMessage(resolvedSourceId)\n : errorMessageFromExit(exit, \"Failed to add integration\"),\n );\n return null;\n }\n return exit.value.slug;\n }, [\n isGoogleBundlePreset,\n bundleDiscoveryUrls,\n specUrl,\n doAdd,\n resolvedSourceId,\n resolvedDisplayName,\n resolvedBaseUrl,\n editedAuthenticationTemplate,\n ]);\n\n const handleAdd = async () => {\n setAdding(true);\n setAddError(null);\n\n const integration = await ensureIntegration();\n if (!integration) {\n setAdding(false);\n return;\n }\n\n props.onComplete(String(integration));\n };\n\n // ---- Render ----\n\n return (\n <div className=\"flex flex-1 flex-col gap-6\">\n <div>\n <h1 className=\"text-xl font-semibold text-foreground\">\n {isGoogleBundlePreset ? \"Add Google\" : \"Add OpenAPI Integration\"}\n </h1>\n {isGoogleBundlePreset ? (\n <p className=\"mt-1 text-[13px] text-muted-foreground\">\n Bundle Google APIs into one integration from their Discovery documents and register\n their methods as tools under a single shared OAuth consent.\n </p>\n ) : null}\n </div>\n\n {isGoogleBundlePreset ? (\n <GoogleProductPicker\n selectedPresetIds={selectedPresetIds}\n onToggle={toggleBundlePreset}\n customUrls={customDiscoveryUrls}\n onAddCustomUrl={addCustomDiscoveryUrl}\n onRemoveCustomUrl={removeCustomDiscoveryUrl}\n />\n ) : !preview ? (\n <CardStack>\n <CardStackContent className=\"border-t-0\">\n <div className=\"space-y-2 p-3\">\n <FieldLabel>OpenAPI Spec</FieldLabel>\n <div className=\"relative\">\n <Textarea\n value={specUrl}\n onChange={(e) => setSpecUrl((e.target as HTMLTextAreaElement).value)}\n placeholder=\"https://api.example.com/openapi.json\"\n rows={3}\n maxRows={10}\n className=\"font-mono text-sm\"\n />\n {analyzing && (\n <div className=\"pointer-events-none absolute right-2 top-2\">\n <IOSSpinner className=\"size-4\" />\n </div>\n )}\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n Paste a URL or raw JSON/YAML content.\n </p>\n </div>\n </CardStackContent>\n </CardStack>\n ) : null}\n\n {isGoogleBundlePreset ? (\n <OpenApiSourceDetailsFields\n title=\"Google\"\n description={`${bundleDiscoveryUrls.length} Google API${\n bundleDiscoveryUrls.length !== 1 ? \"s\" : \"\"\n } · one shared OAuth consent`}\n identity={identity}\n baseUrl={resolvedBaseUrl}\n onBaseUrlChange={setBaseUrl}\n faviconIcon={GOOGLE_BUNDLE_FAVICON}\n faviconUrl={resolvedBaseUrl}\n baseUrlMissingMessage=\"A base URL is required to make requests.\"\n />\n ) : preview ? (\n <OpenApiSourceDetailsFields\n title={Option.getOrElse(preview.title, () => \"API\")}\n description={`${Option.getOrElse(preview.version, () => \"\")}${\n Option.isSome(preview.version) ? \" · \" : \"\"\n }${preview.operationCount} operation${preview.operationCount !== 1 ? \"s\" : \"\"}${\n preview.tags.length > 0\n ? ` · ${preview.tags.length} tag${preview.tags.length !== 1 ? \"s\" : \"\"}`\n : \"\"\n }`}\n identity={identity}\n baseUrl={resolvedBaseUrl}\n onBaseUrlChange={setBaseUrl}\n baseUrlOptions={baseUrlOptions}\n specUrl={specUrl}\n onSpecUrlChange={(value) => {\n setSpecUrl(value);\n setPreview(null);\n setBaseUrl(\"\");\n }}\n faviconIcon={previewPresetIcon}\n faviconUrl={resolvedBaseUrl}\n baseUrlMissingMessage=\"A base URL is required to make requests.\"\n />\n ) : null}\n\n {analyzeError && (\n <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2\">\n <p className=\"text-[12px] text-destructive\">{analyzeError}</p>\n </div>\n )}\n\n {preview && !isGoogleBundlePreset && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <FieldLabel>How does this API authenticate?</FieldLabel>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addAuthMethod}>\n <PlusIcon />\n Add method\n </Button>\n </div>\n {authMethods.length === 0 ? (\n <p className=\"text-[11px] text-muted-foreground\">\n No authentication detected. Add a method, or add the integration without auth and\n connect an account from the integration page later.\n </p>\n ) : (\n <div className=\"flex flex-col gap-3\">\n {authMethods.map((row: AuthMethodRow, index: number) => (\n <div\n key={index}\n className=\"space-y-2 rounded-lg border border-border/60 bg-muted/20 p-3\"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Method {index + 1}\n {detectedMethodLabels[index] ? ` · ${detectedMethodLabels[index]}` : \"\"}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label=\"Remove method\"\n className=\"text-muted-foreground hover:text-foreground\"\n onClick={() => removeAuthMethodAt(index)}\n >\n <XIcon />\n </Button>\n </div>\n <AuthTemplateEditor\n value={row.value}\n onChange={(next: AuthTemplateEditorValue) => setAuthMethodAt(index, next)}\n />\n </div>\n ))}\n </div>\n )}\n <p className=\"text-[11px] text-muted-foreground\">\n Every method here is registered with the integration. Connect an account from the\n integration page after adding.\n </p>\n </section>\n )}\n\n {hasPreviewOrBundle && slugAlreadyExists && !adding && (\n <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2\">\n <p className=\"text-[12px] text-destructive\">\n An integration named "{resolvedSourceId}" already exists. To add more\n authentication, update your existing integration.{\" \"}\n <Link\n to=\"/integrations/$namespace\"\n params={{ namespace: resolvedSourceId }}\n className=\"font-medium underline underline-offset-2\"\n >\n Open it\n </Link>\n </p>\n </div>\n )}\n\n {addError && (\n <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2\">\n <p className=\"text-[12px] text-destructive\">{addError}</p>\n </div>\n )}\n\n <FloatActions>\n <Button variant=\"ghost\" onClick={() => props.onCancel()} disabled={adding}>\n Cancel\n </Button>\n {(hasPreviewOrBundle || isGoogleBundlePreset) && (\n <Button onClick={() => void handleAdd()} disabled={!canAdd || adding}>\n {adding && <Spinner className=\"size-3.5\" />}\n {adding ? \"Adding…\" : isGoogleBundlePreset ? \"Connect Google\" : \"Add integration\"}\n </Button>\n )}\n </FloatActions>\n </div>\n );\n}\n","import {\n CardStack,\n CardStackContent,\n CardStackEntry,\n CardStackEntryContent,\n CardStackEntryDescription,\n CardStackEntryField,\n CardStackEntryTitle,\n} from \"@executor-js/react/components/card-stack\";\nimport {\n FreeformCombobox,\n type FreeformComboboxOption,\n} from \"@executor-js/react/components/combobox\";\nimport { Input } from \"@executor-js/react/components/input\";\nimport { IntegrationFavicon } from \"@executor-js/react/components/integration-favicon\";\nimport {\n IntegrationIdentityFieldRows,\n type IntegrationIdentity,\n} from \"@executor-js/react/plugins/integration-identity\";\n\nexport function OpenApiSourceDetailsFields(props: {\n readonly title: string;\n readonly description?: string;\n readonly identity: IntegrationIdentity;\n readonly baseUrl: string;\n readonly onBaseUrlChange: (value: string) => void;\n readonly baseUrlOptions?: readonly FreeformComboboxOption[];\n readonly specUrl?: string;\n readonly onSpecUrlChange?: (value: string) => void;\n readonly faviconIcon?: string | null;\n readonly faviconUrl?: string;\n readonly namespaceReadOnly?: boolean;\n readonly specUrlDisabled?: boolean;\n readonly saveState?: \"idle\" | \"saving\" | \"saved\";\n readonly baseUrlMissingMessage?: string;\n readonly footer?: string;\n}) {\n const baseUrlOptions = props.baseUrlOptions ?? [];\n\n return (\n <CardStack>\n <CardStackContent className=\"border-t-0\">\n <CardStackEntry>\n {(props.faviconIcon || props.faviconUrl) && (\n <IntegrationFavicon icon={props.faviconIcon} url={props.faviconUrl} size={16} />\n )}\n <CardStackEntryContent>\n <CardStackEntryTitle>{props.title}</CardStackEntryTitle>\n {props.description && (\n <CardStackEntryDescription>{props.description}</CardStackEntryDescription>\n )}\n </CardStackEntryContent>\n {props.saveState && props.saveState !== \"idle\" && (\n <span className=\"text-xs text-muted-foreground\">\n {props.saveState === \"saving\" ? \"Saving…\" : \"Saved\"}\n </span>\n )}\n </CardStackEntry>\n <IntegrationIdentityFieldRows\n identity={props.identity}\n namespaceReadOnly={props.namespaceReadOnly}\n />\n <div className=\"grid grid-cols-1 md:grid-cols-2\">\n <CardStackEntryField label=\"Base URL\">\n {baseUrlOptions.length > 0 ? (\n <FreeformCombobox\n value={props.baseUrl}\n onValueChange={props.onBaseUrlChange}\n options={baseUrlOptions}\n placeholder=\"https://api.example.com\"\n className=\"w-full\"\n inputClassName=\"font-mono text-sm\"\n />\n ) : (\n <Input\n value={props.baseUrl}\n onChange={(e) => props.onBaseUrlChange((e.target as HTMLInputElement).value)}\n placeholder=\"https://api.example.com\"\n className=\"font-mono text-sm\"\n />\n )}\n\n {props.baseUrlMissingMessage && !props.baseUrl && (\n <p className=\"text-[11px] text-amber-600 dark:text-amber-400\">\n {props.baseUrlMissingMessage}\n </p>\n )}\n </CardStackEntryField>\n {props.specUrl !== undefined && props.onSpecUrlChange && (\n <CardStackEntryField label=\"Spec URL\">\n <Input\n value={props.specUrl}\n onChange={(e) => props.onSpecUrlChange?.((e.target as HTMLInputElement).value)}\n placeholder=\"https://api.example.com/openapi.json\"\n className=\"font-mono text-sm\"\n disabled={props.specUrlDisabled}\n />\n </CardStackEntryField>\n )}\n </div>\n {props.footer && (\n <CardStackEntry>\n <CardStackEntryContent>\n <CardStackEntryTitle>{props.footer}</CardStackEntryTitle>\n </CardStackEntryContent>\n </CardStackEntry>\n )}\n </CardStackContent>\n </CardStack>\n );\n}\n","import { useMemo, useState } from \"react\";\nimport { ChevronDownIcon, PlusIcon, TriangleAlert, XIcon } from \"lucide-react\";\n\nimport { cn } from \"@executor-js/react/lib/utils\";\nimport { Badge } from \"@executor-js/react/components/badge\";\nimport { Button } from \"@executor-js/react/components/button\";\nimport { Checkbox } from \"@executor-js/react/components/checkbox\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"@executor-js/react/components/collapsible\";\nimport { FieldLabel } from \"@executor-js/react/components/field\";\nimport { Input } from \"@executor-js/react/components/input\";\nimport { IntegrationFavicon } from \"@executor-js/react/components/integration-favicon\";\n\nimport {\n googleOAuthConsentScopesForPreset,\n googleOpenApiPresets,\n type GoogleOpenApiOAuthAudience,\n type GoogleOpenApiPreset,\n} from \"../sdk/google-presets\";\nimport { googleOAuthConsentBatches } from \"../sdk/google-oauth-batches\";\nimport { isGoogleDiscoveryUrl } from \"../sdk/google-discovery\";\n\n// ---------------------------------------------------------------------------\n// GoogleProductPicker — the \"customize your Google connection\" surface.\n//\n// A checkable card grid over `googleOpenApiPresets`, grouped/annotated by\n// `oauthAudience`. The user picks which Google APIs to bundle into the single\n// `google` integration; the parent turns the selected discovery URLs into a\n// `{ kind: \"googleDiscoveryBundle\", urls }` add. A \"View scopes\" panel previews\n// the unioned OAuth consent (via `googleOAuthConsentBatches`) BEFORE connecting,\n// and a custom-URL escape hatch lets advanced users paste any Google Discovery\n// document the preset list doesn't cover.\n// ---------------------------------------------------------------------------\n\n// Audience groups, ordered from least- to most-privileged. The warning tiers\n// (`workspace-admin`, `unsupported-user`) carry a caution chip so the user sees\n// the consent risk before selecting.\nconst AUDIENCE_ORDER: readonly GoogleOpenApiOAuthAudience[] = [\n \"standard-user\",\n \"advanced-user\",\n \"workspace-admin\",\n \"unsupported-user\",\n];\n\nconst AUDIENCE_LABEL: Readonly<Record<GoogleOpenApiOAuthAudience, string>> = {\n \"standard-user\": \"Core Google services\",\n \"advanced-user\": \"Advanced services\",\n \"workspace-admin\": \"Workspace admin\",\n \"unsupported-user\": \"Limited user consent\",\n};\n\nconst AUDIENCE_DESCRIPTION: Readonly<Record<GoogleOpenApiOAuthAudience, string>> = {\n \"standard-user\": \"Connect with a normal Google account — one consent screen.\",\n \"advanced-user\": \"Broader scopes that may need an unverified-app warning to be accepted.\",\n \"workspace-admin\": \"Requires a Google Workspace admin account; not available on personal Gmail.\",\n \"unsupported-user\": \"Google does not grant these scopes through standard user OAuth consent.\",\n};\n\nconst audienceNeedsWarning = (audience: GoogleOpenApiOAuthAudience): boolean =>\n audience === \"workspace-admin\" || audience === \"unsupported-user\";\n\ntype GoogleProductPickerProps = {\n readonly selectedPresetIds: ReadonlySet<string>;\n readonly onToggle: (presetId: string, checked: boolean) => void;\n readonly customUrls: readonly string[];\n readonly onAddCustomUrl: (url: string) => void;\n readonly onRemoveCustomUrl: (url: string) => void;\n};\n\nconst AudienceWarningChip = ({ audience }: { audience: GoogleOpenApiOAuthAudience }) =>\n audience === \"workspace-admin\" ? (\n <Badge\n variant=\"outline\"\n className=\"shrink-0 border-amber-500/40 text-amber-700 dark:text-amber-400\"\n >\n <TriangleAlert className=\"size-3\" />\n Admin only\n </Badge>\n ) : audience === \"unsupported-user\" ? (\n <Badge variant=\"outline\" className=\"shrink-0 border-destructive/40 text-destructive\">\n <TriangleAlert className=\"size-3\" />\n Limited consent\n </Badge>\n ) : null;\n\n// A Google API row — borderless and single-line, leaning on hover/selected\n// fills instead of per-item card chrome (lightest separation that still reads).\n// Name + truncated summary share one baseline for a dense, scannable two-column\n// list; the audience warning chip trails on the right.\nconst ProductRow = ({\n preset,\n checked,\n onToggle,\n}: {\n readonly preset: GoogleOpenApiPreset;\n readonly checked: boolean;\n readonly onToggle: (checked: boolean) => void;\n}) => (\n <FieldLabel\n className={cn(\n // `w-full` overrides FieldLabel's base `w-fit` (which would size the row to\n // its content and overflow the column); `min-w-0` then lets the cell shrink\n // to its track so the name/summary truncates instead of spilling over.\n \"flex w-full min-w-0 cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 transition-colors\",\n checked ? \"bg-primary/5\" : \"hover:bg-muted/40\",\n )}\n >\n <Checkbox checked={checked} onCheckedChange={(next) => onToggle(next === true)} />\n <div className=\"shrink-0\">\n <IntegrationFavicon icon={preset.icon} url={preset.url} size={16} />\n </div>\n {/* One truncating line — the name + summary clip to the cell with an\n ellipsis instead of overflowing into the neighbouring column. */}\n <div className=\"min-w-0 flex-1 truncate text-sm\">\n <span className=\"font-medium text-foreground\">{preset.name}</span>{\" \"}\n <span className=\"text-[11px] text-muted-foreground\">{preset.summary}</span>\n </div>\n <AudienceWarningChip audience={preset.oauthAudience} />\n </FieldLabel>\n);\n\nconst CustomUrlEscapeHatch = ({\n customUrls,\n onAddCustomUrl,\n onRemoveCustomUrl,\n}: {\n readonly customUrls: readonly string[];\n readonly onAddCustomUrl: (url: string) => void;\n readonly onRemoveCustomUrl: (url: string) => void;\n}) => {\n const [draft, setDraft] = useState(\"\");\n const trimmed = draft.trim();\n const isValid = isGoogleDiscoveryUrl(trimmed);\n const isDuplicate = customUrls.includes(trimmed);\n\n const commit = () => {\n if (!isValid || isDuplicate) return;\n onAddCustomUrl(trimmed);\n setDraft(\"\");\n };\n\n return (\n <div className=\"space-y-2\">\n <FieldLabel className=\"text-[11px] font-medium text-muted-foreground\">\n Add a custom Google Discovery URL\n </FieldLabel>\n <div className=\"flex items-center gap-2\">\n <Input\n value={draft}\n onChange={(event: React.ChangeEvent<HTMLInputElement>) => setDraft(event.target.value)}\n onKeyDown={(event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n commit();\n }\n }}\n placeholder=\"https://www.googleapis.com/discovery/v1/apis/<service>/<version>/rest\"\n className=\"font-mono text-[11px]\"\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={!isValid || isDuplicate}\n onClick={commit}\n >\n <PlusIcon className=\"size-3.5\" />\n Add\n </Button>\n </div>\n {trimmed.length > 0 && !isValid ? (\n <p className=\"text-[11px] text-destructive\">\n Enter a Google Discovery document URL (a *.googleapis.com discovery/$discovery endpoint).\n </p>\n ) : null}\n {customUrls.length > 0 ? (\n <ul className=\"space-y-1\">\n {customUrls.map((url: string) => (\n <li\n key={url}\n className=\"flex items-center justify-between gap-2 rounded-md border border-border bg-muted/20 px-2.5 py-1.5\"\n >\n <span className=\"truncate font-mono text-[11px] text-foreground\">{url}</span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-6 shrink-0\"\n onClick={() => onRemoveCustomUrl(url)}\n aria-label={`Remove ${url}`}\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n};\n\nexport function GoogleProductPicker({\n selectedPresetIds,\n onToggle,\n customUrls,\n onAddCustomUrl,\n onRemoveCustomUrl,\n}: GoogleProductPickerProps) {\n const [scopesOpen, setScopesOpen] = useState(false);\n\n const groups = useMemo(\n () =>\n AUDIENCE_ORDER.flatMap((audience: GoogleOpenApiOAuthAudience) => {\n const presets = googleOpenApiPresets.filter(\n (preset: GoogleOpenApiPreset) => preset.oauthAudience === audience,\n );\n return presets.length > 0 ? [{ audience, presets }] : [];\n }),\n [],\n );\n\n // The \"View scopes\" preview unions the selected presets' representative\n // consent scopes through `googleOAuthConsentBatches`, mirroring the unioned\n // scopes the bundle converter ultimately stores on the integration.\n const consentBatches = useMemo(\n () =>\n googleOAuthConsentBatches(\n googleOpenApiPresets\n .filter((preset: GoogleOpenApiPreset) => selectedPresetIds.has(preset.id))\n .map((preset: GoogleOpenApiPreset) => ({\n id: preset.id,\n name: preset.name,\n oauthAudience: preset.oauthAudience,\n scopes: googleOAuthConsentScopesForPreset(preset.id),\n })),\n ),\n [selectedPresetIds],\n );\n\n const selectedCount = selectedPresetIds.size + customUrls.length;\n\n return (\n <section className=\"space-y-4\">\n <div className=\"space-y-1\">\n <FieldLabel>Customize your Google connection</FieldLabel>\n <p className=\"text-[11px] text-muted-foreground\">\n Pick the Google APIs to bundle into one connection. They share a single OAuth consent and\n appear as merged tools under one Google integration.\n </p>\n </div>\n\n {groups.map(\n ({\n audience,\n presets,\n }: {\n readonly audience: GoogleOpenApiOAuthAudience;\n readonly presets: readonly GoogleOpenApiPreset[];\n }) => (\n <div key={audience} className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[11px] font-semibold tracking-wide text-foreground uppercase\">\n {AUDIENCE_LABEL[audience]}\n </span>\n {audienceNeedsWarning(audience) ? <AudienceWarningChip audience={audience} /> : null}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto h-auto px-1.5 py-0.5 text-[11px] font-normal text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={() => {\n const allSelected = presets.every((preset: GoogleOpenApiPreset) =>\n selectedPresetIds.has(preset.id),\n );\n presets.forEach((preset: GoogleOpenApiPreset) =>\n onToggle(preset.id, !allSelected),\n );\n }}\n >\n {presets.every((preset: GoogleOpenApiPreset) => selectedPresetIds.has(preset.id))\n ? \"Clear\"\n : \"Select all\"}\n </Button>\n </div>\n <p className=\"text-[11px] text-muted-foreground\">{AUDIENCE_DESCRIPTION[audience]}</p>\n <div className=\"grid grid-cols-1 gap-x-4 gap-y-0.5 sm:grid-cols-2\">\n {presets.map((preset: GoogleOpenApiPreset) => (\n <ProductRow\n key={preset.id}\n preset={preset}\n checked={selectedPresetIds.has(preset.id)}\n onToggle={(checked: boolean) => onToggle(preset.id, checked)}\n />\n ))}\n </div>\n </div>\n ),\n )}\n\n <CustomUrlEscapeHatch\n customUrls={customUrls}\n onAddCustomUrl={onAddCustomUrl}\n onRemoveCustomUrl={onRemoveCustomUrl}\n />\n\n <div className=\"space-y-1.5 rounded-lg border border-border bg-muted/10 px-3 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[11px] font-semibold tracking-wide text-foreground uppercase\">\n Authentication\n </span>\n <Badge variant=\"secondary\">OAuth</Badge>\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n The selected Google APIs share one OAuth consent. Review the scopes below, then connect a\n Google account from the integration page after adding.\n </p>\n </div>\n\n <Collapsible open={scopesOpen} onOpenChange={setScopesOpen}>\n <CollapsibleTrigger asChild>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" disabled={consentBatches.length === 0}>\n <ChevronDownIcon\n className={cn(\"size-3.5 transition-transform\", scopesOpen ? \"rotate-180\" : \"\")}\n />\n View scopes\n {selectedCount > 0 ? (\n <Badge variant=\"secondary\" className=\"ml-1\">\n {selectedCount}\n </Badge>\n ) : null}\n </Button>\n </CollapsibleTrigger>\n <CollapsibleContent className=\"pt-3\">\n {consentBatches.length === 0 ? (\n <p className=\"text-[11px] text-muted-foreground\">\n Select at least one Google API to preview the OAuth consent.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {consentBatches.map((batch) => (\n <div key={batch.id} className=\"space-y-1.5\">\n <span className=\"text-[11px] font-semibold text-foreground\">{batch.label}</span>\n <ul className=\"space-y-1\">\n {batch.apiScopes.map((scope: string) => (\n <li\n key={scope}\n className=\"rounded-md border border-border bg-muted/20 px-2.5 py-1 font-mono text-[11px] break-all text-muted-foreground\"\n >\n {scope}\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n )}\n </CollapsibleContent>\n </Collapsible>\n </section>\n );\n}\n\nexport default GoogleProductPicker;\n","import type { GoogleOpenApiOAuthAudience } from \"./google-presets\";\nimport { compactGoogleOAuthScopes } from \"./google-oauth-scopes\";\n\nexport type GoogleOAuthBatchInput = {\n readonly id: string;\n readonly name: string;\n readonly oauthAudience: GoogleOpenApiOAuthAudience;\n readonly scopes: readonly string[];\n};\n\nexport type GoogleOAuthConsentBatch = {\n readonly id: string;\n readonly label: string;\n readonly apiScopes: readonly string[];\n};\n\nconst GOOGLE_CLOUD_BATCH_IDS = new Set([\"google-bigquery\", \"google-cloud-resource-manager\"]);\n\nexport const googleOAuthConsentBatches = (\n items: readonly GoogleOAuthBatchInput[],\n): readonly GoogleOAuthConsentBatch[] => {\n const standardScopes: string[] = [];\n const cloudScopes: string[] = [];\n const batches: GoogleOAuthConsentBatch[] = [];\n\n for (const item of items) {\n if (item.scopes.length === 0) continue;\n if (item.oauthAudience === \"standard-user\") {\n standardScopes.push(...item.scopes);\n continue;\n }\n if (GOOGLE_CLOUD_BATCH_IDS.has(item.id)) {\n cloudScopes.push(...item.scopes);\n continue;\n }\n batches.push({\n id: item.id,\n label: item.name,\n apiScopes: item.scopes,\n });\n }\n\n const compactedStandardScopes = compactGoogleOAuthScopes(standardScopes);\n const compactedCloudScopes = compactGoogleOAuthScopes(cloudScopes);\n return [\n ...(compactedStandardScopes.length > 0\n ? [\n {\n id: \"google-core\",\n label: \"Core Google services\",\n apiScopes: compactedStandardScopes,\n },\n ]\n : []),\n ...batches.map((batch) => ({\n ...batch,\n apiScopes: compactGoogleOAuthScopes(batch.apiScopes),\n })),\n ...(compactedCloudScopes.length > 0\n ? [\n {\n id: \"google-cloud\",\n label: \"Google Cloud services\",\n apiScopes: compactedCloudScopes,\n },\n ]\n : []),\n ].filter((batch) => batch.apiScopes.length > 0);\n};\n"],"mappings":"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","names":["useMemo","useState","Button","CardStack","CardStackContent","FieldLabel","PlusIcon","XIcon","Input","IntegrationFavicon","jsx","jsxs","IntegrationFavicon","Input","jsx","jsxs","isGoogleDiscoveryUrl","useState","useMemo","CardStack","CardStackContent","FieldLabel","Button","PlusIcon","XIcon"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
openApiIntegrationAtom
|
|
3
|
+
} from "./chunk-BSLE6HCE.js";
|
|
4
|
+
import "./chunk-YVRI7KRC.js";
|
|
5
|
+
|
|
6
|
+
// src/react/EditOpenApiSource.tsx
|
|
7
|
+
import { useAtomValue } from "@effect/atom-react";
|
|
8
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
9
|
+
import { connectionsAllAtom } from "@executor-js/react/api/atoms";
|
|
10
|
+
import { ownerLabel, useOwnerDisplay } from "@executor-js/react/api/owner-display";
|
|
11
|
+
import { IntegrationSlug } from "@executor-js/sdk/shared";
|
|
12
|
+
import { Badge } from "@executor-js/react/components/badge";
|
|
13
|
+
import { Button } from "@executor-js/react/components/button";
|
|
14
|
+
import {
|
|
15
|
+
CardStack,
|
|
16
|
+
CardStackContent,
|
|
17
|
+
CardStackEntry,
|
|
18
|
+
CardStackEntryContent,
|
|
19
|
+
CardStackEntryDescription,
|
|
20
|
+
CardStackEntryTitle
|
|
21
|
+
} from "@executor-js/react/components/card-stack";
|
|
22
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
23
|
+
function EditOpenApiSource(props) {
|
|
24
|
+
const slug = IntegrationSlug.make(props.sourceId);
|
|
25
|
+
const integrationResult = useAtomValue(openApiIntegrationAtom(slug));
|
|
26
|
+
const ownerDisplay = useOwnerDisplay();
|
|
27
|
+
const connectionsResult = useAtomValue(connectionsAllAtom);
|
|
28
|
+
const integration = AsyncResult.isSuccess(integrationResult) && integrationResult.value ? integrationResult.value : null;
|
|
29
|
+
const connections = AsyncResult.isSuccess(connectionsResult) ? connectionsResult.value.filter((candidate) => candidate.integration === slug) : [];
|
|
30
|
+
if (!integration) {
|
|
31
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
32
|
+
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-foreground", children: "OpenAPI Integration" }),
|
|
33
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading\u2026" })
|
|
34
|
+
] });
|
|
35
|
+
}
|
|
36
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
37
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-foreground", children: "OpenAPI Integration" }) }),
|
|
38
|
+
/* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsx(CardStackContent, { className: "border-t-0", children: /* @__PURE__ */ jsxs(CardStackEntry, { children: [
|
|
39
|
+
/* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
|
|
40
|
+
/* @__PURE__ */ jsx(CardStackEntryTitle, { children: integration.description || String(slug) }),
|
|
41
|
+
/* @__PURE__ */ jsx(CardStackEntryDescription, { children: String(slug) })
|
|
42
|
+
] }),
|
|
43
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", children: integration.kind })
|
|
44
|
+
] }) }) }),
|
|
45
|
+
/* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsxs(CardStackContent, { className: "border-t-0", children: [
|
|
46
|
+
/* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
|
|
47
|
+
/* @__PURE__ */ jsx(CardStackEntryTitle, { children: "Connections" }),
|
|
48
|
+
/* @__PURE__ */ jsx(CardStackEntryDescription, { children: "Credentials this integration uses to call its API." })
|
|
49
|
+
] }) }),
|
|
50
|
+
connections.length === 0 ? /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsx(CardStackEntryContent, { children: /* @__PURE__ */ jsx(CardStackEntryDescription, { children: "No connections yet." }) }) }) : connections.map((connection) => /* @__PURE__ */ jsxs(CardStackEntry, { children: [
|
|
51
|
+
/* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
|
|
52
|
+
/* @__PURE__ */ jsx(CardStackEntryTitle, { children: connection.identityLabel ?? String(connection.name) }),
|
|
53
|
+
/* @__PURE__ */ jsxs(CardStackEntryDescription, { children: [
|
|
54
|
+
String(connection.template),
|
|
55
|
+
" \xB7 ",
|
|
56
|
+
String(connection.provider)
|
|
57
|
+
] })
|
|
58
|
+
] }),
|
|
59
|
+
ownerDisplay.showOwnerLabels ? /* @__PURE__ */ jsx(Badge, { variant: "outline", children: ownerLabel(connection.owner) }) : null
|
|
60
|
+
] }, `${connection.owner}:${connection.name}`))
|
|
61
|
+
] }) }),
|
|
62
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-start border-t border-border pt-4", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: props.onSave, children: "Back" }) })
|
|
63
|
+
] });
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
EditOpenApiSource as default
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=EditOpenApiSource-WTAMRJUK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/EditOpenApiSource.tsx"],"sourcesContent":["import { useAtomValue } from \"@effect/atom-react\";\nimport * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\n\nimport { connectionsAllAtom } from \"@executor-js/react/api/atoms\";\nimport { ownerLabel, useOwnerDisplay } from \"@executor-js/react/api/owner-display\";\nimport { IntegrationSlug, type Connection } from \"@executor-js/sdk/shared\";\nimport { Badge } from \"@executor-js/react/components/badge\";\nimport { Button } from \"@executor-js/react/components/button\";\nimport {\n CardStack,\n CardStackContent,\n CardStackEntry,\n CardStackEntryContent,\n CardStackEntryDescription,\n CardStackEntryTitle,\n} from \"@executor-js/react/components/card-stack\";\n\nimport { openApiIntegrationAtom } from \"./atoms\";\n\n// ---------------------------------------------------------------------------\n// v2 edit — the integration's spec, base URL, and auth templates are part of\n// its catalog config, which core treats as opaque and the API exposes as a\n// read-only `getIntegration`. There are no per-scope source rows, no credential\n// slots, and no binding writers to configure here anymore. Editing is reduced\n// to surfacing the integration's identity and its current connections; new\n// credentials are created on the integration's detail/connections surface.\n// ---------------------------------------------------------------------------\n\nexport default function EditOpenApiSource(props: {\n readonly sourceId: string;\n readonly onSave: () => void;\n}) {\n const slug = IntegrationSlug.make(props.sourceId);\n const integrationResult = useAtomValue(openApiIntegrationAtom(slug));\n const ownerDisplay = useOwnerDisplay();\n // Connections across BOTH owners (omit-owner read); each row keeps its owner\n // for the per-connection badge below.\n const connectionsResult = useAtomValue(connectionsAllAtom);\n\n const integration =\n AsyncResult.isSuccess(integrationResult) && integrationResult.value\n ? integrationResult.value\n : null;\n const connections: readonly Connection[] = AsyncResult.isSuccess(connectionsResult)\n ? connectionsResult.value.filter((candidate: Connection) => candidate.integration === slug)\n : [];\n\n if (!integration) {\n return (\n <div className=\"space-y-3\">\n <h1 className=\"text-xl font-semibold text-foreground\">OpenAPI Integration</h1>\n <p className=\"text-sm text-muted-foreground\">Loading…</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n <div>\n <h1 className=\"text-xl font-semibold text-foreground\">OpenAPI Integration</h1>\n </div>\n\n <CardStack>\n <CardStackContent className=\"border-t-0\">\n <CardStackEntry>\n <CardStackEntryContent>\n <CardStackEntryTitle>{integration.description || String(slug)}</CardStackEntryTitle>\n <CardStackEntryDescription>{String(slug)}</CardStackEntryDescription>\n </CardStackEntryContent>\n <Badge variant=\"secondary\">{integration.kind}</Badge>\n </CardStackEntry>\n </CardStackContent>\n </CardStack>\n\n <CardStack>\n <CardStackContent className=\"border-t-0\">\n <CardStackEntry>\n <CardStackEntryContent>\n <CardStackEntryTitle>Connections</CardStackEntryTitle>\n <CardStackEntryDescription>\n Credentials this integration uses to call its API.\n </CardStackEntryDescription>\n </CardStackEntryContent>\n </CardStackEntry>\n {connections.length === 0 ? (\n <CardStackEntry>\n <CardStackEntryContent>\n <CardStackEntryDescription>No connections yet.</CardStackEntryDescription>\n </CardStackEntryContent>\n </CardStackEntry>\n ) : (\n connections.map((connection: Connection) => (\n <CardStackEntry key={`${connection.owner}:${connection.name}`}>\n <CardStackEntryContent>\n <CardStackEntryTitle>\n {connection.identityLabel ?? String(connection.name)}\n </CardStackEntryTitle>\n <CardStackEntryDescription>\n {String(connection.template)} · {String(connection.provider)}\n </CardStackEntryDescription>\n </CardStackEntryContent>\n {ownerDisplay.showOwnerLabels ? (\n <Badge variant=\"outline\">{ownerLabel(connection.owner)}</Badge>\n ) : null}\n </CardStackEntry>\n ))\n )}\n </CardStackContent>\n </CardStack>\n\n <div className=\"flex items-center justify-start border-t border-border pt-4\">\n <Button variant=\"ghost\" onClick={props.onSave}>\n Back\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAAA,SAAS,oBAAoB;AAC7B,YAAY,iBAAiB;AAE7B,SAAS,0BAA0B;AACnC,SAAS,YAAY,uBAAuB;AAC5C,SAAS,uBAAwC;AACjD,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkCD,SACE,KADF;AArBS,SAAR,kBAAmC,OAGvC;AACD,QAAM,OAAO,gBAAgB,KAAK,MAAM,QAAQ;AAChD,QAAM,oBAAoB,aAAa,uBAAuB,IAAI,CAAC;AACnE,QAAM,eAAe,gBAAgB;AAGrC,QAAM,oBAAoB,aAAa,kBAAkB;AAEzD,QAAM,cACQ,sBAAU,iBAAiB,KAAK,kBAAkB,QAC1D,kBAAkB,QAClB;AACN,QAAM,cAAiD,sBAAU,iBAAiB,IAC9E,kBAAkB,MAAM,OAAO,CAAC,cAA0B,UAAU,gBAAgB,IAAI,IACxF,CAAC;AAEL,MAAI,CAAC,aAAa;AAChB,WACE,qBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,QAAG,WAAU,yCAAwC,iCAAmB;AAAA,MACzE,oBAAC,OAAE,WAAU,iCAAgC,2BAAQ;AAAA,OACvD;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,SACC,8BAAC,QAAG,WAAU,yCAAwC,iCAAmB,GAC3E;AAAA,IAEA,oBAAC,aACC,8BAAC,oBAAiB,WAAU,cAC1B,+BAAC,kBACC;AAAA,2BAAC,yBACC;AAAA,4BAAC,uBAAqB,sBAAY,eAAe,OAAO,IAAI,GAAE;AAAA,QAC9D,oBAAC,6BAA2B,iBAAO,IAAI,GAAE;AAAA,SAC3C;AAAA,MACA,oBAAC,SAAM,SAAQ,aAAa,sBAAY,MAAK;AAAA,OAC/C,GACF,GACF;AAAA,IAEA,oBAAC,aACC,+BAAC,oBAAiB,WAAU,cAC1B;AAAA,0BAAC,kBACC,+BAAC,yBACC;AAAA,4BAAC,uBAAoB,yBAAW;AAAA,QAChC,oBAAC,6BAA0B,gEAE3B;AAAA,SACF,GACF;AAAA,MACC,YAAY,WAAW,IACtB,oBAAC,kBACC,8BAAC,yBACC,8BAAC,6BAA0B,iCAAmB,GAChD,GACF,IAEA,YAAY,IAAI,CAAC,eACf,qBAAC,kBACC;AAAA,6BAAC,yBACC;AAAA,8BAAC,uBACE,qBAAW,iBAAiB,OAAO,WAAW,IAAI,GACrD;AAAA,UACA,qBAAC,6BACE;AAAA,mBAAO,WAAW,QAAQ;AAAA,YAAE;AAAA,YAAI,OAAO,WAAW,QAAQ;AAAA,aAC7D;AAAA,WACF;AAAA,QACC,aAAa,kBACZ,oBAAC,SAAM,SAAQ,WAAW,qBAAW,WAAW,KAAK,GAAE,IACrD;AAAA,WAXe,GAAG,WAAW,KAAK,IAAI,WAAW,IAAI,EAY3D,CACD;AAAA,OAEL,GACF;AAAA,IAEA,oBAAC,SAAI,WAAU,+DACb,8BAAC,UAAO,SAAQ,SAAQ,SAAS,MAAM,QAAQ,kBAE/C,GACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authMethodsFromConfig,
|
|
3
|
+
templateFromPlacements
|
|
4
|
+
} from "./chunk-QSSRVK6M.js";
|
|
5
|
+
import {
|
|
6
|
+
googleAudienceWarningsForUrls
|
|
7
|
+
} from "./chunk-MZWZQ24W.js";
|
|
8
|
+
import {
|
|
9
|
+
openApiConfigAtom,
|
|
10
|
+
openapiConfigure
|
|
11
|
+
} from "./chunk-BSLE6HCE.js";
|
|
12
|
+
import "./chunk-YVRI7KRC.js";
|
|
13
|
+
|
|
14
|
+
// src/react/OpenApiAccountsPanel.tsx
|
|
15
|
+
import { useCallback, useMemo } from "react";
|
|
16
|
+
import { useAtomValue, useAtomSet } from "@effect/atom-react";
|
|
17
|
+
import * as Exit from "effect/Exit";
|
|
18
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
19
|
+
import { AuthTemplateSlug, IntegrationSlug } from "@executor-js/sdk/shared";
|
|
20
|
+
import { TriangleAlert } from "lucide-react";
|
|
21
|
+
import { AccountsSection } from "@executor-js/react/components/accounts-section";
|
|
22
|
+
import { Alert, AlertDescription, AlertTitle } from "@executor-js/react/components/alert";
|
|
23
|
+
import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
|
|
24
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
25
|
+
var GOOGLE_AUDIENCE_WARNING = {
|
|
26
|
+
"workspace-admin": "This connection includes Google Workspace admin APIs (Chat, Admin Directory, Admin Reports). Connecting requires a Workspace admin account \u2014 personal Gmail accounts cannot grant these scopes.",
|
|
27
|
+
"unsupported-user": "This connection includes APIs (e.g. Google Keep) that Google does not grant through standard user OAuth consent. Those tools may fail to authorize."
|
|
28
|
+
};
|
|
29
|
+
var NO_AUTH_METHOD = {
|
|
30
|
+
id: "none",
|
|
31
|
+
label: "No authentication",
|
|
32
|
+
kind: "none",
|
|
33
|
+
source: "spec",
|
|
34
|
+
template: AuthTemplateSlug.make("none"),
|
|
35
|
+
placements: []
|
|
36
|
+
};
|
|
37
|
+
function OpenApiAccountsPanel(props) {
|
|
38
|
+
const { sourceId, integrationName, accountHandoff } = props;
|
|
39
|
+
const slug = IntegrationSlug.make(sourceId);
|
|
40
|
+
const configResult = useAtomValue(openApiConfigAtom(slug));
|
|
41
|
+
const doConfigure = useAtomSet(openapiConfigure, { mode: "promiseExit" });
|
|
42
|
+
const existingTemplate = useMemo(() => {
|
|
43
|
+
if (!AsyncResult.isSuccess(configResult) || configResult.value == null) return [];
|
|
44
|
+
return configResult.value.authenticationTemplate ?? [];
|
|
45
|
+
}, [configResult]);
|
|
46
|
+
const methods = useMemo(() => {
|
|
47
|
+
const declared = authMethodsFromConfig(existingTemplate);
|
|
48
|
+
return declared.length > 0 ? declared : [NO_AUTH_METHOD];
|
|
49
|
+
}, [existingTemplate]);
|
|
50
|
+
const createCustomMethod = useCallback(
|
|
51
|
+
async (input) => {
|
|
52
|
+
const method = templateFromPlacements(input.placements);
|
|
53
|
+
const exit = await doConfigure({
|
|
54
|
+
params: { slug },
|
|
55
|
+
payload: { authenticationTemplate: [...existingTemplate, method] },
|
|
56
|
+
reactivityKeys: integrationWriteKeys
|
|
57
|
+
});
|
|
58
|
+
if (Exit.isFailure(exit)) return null;
|
|
59
|
+
const before = new Set(existingTemplate.map((template) => String(template.slug)));
|
|
60
|
+
const created = authMethodsFromConfig(
|
|
61
|
+
exit.value.authenticationTemplate
|
|
62
|
+
).find((candidate) => !before.has(String(candidate.template)));
|
|
63
|
+
return created ?? null;
|
|
64
|
+
},
|
|
65
|
+
[doConfigure, slug, existingTemplate]
|
|
66
|
+
);
|
|
67
|
+
const removeCustomMethod = useCallback(
|
|
68
|
+
async (method) => {
|
|
69
|
+
if (method.source !== "custom") return false;
|
|
70
|
+
const next = existingTemplate.filter(
|
|
71
|
+
(template) => String(template.slug) !== String(method.template)
|
|
72
|
+
);
|
|
73
|
+
const exit = await doConfigure({
|
|
74
|
+
params: { slug },
|
|
75
|
+
payload: { authenticationTemplate: next, mode: "replace" },
|
|
76
|
+
reactivityKeys: integrationWriteKeys
|
|
77
|
+
});
|
|
78
|
+
return Exit.isSuccess(exit);
|
|
79
|
+
},
|
|
80
|
+
[doConfigure, existingTemplate, slug]
|
|
81
|
+
);
|
|
82
|
+
const audienceWarnings = useMemo(() => {
|
|
83
|
+
if (!AsyncResult.isSuccess(configResult) || configResult.value == null) return [];
|
|
84
|
+
const urls = configResult.value.googleDiscoveryUrls ?? [];
|
|
85
|
+
return googleAudienceWarningsForUrls(urls).flatMap((audience) => {
|
|
86
|
+
const message = GOOGLE_AUDIENCE_WARNING[audience];
|
|
87
|
+
return message ? [message] : [];
|
|
88
|
+
});
|
|
89
|
+
}, [configResult]);
|
|
90
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-3xl space-y-8 px-6 py-8", children: [
|
|
91
|
+
audienceWarnings.length > 0 && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
|
|
92
|
+
/* @__PURE__ */ jsx(TriangleAlert, {}),
|
|
93
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: "Some Google APIs need special consent" }),
|
|
94
|
+
/* @__PURE__ */ jsx(AlertDescription, { children: audienceWarnings.map((message) => /* @__PURE__ */ jsx("p", { children: message }, message)) })
|
|
95
|
+
] }),
|
|
96
|
+
/* @__PURE__ */ jsx(
|
|
97
|
+
AccountsSection,
|
|
98
|
+
{
|
|
99
|
+
integration: slug,
|
|
100
|
+
integrationName,
|
|
101
|
+
methods,
|
|
102
|
+
accountHandoff,
|
|
103
|
+
createCustomMethod,
|
|
104
|
+
removeCustomMethod
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
] });
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
OpenApiAccountsPanel as default
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=OpenApiAccountsPanel-3VJJXNQF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/OpenApiAccountsPanel.tsx"],"sourcesContent":["import { useCallback, useMemo } from \"react\";\nimport { useAtomValue, useAtomSet } from \"@effect/atom-react\";\nimport * as Exit from \"effect/Exit\";\nimport * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\nimport { AuthTemplateSlug, IntegrationSlug } from \"@executor-js/sdk/shared\";\nimport type { IntegrationAccountHandoff } from \"@executor-js/sdk/client\";\n\nimport { TriangleAlert } from \"lucide-react\";\n\nimport { AccountsSection } from \"@executor-js/react/components/accounts-section\";\nimport { Alert, AlertDescription, AlertTitle } from \"@executor-js/react/components/alert\";\nimport { integrationWriteKeys } from \"@executor-js/react/api/reactivity-keys\";\nimport type { CreateCustomMethod } from \"@executor-js/react/components/add-custom-method-modal\";\nimport type { AuthMethod, Placement } from \"@executor-js/react/lib/auth-placements\";\n\nimport { openApiConfigAtom, openapiConfigure } from \"./atoms\";\nimport { authMethodsFromConfig, templateFromPlacements } from \"./auth-method-config\";\nimport { googleAudienceWarningsForUrls } from \"../sdk/google-presets\";\nimport type { Authentication } from \"../sdk/types\";\n\nconst GOOGLE_AUDIENCE_WARNING: Readonly<Record<string, string>> = {\n \"workspace-admin\":\n \"This connection includes Google Workspace admin APIs (Chat, Admin Directory, Admin Reports). Connecting requires a Workspace admin account — personal Gmail accounts cannot grant these scopes.\",\n \"unsupported-user\":\n \"This connection includes APIs (e.g. Google Keep) that Google does not grant through standard user OAuth consent. Those tools may fail to authorize.\",\n};\n\nconst NO_AUTH_METHOD: AuthMethod = {\n id: \"none\",\n label: \"No authentication\",\n kind: \"none\",\n source: \"spec\",\n template: AuthTemplateSlug.make(\"none\"),\n placements: [],\n};\n\n// ---------------------------------------------------------------------------\n// OpenAPI Accounts hub — fills the generic detail page's `accounts` slot.\n//\n// Reads the integration's real `authenticationTemplate` (via `getConfig`),\n// converts it to generic `AuthMethod[]`, and composes the generic\n// `AccountsSection` — whose Add-account offers those methods plus a \"+ Custom\n// method\" row (apiKey-only). The custom-method create is INJECTED here\n// (`createCustomMethod`): generic placements → an `APIKeyAuthentication`\n// (`templateFromPlacements`, slug omitted → backend `custom_<id>`) merge-\n// appended onto the existing template and persisted via `configure`. Stays\n// plugin-side because it touches the OpenAPI sdk `Authentication` types.\n// ---------------------------------------------------------------------------\n\nexport default function OpenApiAccountsPanel(props: {\n readonly sourceId: string;\n readonly integrationName: string;\n readonly accountHandoff?: IntegrationAccountHandoff | null;\n}) {\n const { sourceId, integrationName, accountHandoff } = props;\n const slug = IntegrationSlug.make(sourceId);\n const configResult = useAtomValue(openApiConfigAtom(slug));\n const doConfigure = useAtomSet(openapiConfigure, { mode: \"promiseExit\" });\n\n // The wire `getConfig` template is structurally an `Authentication[]` (the\n // `slug` is an unbranded string on the wire); treat it as such for the\n // plugin-side converters that brand the slug back.\n const existingTemplate = useMemo<readonly Authentication[]>(() => {\n if (!AsyncResult.isSuccess(configResult) || configResult.value == null) return [];\n return (configResult.value.authenticationTemplate ?? []) as readonly Authentication[];\n }, [configResult]);\n\n const methods = useMemo<readonly AuthMethod[]>(() => {\n const declared = authMethodsFromConfig(existingTemplate);\n return declared.length > 0 ? declared : [NO_AUTH_METHOD];\n }, [existingTemplate]);\n\n // Add a custom apiKey method: build an `APIKeyAuthentication` from the generic\n // placements (slug omitted → backend backfills `custom_<id>`), merge-append it\n // onto the existing template, and persist. Returns the created `AuthMethod`\n // (derived from the same template) so Add-account can select it immediately.\n const createCustomMethod = useCallback<CreateCustomMethod>(\n async (input: { readonly label: string; readonly placements: readonly Placement[] }) => {\n const method = templateFromPlacements(input.placements);\n const exit = await doConfigure({\n params: { slug },\n payload: { authenticationTemplate: [...existingTemplate, method] },\n reactivityKeys: integrationWriteKeys,\n });\n if (Exit.isFailure(exit)) return null;\n const before = new Set(existingTemplate.map((template) => String(template.slug)));\n const created = authMethodsFromConfig(\n exit.value.authenticationTemplate as readonly Authentication[],\n ).find((candidate: AuthMethod) => !before.has(String(candidate.template)));\n return created ?? null;\n },\n [doConfigure, slug, existingTemplate],\n );\n\n const removeCustomMethod = useCallback(\n async (method: AuthMethod): Promise<boolean> => {\n if (method.source !== \"custom\") return false;\n const next = existingTemplate.filter(\n (template: Authentication) => String(template.slug) !== String(method.template),\n );\n const exit = await doConfigure({\n params: { slug },\n payload: { authenticationTemplate: next, mode: \"replace\" },\n reactivityKeys: integrationWriteKeys,\n });\n return Exit.isSuccess(exit);\n },\n [doConfigure, existingTemplate, slug],\n );\n\n // For a bundled `google` integration, surface a caution when any selected API\n // needs a privileged or unsupported OAuth consent the user should know about\n // BEFORE connecting an account. Derived from the stored Discovery URLs.\n const audienceWarnings = useMemo<readonly string[]>(() => {\n if (!AsyncResult.isSuccess(configResult) || configResult.value == null) return [];\n const urls = configResult.value.googleDiscoveryUrls ?? [];\n return googleAudienceWarningsForUrls(urls).flatMap((audience: string) => {\n const message = GOOGLE_AUDIENCE_WARNING[audience];\n return message ? [message] : [];\n });\n }, [configResult]);\n\n return (\n <div className=\"mx-auto max-w-3xl space-y-8 px-6 py-8\">\n {audienceWarnings.length > 0 && (\n <Alert variant=\"destructive\">\n <TriangleAlert />\n <AlertTitle>Some Google APIs need special consent</AlertTitle>\n <AlertDescription>\n {audienceWarnings.map((message: string) => (\n <p key={message}>{message}</p>\n ))}\n </AlertDescription>\n </Alert>\n )}\n <AccountsSection\n integration={slug}\n integrationName={integrationName}\n methods={methods}\n accountHandoff={accountHandoff}\n createCustomMethod={createCustomMethod}\n removeCustomMethod={removeCustomMethod}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|