import type { AgentAppPagination } from '@dify/contracts/api/console/agent/types.gen' import type { ApiBasedExtensionResponse } from '@dify/contracts/api/console/api-based-extension/types.gen' import type { consoleRouterContract } from '@dify/contracts/api/console/router.gen' import type { TagResponse as Tag, TagType } from '@dify/contracts/api/console/tags/types.gen' import type { GetReleaseResponse, ListReleasesResponse, PrecheckReleaseRequest, } from '@dify/contracts/enterprise/types.gen' import type { ClientLink } from '@orpc/client' import type { AnyContractRouter, ContractRouterClient } from '@orpc/contract' import type { JsonifiedClient } from '@orpc/openapi-client' import type { RouterUtils, TanstackQueryOperationContext } from '@orpc/tanstack-query' import type { InfiniteData, QueryClient, QueryKey } from '@tanstack/react-query' import { marketplaceRouterContract } from '@dify/contracts/marketplace' import { createORPCClient, onError } from '@orpc/client' import { OpenAPILink } from '@orpc/openapi-client/fetch' import { createTanstackQueryUtils } from '@orpc/tanstack-query' import { API_PREFIX, APP_VERSION, IS_MARKETPLACE, MARKETPLACE_API_PREFIX } from '@/config' import { isClient } from '@/utils/client' // oxlint-disable-next-line no-restricted-imports import { request, sseGeneratorPost } from './base' import { createConsoleDynamicLink } from './console-link' import { normalizeConsoleOpenAPIURL } from './console-openapi-url' export function streamWorkflowGeneration(...args: Parameters) { return sseGeneratorPost(...args) } function getMarketplaceHeaders() { return new Headers({ 'X-Dify-Version': !IS_MARKETPLACE ? APP_VERSION : '999.0.0', }) } function isURL(path: string) { try { // oxlint-disable-next-line no-new new URL(path) return true } catch { return false } } export function getBaseURL(path: string) { const url = new URL( path, isURL(path) ? undefined : isClient ? window.location.origin : 'http://localhost', ) if (!isClient && !isURL(path)) { console.warn('Using localhost as base URL in server environment, please configure accordingly.') } if (url.protocol !== 'http:' && url.protocol !== 'https:') { console.warn( `Unexpected protocol for API requests, expected http or https. Current protocol: ${url.protocol}. Please configure accordingly.`, ) } return url } export type ConsoleClientContext = TanstackQueryOperationContext & { silent?: boolean } type ConsoleClientLink = ClientLink function createConsoleOpenAPILink(contract: AnyContractRouter): ConsoleClientLink { return new OpenAPILink(contract, { url: getBaseURL(API_PREFIX), fetch: (input, init, options) => { return request(normalizeConsoleOpenAPIURL(input.url), init, { fetchCompat: true, request: input, silent: options.context.silent, }) }, interceptors: [ onError((error) => { console.error(error) }), ], }) } const marketplaceLink = new OpenAPILink(marketplaceRouterContract, { url: MARKETPLACE_API_PREFIX, headers: () => getMarketplaceHeaders(), fetch: (request, init) => { return globalThis.fetch(request, { ...init, cache: 'no-store', }) }, interceptors: [ onError((error) => { console.error(error) }), ], }) export const marketplaceClient: JsonifiedClient< ContractRouterClient > = createORPCClient(marketplaceLink) export const marketplaceQuery = createTanstackQueryUtils(marketplaceClient, { path: ['marketplace'], }) const APP_DEPLOY_SOURCE_APPS_PAGE_SIZE = 100 const APP_DEPLOY_READINESS_RETRY_DELAYS = [0, 300, 700, 1200] type AppDeployInvalidationOptions = { appInstances?: boolean appInstanceSummaries?: boolean appInstance?: boolean appInstanceOverview?: boolean environmentDeployments?: boolean releases?: boolean releaseSummaries?: boolean releaseDeploymentView?: boolean accessChannels?: boolean accessSettings?: boolean developerApiSettings?: boolean } type ConsoleQueryUtils = RouterUtils< JsonifiedClient> > function isTagType(type: string | null | undefined): type is TagType { return type === 'app' || type === 'knowledge' || type === 'snippet' } const defaultAppDeployInvalidationOptions = { appInstances: true, appInstanceSummaries: true, appInstance: true, appInstanceOverview: true, environmentDeployments: true, releases: true, releaseSummaries: true, releaseDeploymentView: true, accessChannels: true, accessSettings: true, developerApiSettings: true, } satisfies Required function invalidateQueryKeys(client: QueryClient, queryKeys: QueryKey[]) { return Promise.all(queryKeys.map((queryKey) => client.invalidateQueries({ queryKey }))) } function appInstanceQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.appInstanceService.getAppInstance.key({ type: 'query', input: { params: { appInstanceId } }, }) } function appInstanceOverviewQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.appInstanceService.getAppInstanceOverview.key({ type: 'query', input: { params: { appInstanceId } }, }) } function environmentDeploymentsQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.deploymentService.listEnvironmentDeployments.key({ type: 'query', input: { params: { appInstanceId } }, }) } function releasesQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.releaseService.listReleases.key({ type: 'query', input: { params: { appInstanceId } }, }) } function releaseSummariesQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.releaseService.listReleaseSummaries.key({ type: 'query', input: { params: { appInstanceId } }, }) } function releaseDeploymentViewQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.releaseService.computeReleaseDeploymentView.key({ type: 'query', input: { params: { appInstanceId } }, }) } function releaseQueryKey(query: ConsoleQueryUtils, releaseId: string) { return query.enterprise.releaseService.getRelease.key({ type: 'query', input: { params: { releaseId } }, }) } function cachedReleaseAppInstanceId( query: ConsoleQueryUtils, client: QueryClient, releaseId: string, ) { const listQueries = client.getQueriesData({ queryKey: query.enterprise.releaseService.listReleases.key({ type: 'query' }), }) for (const [, data] of listQueries) { const appInstanceId = data?.releases?.find((release) => release.id === releaseId)?.appInstanceId if (appInstanceId) return appInstanceId } const releaseQueries = client.getQueriesData({ queryKey: query.enterprise.releaseService.getRelease.key({ type: 'query' }), }) for (const [, data] of releaseQueries) { const release = data?.release if (release?.id === releaseId && release.appInstanceId) return release.appInstanceId } } function precheckReleaseQueryKey(query: ConsoleQueryUtils, body: PrecheckReleaseRequest) { return query.enterprise.releaseService.precheckRelease.key({ type: 'query', input: { body }, }) } function accessChannelsQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.accessService.getAccessChannels.key({ type: 'query', input: { params: { appInstanceId } }, }) } function accessSettingsQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.accessService.getAccessSettings.key({ type: 'query', input: { params: { appInstanceId } }, }) } function developerApiSettingsQueryKey(query: ConsoleQueryUtils, appInstanceId: string) { return query.enterprise.accessService.getDeveloperApiSettings.key({ type: 'query', input: { params: { appInstanceId } }, }) } function apiKeysQueryKey(query: ConsoleQueryUtils, appInstanceId: string, environmentId: string) { return query.enterprise.accessService.listApiKeys.key({ type: 'query', input: { params: { appInstanceId, environmentId } }, }) } function accessPolicyQueryKey( query: ConsoleQueryUtils, appInstanceId: string, environmentId: string, ) { return query.enterprise.accessService.getAccessPolicy.key({ type: 'query', input: { params: { appInstanceId, environmentId } }, }) } function invalidateAppDeployQueries( query: ConsoleQueryUtils, client: QueryClient, appInstanceId: string, options: AppDeployInvalidationOptions = {}, ) { const resolvedOptions = { ...defaultAppDeployInvalidationOptions, ...options, } const queryKeys: QueryKey[] = [] if (resolvedOptions.appInstances) queryKeys.push(query.enterprise.appInstanceService.listAppInstances.key()) if (resolvedOptions.appInstanceSummaries) queryKeys.push(query.enterprise.appInstanceService.listAppInstanceSummaries.key()) if (resolvedOptions.appInstance) queryKeys.push(appInstanceQueryKey(query, appInstanceId)) if (resolvedOptions.appInstanceOverview) queryKeys.push(appInstanceOverviewQueryKey(query, appInstanceId)) if (resolvedOptions.environmentDeployments) queryKeys.push(environmentDeploymentsQueryKey(query, appInstanceId)) if (resolvedOptions.releases) queryKeys.push(releasesQueryKey(query, appInstanceId)) if (resolvedOptions.releaseSummaries) queryKeys.push(releaseSummariesQueryKey(query, appInstanceId)) if (resolvedOptions.releaseDeploymentView) queryKeys.push(releaseDeploymentViewQueryKey(query, appInstanceId)) if (resolvedOptions.accessChannels) queryKeys.push(accessChannelsQueryKey(query, appInstanceId)) if (resolvedOptions.accessSettings) queryKeys.push(accessSettingsQueryKey(query, appInstanceId)) if (resolvedOptions.developerApiSettings) queryKeys.push(developerApiSettingsQueryKey(query, appInstanceId)) return invalidateQueryKeys(client, queryKeys) } function removeAppDeployQueries( query: ConsoleQueryUtils, client: QueryClient, appInstanceId: string, ) { const queryKeys = [ appInstanceQueryKey(query, appInstanceId), appInstanceOverviewQueryKey(query, appInstanceId), environmentDeploymentsQueryKey(query, appInstanceId), releasesQueryKey(query, appInstanceId), releaseSummariesQueryKey(query, appInstanceId), releaseDeploymentViewQueryKey(query, appInstanceId), accessChannelsQueryKey(query, appInstanceId), accessSettingsQueryKey(query, appInstanceId), developerApiSettingsQueryKey(query, appInstanceId), ] queryKeys.forEach((queryKey) => client.removeQueries({ queryKey })) } async function invalidateReleaseMutationQueries( query: ConsoleQueryUtils, client: QueryClient, releaseId: string, appInstanceId?: string, options: { removeRelease?: boolean } = {}, ) { const releaseDetailQueryKey = releaseQueryKey(query, releaseId) if (options.removeRelease) { client.removeQueries({ queryKey: releaseDetailQueryKey, }) } else { await client.invalidateQueries({ queryKey: releaseDetailQueryKey, }) } if (appInstanceId) { return invalidateAppDeployQueries(query, client, appInstanceId, { accessChannels: false, accessSettings: false, developerApiSettings: false, }) } return invalidateQueryKeys(client, [ query.enterprise.appInstanceService.listAppInstances.key(), query.enterprise.appInstanceService.listAppInstanceSummaries.key(), query.enterprise.releaseService.listReleases.key({ type: 'query' }), query.enterprise.releaseService.listReleaseSummaries.key({ type: 'query' }), query.enterprise.releaseService.computeReleaseDeploymentView.key({ type: 'query' }), query.enterprise.appInstanceService.getAppInstance.key({ type: 'query' }), query.enterprise.appInstanceService.getAppInstanceOverview.key({ type: 'query' }), ]) } const consoleLink = createConsoleDynamicLink(createConsoleOpenAPILink) export const consoleClient: JsonifiedClient< ContractRouterClient > = createORPCClient(consoleLink) export const consoleQuery: RouterUtils = createTanstackQueryUtils( consoleClient, { path: ['console'], experimental_defaults: { apps: { byAppId: { workflows: { draft: { nodes: { byNodeId: { agentComposer: { put: { mutationOptions: { onSuccess: (composerState, variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.apps.byAppId.workflows.draft.nodes.byNodeId.agentComposer.get.queryKey( { input: { params: variables.params, }, }, ), composerState, ) }, }, }, copyFromRoster: { post: { mutationOptions: { onSuccess: (composerState, variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.apps.byAppId.workflows.draft.nodes.byNodeId.agentComposer.get.queryKey( { input: { params: variables.params, }, }, ), composerState, ) }, }, }, }, saveToRoster: { post: { mutationOptions: { onSuccess: (composerState, variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.apps.byAppId.workflows.draft.nodes.byNodeId.agentComposer.get.queryKey( { input: { params: variables.params, }, }, ), composerState, ) context.client.invalidateQueries({ queryKey: consoleQuery.agent.get.key(), }) context.client.invalidateQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) const agentId = composerState.binding?.binding_type === 'roster_agent' ? composerState.binding.agent_id : undefined if (agentId) { context.client.invalidateQueries({ queryKey: consoleQuery.agent.byAgentId.get.queryKey({ input: { params: { agent_id: agentId, }, }, }), }) } }, }, }, }, }, }, }, }, }, }, }, snippets: { bySnippetId: { workflows: { draft: { nodes: { byNodeId: { agentComposer: { put: { mutationOptions: { onSuccess: (composerState, variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.snippets.bySnippetId.workflows.draft.nodes.byNodeId.agentComposer.get.queryKey( { input: { params: variables.params, }, }, ), composerState, ) }, }, }, copyFromRoster: { post: { mutationOptions: { onSuccess: (composerState, variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.snippets.bySnippetId.workflows.draft.nodes.byNodeId.agentComposer.get.queryKey( { input: { params: variables.params, }, }, ), composerState, ) }, }, }, }, saveToRoster: { post: { mutationOptions: { onSuccess: (composerState, variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.snippets.bySnippetId.workflows.draft.nodes.byNodeId.agentComposer.get.queryKey( { input: { params: variables.params, }, }, ), composerState, ) context.client.invalidateQueries({ queryKey: consoleQuery.agent.get.key(), }) context.client.invalidateQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) const agentId = composerState.binding?.binding_type === 'roster_agent' ? composerState.binding.agent_id : undefined if (agentId) { context.client.invalidateQueries({ queryKey: consoleQuery.agent.byAgentId.get.queryKey({ input: { params: { agent_id: agentId, }, }, }), }) } }, }, }, }, }, }, }, }, }, }, }, agent: { post: { mutationOptions: { onSuccess: (_createdAgent, _variables, _onMutateResult, context) => { context.client.invalidateQueries({ queryKey: consoleQuery.agent.get.key(), }) context.client.invalidateQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) }, }, }, byAgentId: { get: { queryOptions: { retry: (failureCount, error) => { if (error instanceof Response && error.status === 404) return false return failureCount < 3 }, }, }, copy: { post: { mutationOptions: { onSuccess: (copiedAgent, _variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.agent.byAgentId.get.queryKey({ input: { params: { agent_id: copiedAgent.id, }, }, }), copiedAgent, ) context.client.invalidateQueries({ queryKey: consoleQuery.agent.get.key(), }) context.client.invalidateQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) }, }, }, }, put: { mutationOptions: { onSuccess: (updatedAgent, variables, _onMutateResult, context) => { context.client.setQueriesData( { queryKey: consoleQuery.agent.get.key({ type: 'query' }), }, (oldList: AgentAppPagination | undefined) => { if (!oldList?.data.some((item) => item.id === updatedAgent.id)) return oldList return { ...oldList, data: oldList.data.map((item) => item.id === updatedAgent.id ? updatedAgent : item, ), } }, ) context.client.setQueriesData( { queryKey: consoleQuery.agent.get.key({ type: 'infinite' }), }, (oldList: InfiniteData | undefined) => { if ( !oldList?.pages.some((page) => page.data.some((item) => item.id === updatedAgent.id), ) ) return oldList return { ...oldList, pages: oldList.pages.map((page) => ({ ...page, data: page.data.map((item) => item.id === updatedAgent.id ? updatedAgent : item, ), })), } }, ) context.client.setQueryData( consoleQuery.agent.byAgentId.get.queryKey({ input: { params: { agent_id: variables.params.agent_id, }, }, }), updatedAgent, ) context.client.invalidateQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) }, }, }, composer: { put: { mutationOptions: { onSuccess: (_composerState, variables, _onMutateResult, context) => { context.client.invalidateQueries({ queryKey: consoleQuery.agent.get.key(), }) if (variables.body.save_strategy !== 'save_as_new_version') return context.client.invalidateQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) context.client.removeQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) }, }, }, }, publish: { post: { mutationOptions: { onSuccess: (_publishResult, _variables, _onMutateResult, context) => { context.client.invalidateQueries({ queryKey: consoleQuery.agent.get.key(), }) context.client.invalidateQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) context.client.removeQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) }, }, }, }, delete: { mutationOptions: { onSuccess: (_data, variables, _onMutateResult, context) => { context.client.setQueriesData( { queryKey: consoleQuery.agent.get.key({ type: 'query' }), }, (oldList: AgentAppPagination | undefined) => { if (!oldList?.data.some((item) => item.id === variables.params.agent_id)) return oldList return { ...oldList, data: oldList.data.filter((item) => item.id !== variables.params.agent_id), total: Math.max(0, oldList.total - 1), } }, ) context.client.setQueriesData( { queryKey: consoleQuery.agent.get.key({ type: 'infinite' }), }, (oldList: InfiniteData | undefined) => { if ( !oldList?.pages.some((page) => page.data.some((item) => item.id === variables.params.agent_id), ) ) return oldList return { ...oldList, pages: oldList.pages.map((page) => { const total = Math.max(0, page.total - 1) return { ...page, data: page.data.filter((item) => item.id !== variables.params.agent_id), has_more: page.page * page.limit < total, total, } }), } }, ) context.client.invalidateQueries({ queryKey: consoleQuery.agent.get.key(), }) context.client.invalidateQueries({ queryKey: consoleQuery.agent.inviteOptions.get.key(), }) }, }, }, }, }, apiBasedExtension: { post: { mutationOptions: { onSuccess: (createdExtension, _variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.apiBasedExtension.get.queryKey(), (oldExtensions: ApiBasedExtensionResponse[] | undefined) => oldExtensions ? [createdExtension, ...oldExtensions] : oldExtensions, ) }, }, }, byId: { post: { mutationOptions: { onSuccess: (updatedExtension, variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.apiBasedExtension.get.queryKey(), (oldExtensions: ApiBasedExtensionResponse[] | undefined) => oldExtensions?.map((extension) => extension.id === variables.params.id ? updatedExtension : extension, ), ) }, }, }, delete: { mutationOptions: { onSuccess: (_data, variables, _onMutateResult, context) => { context.client.setQueryData( consoleQuery.apiBasedExtension.get.queryKey(), (oldExtensions: ApiBasedExtensionResponse[] | undefined) => oldExtensions?.filter((extension) => extension.id !== variables.params.id), ) }, }, }, }, }, tags: { post: { mutationOptions: { onSuccess: (tag, _variables, _onMutateResult, context) => { if (!isTagType(tag.type)) return context.client.setQueryData( consoleQuery.tags.get.queryKey({ input: { query: { type: tag.type, }, }, }), (oldTags: Tag[] | undefined) => (oldTags ? [tag, ...oldTags] : oldTags), ) }, }, }, byTagId: { patch: { mutationOptions: { onSuccess: (updatedTag, variables, _onMutateResult, context) => { context.client.setQueriesData( { queryKey: consoleQuery.tags.get.key({ type: 'query' }), }, (oldTags: Tag[] | undefined) => oldTags?.map((tag) => (tag.id === variables.params.tag_id ? updatedTag : tag)), ) }, }, }, delete: { mutationOptions: { onSuccess: (_data, variables, _onMutateResult, context) => { context.client.setQueriesData( { queryKey: consoleQuery.tags.get.key({ type: 'query' }), }, (oldTags: Tag[] | undefined) => oldTags?.filter((tag) => tag.id !== variables.params.tag_id), ) }, }, }, }, }, enterprise: { appInstanceService: { createAppInstance: { mutationOptions: { onSuccess: async (data, _variables, _result, context) => { const appInstanceId = data.appInstance?.id if (appInstanceId) { for (const delay of APP_DEPLOY_READINESS_RETRY_DELAYS) { if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay)) const listResponse = await context.client .fetchQuery( consoleQuery.enterprise.appInstanceService.listAppInstances.queryOptions({ input: { query: { pageNumber: 1, resultsPerPage: APP_DEPLOY_SOURCE_APPS_PAGE_SIZE, }, }, }), ) .catch(() => undefined) if (listResponse?.appInstances?.some((app) => app.id === appInstanceId)) break } } await context.client.invalidateQueries({ queryKey: consoleQuery.enterprise.appInstanceService.listAppInstances.key(), }) await context.client.invalidateQueries({ queryKey: consoleQuery.enterprise.appInstanceService.listAppInstanceSummaries.key(), }) }, }, }, updateAppInstance: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const appInstanceId = variables.params.appInstanceId return invalidateAppDeployQueries(consoleQuery, context.client, appInstanceId, { environmentDeployments: false, releases: false, accessChannels: false, }) }, }, }, deleteAppInstance: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const appInstanceId = variables.params.appInstanceId removeAppDeployQueries(consoleQuery, context.client, appInstanceId) return Promise.all([ context.client.invalidateQueries({ queryKey: consoleQuery.enterprise.appInstanceService.listAppInstances.key(), }), context.client.invalidateQueries({ queryKey: consoleQuery.enterprise.appInstanceService.listAppInstanceSummaries.key(), }), ]) }, }, }, }, releaseService: { createRelease: { mutationOptions: { onSuccess: (data, variables, _result, context) => { const appInstanceId = data.release?.appInstanceId ?? data.appInstance?.id ?? variables.body.appInstanceId const { dsl, sourceAppId } = variables.body if (!appInstanceId) { return Promise.all([ context.client.invalidateQueries({ queryKey: consoleQuery.enterprise.appInstanceService.listAppInstances.key(), }), context.client.invalidateQueries({ queryKey: consoleQuery.enterprise.appInstanceService.listAppInstanceSummaries.key(), }), ]) } const appDeployInvalidation = invalidateAppDeployQueries( consoleQuery, context.client, appInstanceId, { environmentDeployments: false, accessChannels: false, }, ) if (!dsl && !sourceAppId) return appDeployInvalidation return Promise.all([ appDeployInvalidation, context.client.invalidateQueries({ queryKey: precheckReleaseQueryKey(consoleQuery, { appInstanceId, ...(dsl ? { dsl } : { sourceAppId }), }), }), ]) }, }, }, deleteRelease: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const releaseId = variables.params.releaseId const appInstanceId = cachedReleaseAppInstanceId( consoleQuery, context.client, releaseId, ) return invalidateReleaseMutationQueries( consoleQuery, context.client, releaseId, appInstanceId, { removeRelease: true, }, ) }, }, }, updateRelease: { mutationOptions: { onSuccess: (data, variables, _result, context) => { const releaseId = variables.params.releaseId const appInstanceId = data.release?.appInstanceId ?? cachedReleaseAppInstanceId(consoleQuery, context.client, releaseId) return invalidateReleaseMutationQueries( consoleQuery, context.client, releaseId, appInstanceId, ) }, }, }, }, deploymentService: { deploy: { mutationOptions: { onSuccess: (data, _variables, _result, context) => { // Deploy always creates a new AppInstance, so the reply carries it. const appInstanceId = data.appInstance?.id ?? data.release?.appInstanceId if (!appInstanceId) { return Promise.all([ context.client.invalidateQueries({ queryKey: consoleQuery.enterprise.appInstanceService.listAppInstances.key(), }), context.client.invalidateQueries({ queryKey: consoleQuery.enterprise.appInstanceService.listAppInstanceSummaries.key(), }), ]) } return invalidateAppDeployQueries(consoleQuery, context.client, appInstanceId) }, }, }, cancelDeployment: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const appInstanceId = variables.params.appInstanceId return invalidateAppDeployQueries(consoleQuery, context.client, appInstanceId) }, }, }, promote: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const appInstanceId = variables.params.appInstanceId return invalidateAppDeployQueries(consoleQuery, context.client, appInstanceId) }, }, }, rollback: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const appInstanceId = variables.params.appInstanceId return invalidateAppDeployQueries(consoleQuery, context.client, appInstanceId) }, }, }, undeploy: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const appInstanceId = variables.params.appInstanceId return invalidateAppDeployQueries(consoleQuery, context.client, appInstanceId) }, }, }, }, accessService: { createApiKey: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const appInstanceId = variables.params.appInstanceId const environmentId = variables.params.environmentId return invalidateQueryKeys(context.client, [ appInstanceQueryKey(consoleQuery, appInstanceId), appInstanceOverviewQueryKey(consoleQuery, appInstanceId), consoleQuery.enterprise.appInstanceService.listAppInstanceSummaries.key(), accessChannelsQueryKey(consoleQuery, appInstanceId), developerApiSettingsQueryKey(consoleQuery, appInstanceId), apiKeysQueryKey(consoleQuery, appInstanceId, environmentId), ]) }, }, }, deleteApiKey: { mutationOptions: { onSuccess: (_data, _variables, _result, context) => { return invalidateQueryKeys(context.client, [ consoleQuery.enterprise.accessService.listApiKeys.key({ type: 'query' }), consoleQuery.enterprise.accessService.getDeveloperApiSettings.key({ type: 'query', }), consoleQuery.enterprise.appInstanceService.getAppInstanceOverview.key({ type: 'query', }), consoleQuery.enterprise.appInstanceService.listAppInstanceSummaries.key(), ]) }, }, }, updateAccessChannels: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const appInstanceId = variables.params.appInstanceId return invalidateAppDeployQueries(consoleQuery, context.client, appInstanceId, { environmentDeployments: false, releases: false, }) }, }, }, updateAccessPolicy: { mutationOptions: { onSuccess: (_data, variables, _result, context) => { const { appInstanceId, environmentId } = variables.params return invalidateQueryKeys(context.client, [ accessPolicyQueryKey(consoleQuery, appInstanceId, environmentId), accessChannelsQueryKey(consoleQuery, appInstanceId), accessSettingsQueryKey(consoleQuery, appInstanceId), ]) }, }, }, }, }, }, }, )