improvement: native integration legacy details and sync redirects

This commit is contained in:
Scott Wilson
2025-04-11 19:34:33 -07:00
parent cdc364d44c
commit d00b34663e
16 changed files with 204 additions and 105 deletions

View File

@@ -31,6 +31,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
.object({ .object({
name: z.string(), name: z.string(),
slug: z.string(), slug: z.string(),
syncSlug: z.string().optional(),
clientSlug: z.string().optional(), clientSlug: z.string().optional(),
image: z.string(), image: z.string(),
isAvailable: z.boolean().optional(), isAvailable: z.boolean().optional(),

View File

@@ -195,6 +195,7 @@ export const getIntegrationOptions = async () => {
{ {
name: "AWS Secrets Manager", name: "AWS Secrets Manager",
slug: "aws-secret-manager", slug: "aws-secret-manager",
syncSlug: "aws-secrets-manager",
image: "Amazon Web Services.png", image: "Amazon Web Services.png",
isAvailable: true, isAvailable: true,
type: "custom", type: "custom",

View File

@@ -3,6 +3,10 @@ title: "AWS Parameter Store"
description: "Learn how to sync secrets from Infisical to AWS Parameter Store." description: "Learn how to sync secrets from Infisical to AWS Parameter Store."
--- ---
<Note>
The AWS Parameter Store Native Integration will be deprecated in 2026. Please migrate to our new [AWS Parameter Store Sync](../secret-syncs/aws-parameter-store).
</Note>
<Tabs> <Tabs>
<Tab title="Assume Role (Recommended)"> <Tab title="Assume Role (Recommended)">
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials. Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.

View File

@@ -3,6 +3,10 @@ title: "AWS Secrets Manager"
description: "Learn how to sync secrets from Infisical to AWS Secrets Manager." description: "Learn how to sync secrets from Infisical to AWS Secrets Manager."
--- ---
<Note>
The AWS Secrets Manager Native Integration will be deprecated in 2026. Please migrate to our new [AWS Secrets Manager Sync](../secret-syncs/aws-secrets-manager).
</Note>
<Tabs> <Tabs>
<Tab title="Assume Role (Recommended)"> <Tab title="Assume Role (Recommended)">
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials. Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.

View File

@@ -3,6 +3,10 @@ title: "Azure App Configuration"
description: "How to sync secrets from Infisical to Azure App Configuration" description: "How to sync secrets from Infisical to Azure App Configuration"
--- ---
<Note>
The Azure App Configuration Native Integration will be deprecated in 2026. Please migrate to our new [Azure App Configuration Sync](../secret-syncs/azure-app-configuration).
</Note>
<Tabs> <Tabs>
<Tab title="Usage"> <Tab title="Usage">
**Prerequisites:** **Prerequisites:**

View File

@@ -3,6 +3,10 @@ title: "Azure Key Vault"
description: "How to sync secrets from Infisical to Azure Key Vault" description: "How to sync secrets from Infisical to Azure Key Vault"
--- ---
<Note>
The Azure Key Vault Native Integration will be deprecated in 2026. Please migrate to our new [Azure Key Vault Sync](../secret-syncs/azure-key-vault).
</Note>
<Tabs> <Tabs>
<Tab title="Usage"> <Tab title="Usage">
Prerequisites: Prerequisites:

View File

@@ -3,6 +3,10 @@ title: "Databricks"
description: "Learn how to sync secrets from Infisical to Databricks." description: "Learn how to sync secrets from Infisical to Databricks."
--- ---
<Note>
The Databricks Native Integration will be deprecated in 2026. Please migrate to our new [Databricks Sync](../secret-syncs/databricks).
</Note>
Prerequisites: Prerequisites:
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com) - Set up and add secrets to [Infisical Cloud](https://app.infisical.com)

View File

@@ -3,6 +3,10 @@ title: "GCP Secret Manager"
description: "How to sync secrets from Infisical to GCP Secret Manager" description: "How to sync secrets from Infisical to GCP Secret Manager"
--- ---
<Note>
The GCP Secret Manager Native Integration will be deprecated in 2026. Please migrate to our new [GCP Secret Manager Sync](../secret-syncs/gcp-secret-manager).
</Note>
<Tabs> <Tabs>
<Tab title="Usage"> <Tab title="Usage">
<AccordionGroup> <AccordionGroup>

View File

@@ -3,6 +3,10 @@ title: "Terraform Cloud"
description: "How to sync secrets from Infisical to Terraform Cloud" description: "How to sync secrets from Infisical to Terraform Cloud"
--- ---
<Note>
The Terraform Cloud Native Integration will be deprecated in 2026. Please migrate to our new [Terraform Cloud Sync](../secret-syncs/terraform-cloud).
</Note>
Prerequisites: Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - Set up and add envars to [Infisical Cloud](https://app.infisical.com)

View File

@@ -3,6 +3,11 @@ title: "Vercel"
description: "How to sync secrets from Infisical to Vercel" description: "How to sync secrets from Infisical to Vercel"
--- ---
<Note>
The Vercel Native Integration will be deprecated in 2026. Please migrate to our new [Vercel Sync](../secret-syncs/vercel).
</Note>
<Tabs> <Tabs>
<Tab title="Usage"> <Tab title="Usage">
Prerequisites: Prerequisites:

View File

@@ -1,4 +1,4 @@
import { useState } from "react"; import { useEffect, useState } from "react";
import { Modal, ModalContent } from "@app/components/v2"; import { Modal, ModalContent } from "@app/components/v2";
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs"; import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
@@ -10,6 +10,7 @@ import { SecretSyncSelect } from "./SecretSyncSelect";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
onOpenChange: (isOpen: boolean) => void; onOpenChange: (isOpen: boolean) => void;
selectSync?: SecretSync | null;
}; };
type ContentProps = { type ContentProps = {
@@ -32,8 +33,12 @@ const Content = ({ onComplete, setSelectedSync, selectedSync }: ContentProps) =>
return <SecretSyncSelect onSelect={setSelectedSync} />; return <SecretSyncSelect onSelect={setSelectedSync} />;
}; };
export const CreateSecretSyncModal = ({ onOpenChange, ...props }: Props) => { export const CreateSecretSyncModal = ({ onOpenChange, selectSync = null, ...props }: Props) => {
const [selectedSync, setSelectedSync] = useState<SecretSync | null>(null); const [selectedSync, setSelectedSync] = useState<SecretSync | null>(selectSync);
useEffect(() => {
setSelectedSync(selectSync);
}, [selectSync]);
return ( return (
<Modal <Modal

View File

@@ -1,6 +1,7 @@
export type TCloudIntegration = { export type TCloudIntegration = {
name: string; name: string;
slug: string; slug: string;
syncSlug?: string;
image: string; image: string;
isAvailable: boolean; isAvailable: boolean;
type: string; type: string;

View File

@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate, useSearch } from "@tanstack/react-router"; import { useNavigate, useSearch } from "@tanstack/react-router";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { Badge, PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; import { Badge, PageHeader, Tab, TabList, TabPanel, Tabs, Tooltip } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes"; import { ROUTE_PATHS } from "@app/const/routes";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types"; import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
@@ -51,46 +51,19 @@ export const IntegrationsListPage = () => {
title="Integrations" title="Integrations"
description="Manage integrations with third-party services." description="Manage integrations with third-party services."
/> />
<div className="mb-4 mt-4 flex flex-col rounded-r border-l-2 border-l-primary bg-mineshaft-300/5 px-4 py-2.5">
<div className="mb-1 flex items-center text-sm">
<FontAwesomeIcon icon={faInfoCircle} size="sm" className="mr-1.5 text-primary" />
Integrations Update
</div>
<p className="mb-2 mt-1 text-sm text-bunker-300">
Infisical is excited to announce{" "}
<a
className="text-bunker-200 underline decoration-primary-700 underline-offset-4 duration-200 hover:text-mineshaft-100 hover:decoration-primary-600"
href="https://infisical.com/docs/integrations/secret-syncs/overview"
target="_blank"
rel="noopener noreferrer"
>
Secret Syncs
</a>
, a new way to sync your secrets to third-party services using{" "}
<a
className="text-bunker-200 underline decoration-primary-700 underline-offset-4 duration-200 hover:text-mineshaft-100 hover:decoration-primary-600"
href="https://infisical.com/docs/integrations/app-connections/overview"
target="_blank"
rel="noopener noreferrer"
>
App Connections
</a>
, offering deeper customization and re-configurability.
</p>
<p className="text-sm text-bunker-300">
Existing integrations (now called Native Integrations) will continue to be supported
as we build out our Secret Sync library.
</p>
</div>
<Tabs value={selectedTab} onValueChange={updateSelectedTab}> <Tabs value={selectedTab} onValueChange={updateSelectedTab}>
<TabList> <TabList>
<Tab value={IntegrationsListPageTabs.SecretSyncs}> <Tab value={IntegrationsListPageTabs.SecretSyncs}>Secret Syncs</Tab>
Secret Syncs <Tab value={IntegrationsListPageTabs.NativeIntegrations}>
<Badge variant="primary" className="ml-1 cursor-pointer text-xs"> Native Integrations
New <Tooltip content="Native Integrations will be deprecated in 2026. Please migrate to Secret Syncs as they become available.">
</Badge> <div>
<Badge variant="primary" className="ml-1 cursor-pointer text-xs">
Legacy
</Badge>
</div>
</Tooltip>
</Tab> </Tab>
<Tab value={IntegrationsListPageTabs.NativeIntegrations}>Native Integrations</Tab>
<Tab value={IntegrationsListPageTabs.FrameworkIntegrations}> <Tab value={IntegrationsListPageTabs.FrameworkIntegrations}>
Framework Integrations Framework Integrations
</Tab> </Tab>
@@ -108,6 +81,26 @@ export const IntegrationsListPage = () => {
</ProjectPermissionCan> </ProjectPermissionCan>
</TabPanel> </TabPanel>
<TabPanel value={IntegrationsListPageTabs.NativeIntegrations}> <TabPanel value={IntegrationsListPageTabs.NativeIntegrations}>
<div className="mb-5 flex flex-col rounded-r border-l-2 border-l-primary bg-mineshaft-300/5 px-4 py-2.5">
<div className="mb-1 flex items-center text-sm">
<FontAwesomeIcon icon={faInfoCircle} size="sm" className="mr-1.5 text-primary" />
Native Integrations Transitioning to Legacy Status
</div>
<p className="mb-2 mt-1 text-sm text-bunker-300">
Native integrations are now a legacy feature and will begin deprecation in 2026.
We recommend migrating to our new{" "}
<a
className="text-bunker-200 underline decoration-primary-700 underline-offset-4 duration-200 hover:text-mineshaft-100 hover:decoration-primary-600"
href="https://infisical.com/docs/integrations/secret-syncs/overview"
target="_blank"
rel="noopener noreferrer"
>
Secret Syncs
</a>{" "}
feature which offers the same functionality as Native Integrations with improved
stability, insights, re-configurability, and customization.
</p>
</div>
<ProjectPermissionCan <ProjectPermissionCan
renderGuardBanner renderGuardBanner
I={ProjectPermissionActions.Read} I={ProjectPermissionActions.Read}

View File

@@ -8,6 +8,7 @@ import {
faXmark faXmark
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate } from "@tanstack/react-router";
import { NoEnvironmentsBanner } from "@app/components/integrations/NoEnvironmentsBanner"; import { NoEnvironmentsBanner } from "@app/components/integrations/NoEnvironmentsBanner";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -19,6 +20,7 @@ import {
Skeleton, Skeleton,
Tooltip Tooltip
} from "@app/components/v2"; } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { import {
ProjectPermissionActions, ProjectPermissionActions,
ProjectPermissionSub, ProjectPermissionSub,
@@ -26,7 +28,9 @@ import {
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types"; import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types";
import { IntegrationsListPageTabs } from "@app/types/integrations";
type Props = { type Props = {
isLoading?: boolean; isLoading?: boolean;
@@ -40,6 +44,10 @@ type Props = {
type TRevokeIntegrationPopUp = { provider: string }; type TRevokeIntegrationPopUp = { provider: string };
const SECRET_SYNCS = Object.values(SecretSync) as string[];
// this assumes sync and integration will have same name which looks like it will be the case
const isSecretSyncAvailable = (type: string) => SECRET_SYNCS.includes(type);
export const CloudIntegrationSection = ({ export const CloudIntegrationSection = ({
isLoading, isLoading,
cloudIntegrations = [], cloudIntegrations = [],
@@ -54,6 +62,7 @@ export const CloudIntegrationSection = ({
] as const); ] as const);
const { permission } = useProjectPermission(); const { permission } = useProjectPermission();
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const navigate = useNavigate();
const isEmpty = !isLoading && !cloudIntegrations?.length; const isEmpty = !isLoading && !cloudIntegrations?.length;
@@ -111,73 +120,104 @@ export const CloudIntegrationSection = ({
))} ))}
{!isLoading && filteredIntegrations.length ? ( {!isLoading && filteredIntegrations.length ? (
filteredIntegrations.map((cloudIntegration) => ( filteredIntegrations.map((cloudIntegration) => {
<div const syncSlug = cloudIntegration.syncSlug ?? cloudIntegration.slug;
onKeyDown={() => null} const isSyncAvailable = isSecretSyncAvailable(syncSlug);
role="button"
tabIndex={0} return (
className={`group relative ${ <div
cloudIntegration.isAvailable onKeyDown={() => null}
? "cursor-pointer duration-200 hover:bg-mineshaft-700" role="button"
: "opacity-50" tabIndex={0}
} flex h-32 flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4`} className={`group relative ${
onClick={() => { cloudIntegration.isAvailable
if (!cloudIntegration.isAvailable) return; ? "cursor-pointer duration-200 hover:bg-mineshaft-700"
if ( : "opacity-50"
permission.cannot( } flex h-36 flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-3`}
ProjectPermissionActions.Create, onClick={() => {
ProjectPermissionSub.Integrations if (isSyncAvailable) {
) navigate({
) { to: ROUTE_PATHS.SecretManager.IntegrationsListPage.path,
createNotification({ params: {
type: "error", projectId: currentWorkspace.id
text: "You do not have permission to create an integration" },
}); search: {
return; selectedTab: IntegrationsListPageTabs.SecretSyncs,
} addSync: syncSlug as SecretSync
onIntegrationStart(cloudIntegration.slug); }
}} });
key={cloudIntegration.slug} return;
> }
<img if (!cloudIntegration.isAvailable) return;
src={`/images/integrations/${cloudIntegration.image}`} if (
height={60} permission.cannot(
width={60} ProjectPermissionActions.Create,
className="mt-auto" ProjectPermissionSub.Integrations
alt="integration logo" )
/> ) {
<div className="mt-auto max-w-xs text-center text-sm font-semibold text-gray-300 duration-200 group-hover:text-gray-200"> createNotification({
{cloudIntegration.name} type: "error",
</div> text: "You do not have permission to create an integration"
{cloudIntegration.isAvailable && });
Boolean(integrationAuths?.[cloudIntegration.slug]) && ( return;
<div className="absolute right-0 top-0 z-30 h-full"> }
<div className="relative h-full"> onIntegrationStart(cloudIntegration.slug);
<div className="absolute right-0 top-0 w-24 flex-row items-center overflow-hidden whitespace-nowrap rounded-bl-md rounded-tr-md bg-primary px-2 py-0.5 text-xs text-black opacity-80 transition-all duration-300 group-hover:w-0 group-hover:p-0"> }}
<FontAwesomeIcon icon={faCheck} className="mr-2 text-xs" /> key={cloudIntegration.slug}
Authorized >
</div> <div className="m-auto flex flex-col items-center">
<Tooltip content="Revoke Access"> <img
<div src={`/images/integrations/${cloudIntegration.image}`}
onKeyDown={() => null} height={60}
role="button" width={60}
tabIndex={0} className="mt-auto"
onClick={async (event) => { alt="integration logo"
event.stopPropagation(); />
handlePopUpOpen("deleteConfirmation", { <div
provider: cloudIntegration.slug className={`mt-2 max-w-xs text-center text-sm font-semibold text-gray-300 duration-200 group-hover:text-gray-200 ${isSyncAvailable ? "mb-4" : ""}`}
}); >
}} {cloudIntegration.name}
className="absolute right-0 top-0 flex h-0 w-12 cursor-pointer items-center justify-center overflow-hidden rounded-r-md bg-red text-xs opacity-50 transition-all duration-300 hover:opacity-100 group-hover:h-full" </div>
> </div>
<FontAwesomeIcon icon={faXmark} size="xl" /> {cloudIntegration.isAvailable &&
Boolean(integrationAuths?.[cloudIntegration.slug]) && (
<div className="absolute right-0 top-0 z-30 h-full">
<div className="relative h-full">
<div className="absolute right-0 top-0 w-24 flex-row items-center overflow-hidden whitespace-nowrap rounded-bl-md rounded-tr-md bg-primary px-2 py-0.5 text-xs text-black opacity-80 transition-all duration-300 group-hover:w-0 group-hover:p-0">
<FontAwesomeIcon icon={faCheck} className="mr-2 text-xs" />
Authorized
</div> </div>
</Tooltip> <Tooltip content="Revoke Access">
<div
onKeyDown={() => null}
role="button"
tabIndex={0}
onClick={async (event) => {
event.stopPropagation();
handlePopUpOpen("deleteConfirmation", {
provider: cloudIntegration.slug
});
}}
className="absolute right-0 top-0 flex h-0 w-12 cursor-pointer items-center justify-center overflow-hidden rounded-r-md bg-red text-xs opacity-50 transition-all duration-300 hover:opacity-100 group-hover:h-full"
>
<FontAwesomeIcon icon={faXmark} size="xl" />
</div>
</Tooltip>
</div>
</div>
)}
{isSyncAvailable && (
<div className="absolute bottom-0 left-0 z-30 h-full w-full">
<div className="relative h-full">
<div className="absolute bottom-0 left-0 w-full flex-row overflow-hidden whitespace-nowrap rounded-bl-md rounded-br-md bg-yellow/20 px-2 py-0.5 text-center text-xs text-yellow">
Secret Sync Available
</div>
</div> </div>
</div> </div>
)} )}
</div> </div>
)) );
})
) : ( ) : (
<EmptyState <EmptyState
className="col-span-full h-32 w-full rounded-md bg-transparent pt-14" className="col-span-full h-32 w-full rounded-md bg-transparent pt-14"

View File

@@ -1,9 +1,12 @@
import { useEffect } from "react";
import { faArrowUpRightFromSquare, faBookOpen, faPlus } from "@fortawesome/free-solid-svg-icons"; import { faArrowUpRightFromSquare, faBookOpen, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate, useSearch } from "@tanstack/react-router";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { CreateSecretSyncModal } from "@app/components/secret-syncs"; import { CreateSecretSyncModal } from "@app/components/secret-syncs";
import { Button, Spinner } from "@app/components/v2"; import { Button, Spinner } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { ProjectPermissionSub, useWorkspace } from "@app/context"; import { ProjectPermissionSub, useWorkspace } from "@app/context";
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types"; import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
@@ -14,8 +17,27 @@ import { SecretSyncsTable } from "./SecretSyncTable";
export const SecretSyncsTab = () => { export const SecretSyncsTab = () => {
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["addSync"] as const); const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["addSync"] as const);
const { addSync, ...search } = useSearch({
from: ROUTE_PATHS.SecretManager.IntegrationsListPage.id
});
const navigate = useNavigate();
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
useEffect(() => {
if (!addSync) return;
handlePopUpOpen("addSync", addSync);
navigate({
to: ROUTE_PATHS.SecretManager.IntegrationsListPage.path,
params: {
projectId: currentWorkspace.id
},
search
});
}, [addSync]);
const { data: secretSyncs = [], isPending: isSecretSyncsPending } = useListSecretSyncs( const { data: secretSyncs = [], isPending: isSecretSyncsPending } = useListSecretSyncs(
currentWorkspace.id, currentWorkspace.id,
{ {
@@ -76,6 +98,7 @@ export const SecretSyncsTab = () => {
<SecretSyncsTable secretSyncs={secretSyncs} /> <SecretSyncsTable secretSyncs={secretSyncs} />
</div> </div>
<CreateSecretSyncModal <CreateSecretSyncModal
selectSync={popUp.addSync.data}
isOpen={popUp.addSync.isOpen} isOpen={popUp.addSync.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("addSync", isOpen)} onOpenChange={(isOpen) => handlePopUpToggle("addSync", isOpen)}
/> />

View File

@@ -6,6 +6,7 @@ import { workspaceKeys } from "@app/hooks/api";
import { TIntegration } from "@app/hooks/api/integrations/types"; import { TIntegration } from "@app/hooks/api/integrations/types";
import { import {
fetchSecretSyncsByProjectId, fetchSecretSyncsByProjectId,
SecretSync,
secretSyncKeys, secretSyncKeys,
TSecretSync TSecretSync
} from "@app/hooks/api/secretSyncs"; } from "@app/hooks/api/secretSyncs";
@@ -15,7 +16,8 @@ import { IntegrationsListPageTabs } from "@app/types/integrations";
import { IntegrationsListPage } from "./IntegrationsListPage"; import { IntegrationsListPage } from "./IntegrationsListPage";
const IntegrationsListPageQuerySchema = z.object({ const IntegrationsListPageQuerySchema = z.object({
selectedTab: z.nativeEnum(IntegrationsListPageTabs).optional() selectedTab: z.nativeEnum(IntegrationsListPageTabs).optional(),
addSync: z.nativeEnum(SecretSync).optional()
}); });
export const Route = createFileRoute( export const Route = createFileRoute(