export const INTEGRATION_SECTION_VALUES = [ 'provider', 'builtin', 'mcp', 'custom-tool', 'workflow-tool', 'data-source', 'custom-endpoint', 'trigger', 'agent-strategy', 'extension', ] as const export type IntegrationSection = (typeof INTEGRATION_SECTION_VALUES)[number] export const TOOL_CATEGORY_VALUES = ['builtin', 'api', 'workflow', 'mcp'] as const export type ToolCategory = (typeof TOOL_CATEGORY_VALUES)[number] export type LegacyToolsSearchParams = Record export type IntegrationRouteSearchParams = Record const integrationSectionSet = new Set(INTEGRATION_SECTION_VALUES) const toolCategorySet = new Set(TOOL_CATEGORY_VALUES) const isIntegrationSection = (value: string): value is IntegrationSection => { return integrationSectionSet.has(value) } const isToolCategory = (value: string): value is ToolCategory => { return toolCategorySet.has(value) } const getFirstSearchParamValue = (value: string | string[] | undefined) => { if (Array.isArray(value)) return value[0] return value } const appendSearchParams = (path: string, searchParams: IntegrationRouteSearchParams = {}) => { const params = new URLSearchParams() Object.entries(searchParams).forEach(([key, value]) => { if (value === undefined) return if (Array.isArray(value)) { value.forEach((item) => params.append(key, item)) return } params.set(key, value) }) const query = params.toString() return query ? `${path}?${query}` : path } export const toolCategoryBySection: Partial> = { builtin: 'builtin', mcp: 'mcp', 'custom-tool': 'api', 'workflow-tool': 'workflow', } export const sectionByToolCategory: Record = { builtin: 'builtin', api: 'custom-tool', workflow: 'workflow-tool', mcp: 'mcp', } export const marketplaceUrlPathByIntegrationSection: Partial> = { provider: '/plugins/model', builtin: '/plugins/tool', mcp: '/plugins/tool', 'custom-tool': '/plugins/tool', 'workflow-tool': '/plugins/tool', 'data-source': '/plugins/datasource', 'custom-endpoint': '/plugins/extension', trigger: '/plugins/trigger', 'agent-strategy': '/plugins/agent-strategy', extension: '/plugins/extension', } export const integrationPathBySection: Record = { provider: '/integrations/model-provider', builtin: '/integrations/tools/built-in', 'custom-tool': '/integrations/tools/api', 'workflow-tool': '/integrations/tools/workflow', mcp: '/integrations/tools/mcp', 'data-source': '/integrations/data-source', 'custom-endpoint': '/integrations/custom-endpoint', trigger: '/integrations/trigger', 'agent-strategy': '/integrations/agent-strategy', extension: '/integrations/extension', } export const buildIntegrationPath = (section: IntegrationSection) => { return integrationPathBySection[section] } export const buildMarketplaceUrlPathByIntegrationSection = (section: IntegrationSection) => { return marketplaceUrlPathByIntegrationSection[section] ?? '/plugins' } export const getIntegrationRedirectPathByLegacyToolsSearchParams = ( searchParams: LegacyToolsSearchParams = {}, ) => { const sectionParam = getFirstSearchParamValue(searchParams.section) const categoryParam = getFirstSearchParamValue(searchParams.category) const section = sectionParam && isIntegrationSection(sectionParam) ? sectionParam : categoryParam && isToolCategory(categoryParam) ? sectionByToolCategory[categoryParam] : 'builtin' const preservedSearchParams = new URLSearchParams() Object.entries(searchParams).forEach(([key, value]) => { if (key === 'section' || key === 'category' || value === undefined) return if (Array.isArray(value)) { value.forEach((item) => preservedSearchParams.append(key, item)) return } preservedSearchParams.set(key, value) }) const query = preservedSearchParams.toString() return query ? `${buildIntegrationPath(section)}?${query}` : buildIntegrationPath(section) } type IntegrationRouteTarget = | { type: 'redirect'; destination: string } | { type: 'section'; section: IntegrationSection } | { type: 'not-found' } const getSectionByCanonicalPath = (path: string) => { const entry = Object.entries(integrationPathBySection).find(([, sectionPath]) => { return sectionPath.replace('/integrations/', '') === path }) return entry?.[0] as IntegrationSection | undefined } export const getIntegrationRouteTargetBySlug = ( slug?: string[], searchParams?: IntegrationRouteSearchParams, ): IntegrationRouteTarget => { const path = slug?.join('/') ?? '' const nestedMarketplaceCallbackPath = path.endsWith('/plugins') ? path.slice(0, -'/plugins'.length) : undefined const nestedMarketplaceCallbackSection = nestedMarketplaceCallbackPath ? getSectionByCanonicalPath(nestedMarketplaceCallbackPath) : undefined if (nestedMarketplaceCallbackSection) { return { type: 'redirect', destination: appendSearchParams( buildIntegrationPath(nestedMarketplaceCallbackSection), searchParams, ), } } switch (path) { case '': return { type: 'redirect', destination: buildIntegrationPath('provider') } case 'model-provider': return { type: 'section', section: 'provider' } case 'tools': return { type: 'redirect', destination: buildIntegrationPath('builtin') } case 'tools/built-in': return { type: 'section', section: 'builtin' } case 'tool/api': return { type: 'redirect', destination: appendSearchParams(buildIntegrationPath('custom-tool'), searchParams), } case 'tools/api': return { type: 'section', section: 'custom-tool' } case 'tools/workflow': return { type: 'section', section: 'workflow-tool' } case 'tools/mcp': return { type: 'section', section: 'mcp' } case 'data-source': return { type: 'section', section: 'data-source' } case 'custom-endpoint': return { type: 'section', section: 'custom-endpoint' } case 'trigger': return { type: 'section', section: 'trigger' } case 'agent-strategy': return { type: 'section', section: 'agent-strategy' } case 'extension': return { type: 'section', section: 'extension' } default: return { type: 'not-found' } } }