diff --git a/apis/api-gateway/schema.graphql b/apis/api-gateway/schema.graphql index 9b3c404990d..c6cc96b4654 100644 --- a/apis/api-gateway/schema.graphql +++ b/apis/api-gateway/schema.graphql @@ -425,6 +425,8 @@ type Mutation @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS """ timezone: String ): JourneyVisitorGoogleSheetExportResult! @join__field(graph: API_JOURNEYS_MODERN) + userDeleteJourneysCheck(userId: String!) : UserDeleteJourneysCheckResult! @join__field(graph: API_JOURNEYS_MODERN) + userDeleteJourneysConfirm(userId: String!) : UserDeleteJourneysConfirmResult! @join__field(graph: API_JOURNEYS_MODERN) audioPreviewCreate(input: MutationAudioPreviewCreateInput!) : AudioPreview! @join__field(graph: API_LANGUAGES) audioPreviewUpdate(input: MutationAudioPreviewUpdateInput!) : AudioPreview! @join__field(graph: API_LANGUAGES) audioPreviewDelete(languageId: ID!) : AudioPreview! @join__field(graph: API_LANGUAGES) @@ -554,6 +556,7 @@ type Mutation @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS userImpersonate(email: String!) : String @join__field(graph: API_USERS) createVerificationRequest(input: CreateVerificationRequestInput) : Boolean @join__field(graph: API_USERS) validateEmail(email: String!, token: String!) : AuthenticatedUser @join__field(graph: API_USERS) + userDeleteCheck(idType: UserDeleteIdType!, id: String!) : UserDeleteCheckResult! @join__field(graph: API_USERS) } type MutationSiteCreateSuccess @join__type(graph: API_ANALYTICS) { @@ -2523,8 +2526,9 @@ type PlausibleStatsResponse @join__type(graph: API_JOURNEYS_MODERN) { timeOnPage: Float } -type Subscription @join__type(graph: API_JOURNEYS_MODERN) { - journeyAiTranslateCreateSubscription(input: JourneyAiTranslateInput!) : JourneyAiTranslateProgress! +type Subscription @join__type(graph: API_JOURNEYS_MODERN) @join__type(graph: API_USERS) { + journeyAiTranslateCreateSubscription(input: JourneyAiTranslateInput!) : JourneyAiTranslateProgress! @join__field(graph: API_JOURNEYS_MODERN) + userDeleteConfirm(idType: UserDeleteIdType!, id: String!) : UserDeleteConfirmProgress! @join__field(graph: API_USERS) } type TemplateFamilyStatsAggregateResponse @join__type(graph: API_JOURNEYS_MODERN) { @@ -2546,6 +2550,31 @@ type TemplateFamilyStatsEventResponse @join__type(graph: API_JOURNEYS_MODERN) { visitors: Int! } +type UserDeleteJourneysCheckResult @join__type(graph: API_JOURNEYS_MODERN) { + journeysToDelete: Int! + journeysToTransfer: Int! + journeysToRemove: Int! + teamsToDelete: Int! + teamsToTransfer: Int! + teamsToRemove: Int! + logs: [UserDeleteJourneysLogEntry!]! +} + +type UserDeleteJourneysConfirmResult @join__type(graph: API_JOURNEYS_MODERN) { + success: Boolean! + deletedJourneyIds: [String!]! + deletedTeamIds: [String!]! + deletedUserJourneyIds: [String!]! + deletedUserTeamIds: [String!]! + logs: [UserDeleteJourneysLogEntry!]! +} + +type UserDeleteJourneysLogEntry @join__type(graph: API_JOURNEYS_MODERN) { + message: String! + level: String! + timestamp: String! +} + type YouTube @join__type(graph: API_JOURNEYS_MODERN, key: "id primaryLanguageId", extension: true) { id: ID! primaryLanguageId: ID @@ -3288,6 +3317,31 @@ type AnonymousUser implements User @join__type(graph: API_USERS, key: "id") @jo id: ID! } +type UserDeleteCheckResult @join__type(graph: API_USERS) { + userId: String! + userEmail: String + userFirstName: String! + journeysToDelete: Int! + journeysToTransfer: Int! + journeysToRemove: Int! + teamsToDelete: Int! + teamsToTransfer: Int! + teamsToRemove: Int! + logs: [UserDeleteLogEntry!]! +} + +type UserDeleteConfirmProgress @join__type(graph: API_USERS) { + log: UserDeleteLogEntry! + done: Boolean! + success: Boolean +} + +type UserDeleteLogEntry @join__type(graph: API_USERS) { + message: String! + level: String! + timestamp: String! +} + interface BaseError @join__type(graph: API_ANALYTICS) @join__type(graph: API_MEDIA) { message: String } @@ -3889,6 +3943,11 @@ enum App @join__type(graph: API_USERS) { JesusFilmOne @join__enumValue(graph: API_USERS) } +enum UserDeleteIdType @join__type(graph: API_USERS) { + databaseId @join__enumValue(graph: API_USERS) + email @join__enumValue(graph: API_USERS) +} + input SiteCreateInput @join__type(graph: API_ANALYTICS) { domain: String! goals: [String!] diff --git a/apis/api-journeys-modern/schema.graphql b/apis/api-journeys-modern/schema.graphql index 1d2515304bf..5980d187994 100644 --- a/apis/api-journeys-modern/schema.graphql +++ b/apis/api-journeys-modern/schema.graphql @@ -1268,6 +1268,9 @@ enum MessagePlatform { checkBroken checkContained settings + discord + signal + weChat } type MultiselectBlock implements Block @@ -1406,6 +1409,8 @@ type Mutation { """ timezone: String ): JourneyVisitorGoogleSheetExportResult! + userDeleteJourneysCheck(userId: String!): UserDeleteJourneysCheckResult! + userDeleteJourneysConfirm(userId: String!): UserDeleteJourneysConfirmResult! } input MutationJourneyLanguageAiDetectInput { @@ -2288,6 +2293,31 @@ type UserAgent os: OperatingSystem! } +type UserDeleteJourneysCheckResult { + journeysToDelete: Int! + journeysToTransfer: Int! + journeysToRemove: Int! + teamsToDelete: Int! + teamsToTransfer: Int! + teamsToRemove: Int! + logs: [UserDeleteJourneysLogEntry!]! +} + +type UserDeleteJourneysConfirmResult { + success: Boolean! + deletedJourneyIds: [String!]! + deletedTeamIds: [String!]! + deletedUserJourneyIds: [String!]! + deletedUserTeamIds: [String!]! + logs: [UserDeleteJourneysLogEntry!]! +} + +type UserDeleteJourneysLogEntry { + message: String! + level: String! + timestamp: String! +} + type UserInvite @key(fields: "id") @shareable diff --git a/apis/api-journeys-modern/src/schema/schema.ts b/apis/api-journeys-modern/src/schema/schema.ts index 509d23220cc..d0310336ba5 100644 --- a/apis/api-journeys-modern/src/schema/schema.ts +++ b/apis/api-journeys-modern/src/schema/schema.ts @@ -25,6 +25,7 @@ import './plausible' import './qrCode' import './team' import './user' +import './userDelete' import './userInvite' import './userJourney' import './userRole' diff --git a/apis/api-journeys-modern/src/schema/userDelete/index.ts b/apis/api-journeys-modern/src/schema/userDelete/index.ts new file mode 100644 index 00000000000..15b19535cc4 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userDelete/index.ts @@ -0,0 +1,2 @@ +import './userDeleteJourneysCheck' +import './userDeleteJourneysConfirm' diff --git a/apis/api-journeys-modern/src/schema/userDelete/service/checkJourneysData.ts b/apis/api-journeys-modern/src/schema/userDelete/service/checkJourneysData.ts new file mode 100644 index 00000000000..e0007be5775 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userDelete/service/checkJourneysData.ts @@ -0,0 +1,171 @@ +import { prisma } from '@core/prisma/journeys/client' + +import { LogEntry, createLog } from './types' + +export interface CheckJourneysDataResult { + journeysToDelete: number + journeysToTransfer: number + journeysToRemove: number + teamsToDelete: number + teamsToTransfer: number + teamsToRemove: number + logs: LogEntry[] +} + +export async function checkJourneysData( + userId: string +): Promise { + const logs: LogEntry[] = [] + + // Check journeys + const userJourneys = await prisma.userJourney.findMany({ + where: { userId }, + include: { + journey: { + select: { + id: true, + title: true, + userJourneys: { + select: { id: true, userId: true, role: true } + } + } + } + } + }) + + logs.push(createLog(`📂 ${userJourneys.length} journeys found`)) + + let journeysToDelete = 0 + let journeysToTransfer = 0 + let journeysToRemove = 0 + + for (const uj of userJourneys) { + const others = uj.journey.userJourneys.filter((j) => j.userId !== userId) + if (others.length === 0) { + journeysToDelete++ + } else if (uj.role === 'owner') { + journeysToTransfer++ + } else { + journeysToRemove++ + } + } + + if (journeysToDelete > 0) + logs.push( + createLog( + `${journeysToDelete} journeys will be deleted (user is sole accessor)` + ) + ) + if (journeysToTransfer > 0) + logs.push( + createLog(`${journeysToTransfer} journey ownerships will be transferred`) + ) + if (journeysToRemove > 0) + logs.push( + createLog(`${journeysToRemove} journey memberships will be removed`) + ) + + // Check teams + const userTeams = await prisma.userTeam.findMany({ + where: { userId }, + include: { + team: { + select: { + id: true, + title: true, + userTeams: { + select: { id: true, userId: true, role: true } + } + } + } + } + }) + + logs.push(createLog(`👥 ${userTeams.length} teams found`)) + + let teamsToDelete = 0 + let teamsToTransfer = 0 + let teamsToRemove = 0 + + for (const ut of userTeams) { + const others = ut.team.userTeams.filter((t) => t.userId !== userId) + if (others.length === 0) { + teamsToDelete++ + } else if (ut.role === 'manager') { + teamsToTransfer++ + } else { + teamsToRemove++ + } + } + + if (teamsToDelete > 0) + logs.push( + createLog(`${teamsToDelete} teams will be deleted (user is sole member)`) + ) + if (teamsToTransfer > 0) + logs.push( + createLog(`${teamsToTransfer} team manager roles will be transferred`) + ) + if (teamsToRemove > 0) + logs.push(createLog(`${teamsToRemove} team memberships will be removed`)) + + // Check related records + const [ + userRole, + journeyProfile, + integration, + visitor, + journeyNotification, + userTeamInvite, + userInvite, + journeyEventsExportLog, + journeyTheme + ] = await Promise.all([ + prisma.userRole.count({ where: { userId } }), + prisma.journeyProfile.count({ where: { userId } }), + prisma.integration.count({ where: { userId } }), + prisma.visitor.count({ where: { userId } }), + prisma.journeyNotification.count({ where: { userId } }), + prisma.userTeamInvite.count({ + where: { OR: [{ senderId: userId }, { receipientId: userId }] } + }), + prisma.userInvite.count({ where: { senderId: userId } }), + prisma.journeyEventsExportLog.count({ where: { userId } }), + prisma.journeyTheme.count({ where: { userId } }) + ]) + + const tablesToClean: string[] = [] + if (userRole > 0) tablesToClean.push(`UserRole(${userRole})`) + if (journeyProfile > 0) + tablesToClean.push(`JourneyProfile(${journeyProfile})`) + if (integration > 0) tablesToClean.push(`Integration(${integration})`) + if (visitor > 0) tablesToClean.push(`Visitor(${visitor})`) + if (journeyNotification > 0) + tablesToClean.push(`JourneyNotification(${journeyNotification})`) + if (userTeamInvite > 0) + tablesToClean.push(`UserTeamInvite(${userTeamInvite})`) + if (userInvite > 0) tablesToClean.push(`UserInvite(${userInvite})`) + if (journeyEventsExportLog > 0) + tablesToClean.push(`JourneyEventsExportLog(${journeyEventsExportLog})`) + if (journeyTheme > 0) tablesToClean.push(`JourneyTheme(${journeyTheme})`) + + if (tablesToClean.length > 0) { + logs.push( + createLog(`Related records to clean up: ${tablesToClean.join(', ')}`) + ) + } else { + logs.push(createLog('✨ No additional related records found')) + } + + logs.push(createLog('✅ Check complete. Ready for deletion confirmation.')) + + return { + journeysToDelete, + journeysToTransfer, + journeysToRemove, + teamsToDelete, + teamsToTransfer, + teamsToRemove, + logs + } +} diff --git a/apis/api-journeys-modern/src/schema/userDelete/service/deleteJourneysData.ts b/apis/api-journeys-modern/src/schema/userDelete/service/deleteJourneysData.ts new file mode 100644 index 00000000000..73bb893f1c1 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userDelete/service/deleteJourneysData.ts @@ -0,0 +1,265 @@ +import { prisma } from '@core/prisma/journeys/client' + +import { LogEntry, createLog } from './types' + +export interface DeleteJourneysDataResult { + success: boolean + deletedJourneyIds: string[] + deletedTeamIds: string[] + deletedUserJourneyIds: string[] + deletedUserTeamIds: string[] + logs: LogEntry[] +} + +export async function deleteJourneysData( + userId: string +): Promise { + const logs: LogEntry[] = [] + const deletedUserJourneyIds: string[] = [] + const deletedUserTeamIds: string[] = [] + const journeyIdsToDelete: string[] = [] + const teamIdsToDelete: string[] = [] + + try { + // Phase 1: Analyze and transfer ownership (lightweight, use transaction) + await prisma.$transaction(async (tx) => { + const userJourneys = await tx.userJourney.findMany({ + where: { userId }, + include: { + journey: { + select: { + id: true, + title: true, + userJourneys: { + select: { id: true, userId: true, role: true } + } + } + } + } + }) + + for (const uj of userJourneys) { + const others = uj.journey.userJourneys.filter( + (j) => j.userId !== userId + ) + if (others.length === 0) { + journeyIdsToDelete.push(uj.journey.id) + } else if (uj.role === 'owner') { + const nextOwner = others.find((o) => o.role === 'editor') ?? others[0] + if (nextOwner.role === 'owner') { + logs.push( + createLog( + `Journey "${uj.journey.title}" already has another owner (${nextOwner.userId}), skipping transfer` + ) + ) + } else { + await tx.userJourney.updateMany({ + where: { + journeyId: uj.journey.id, + userId: nextOwner.userId + }, + data: { role: 'owner' } + }) + logs.push( + createLog( + `🔄 Transferred ownership of journey "${uj.journey.title}" to user ${nextOwner.userId}` + ) + ) + } + } + } + + const userTeams = await tx.userTeam.findMany({ + where: { userId }, + include: { + team: { + select: { + id: true, + title: true, + userTeams: { + select: { id: true, userId: true, role: true } + } + } + } + } + }) + + for (const ut of userTeams) { + const others = ut.team.userTeams.filter((t) => t.userId !== userId) + if (others.length === 0) { + teamIdsToDelete.push(ut.team.id) + } else if (ut.role === 'manager') { + const existingManager = others.find((o) => o.role === 'manager') + if (existingManager != null) { + logs.push( + createLog( + `Team "${ut.team.title}" already has another manager (${existingManager.userId}), skipping transfer` + ) + ) + } else { + const nextManager = others[0] + await tx.userTeam.updateMany({ + where: { + teamId: ut.team.id, + userId: nextManager.userId + }, + data: { role: 'manager' } + }) + logs.push( + createLog( + `🔄 Transferred manager role of team "${ut.team.title}" to user ${nextManager.userId}` + ) + ) + } + } + } + }) + logs.push(createLog('✅ Ownership transfers completed')) + + // Phase 2: Remove user memberships + const ujRecords = await prisma.userJourney.findMany({ + where: { userId }, + select: { id: true } + }) + deletedUserJourneyIds.push(...ujRecords.map((r) => r.id)) + await prisma.userJourney.deleteMany({ where: { userId } }) + logs.push( + createLog(`🗑️ Removed ${ujRecords.length} user-journey memberships`) + ) + + const utRecords = await prisma.userTeam.findMany({ + where: { userId }, + select: { id: true } + }) + deletedUserTeamIds.push(...utRecords.map((r) => r.id)) + await prisma.userTeam.deleteMany({ where: { userId } }) + logs.push(createLog(`🗑️ Removed ${utRecords.length} user-team memberships`)) + + // Phase 3: Delete sole-accessor journeys + if (journeyIdsToDelete.length > 0) { + // Pre-delete heavy child records to avoid slow cascade deletes + const eventCount = await prisma.event.deleteMany({ + where: { journeyId: { in: journeyIdsToDelete } } + }) + if (eventCount.count > 0) + logs.push(createLog(`🗑️ Deleted ${eventCount.count} events`)) + + const journeyVisitorCount = await prisma.journeyVisitor.deleteMany({ + where: { journeyId: { in: journeyIdsToDelete } } + }) + if (journeyVisitorCount.count > 0) + logs.push( + createLog(`🗑️ Deleted ${journeyVisitorCount.count} journey visitors`) + ) + + const actionCount = await prisma.action.deleteMany({ + where: { journeyId: { in: journeyIdsToDelete } } + }) + if (actionCount.count > 0) + logs.push(createLog(`🗑️ Deleted ${actionCount.count} actions`)) + + const blockCount = await prisma.block.deleteMany({ + where: { journeyId: { in: journeyIdsToDelete } } + }) + if (blockCount.count > 0) + logs.push(createLog(`🗑️ Deleted ${blockCount.count} blocks`)) + + // Now delete the journeys (cascades are already cleared) + for (const journeyId of journeyIdsToDelete) { + await prisma.journey.delete({ where: { id: journeyId } }) + } + logs.push(createLog(`🗑️ Deleted ${journeyIdsToDelete.length} journeys`)) + } + + // Phase 4: Delete sole-member teams + if (teamIdsToDelete.length > 0) { + await prisma.team.deleteMany({ + where: { id: { in: teamIdsToDelete } } + }) + logs.push(createLog(`🗑️ Deleted ${teamIdsToDelete.length} teams`)) + } + + // Phase 5: Clean up related records + const journeyNotifications = await prisma.journeyNotification.deleteMany({ + where: { userId } + }) + if (journeyNotifications.count > 0) + logs.push( + createLog(`Deleted ${journeyNotifications.count} journey notifications`) + ) + + const userTeamInvites = await prisma.userTeamInvite.deleteMany({ + where: { + OR: [{ senderId: userId }, { receipientId: userId }] + } + }) + if (userTeamInvites.count > 0) + logs.push(createLog(`Deleted ${userTeamInvites.count} team invites`)) + + const userInvites = await prisma.userInvite.deleteMany({ + where: { senderId: userId } + }) + if (userInvites.count > 0) + logs.push(createLog(`Deleted ${userInvites.count} journey invites`)) + + const exportLogs = await prisma.journeyEventsExportLog.deleteMany({ + where: { userId } + }) + if (exportLogs.count > 0) + logs.push(createLog(`Deleted ${exportLogs.count} export logs`)) + + const journeyThemes = await prisma.journeyTheme.deleteMany({ + where: { userId } + }) + if (journeyThemes.count > 0) + logs.push(createLog(`Deleted ${journeyThemes.count} journey themes`)) + + const journeyProfile = await prisma.journeyProfile.deleteMany({ + where: { userId } + }) + if (journeyProfile.count > 0) + logs.push(createLog(`Deleted ${journeyProfile.count} journey profile`)) + + const integrations = await prisma.integration.deleteMany({ + where: { userId } + }) + if (integrations.count > 0) + logs.push(createLog(`Deleted ${integrations.count} integrations`)) + + const userRoles = await prisma.userRole.deleteMany({ + where: { userId } + }) + if (userRoles.count > 0) + logs.push(createLog(`Deleted ${userRoles.count} user roles`)) + + const visitors = await prisma.visitor.deleteMany({ + where: { userId } + }) + if (visitors.count > 0) + logs.push(createLog(`Deleted ${visitors.count} visitor records`)) + + logs.push(createLog('✅ Journeys database cleanup completed')) + + return { + success: true, + deletedJourneyIds: journeyIdsToDelete, + deletedTeamIds: teamIdsToDelete, + deletedUserJourneyIds, + deletedUserTeamIds, + logs + } + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error' + logs.push( + createLog(`❌ Journeys database cleanup failed: ${message}`, 'error') + ) + return { + success: false, + deletedJourneyIds: [], + deletedTeamIds: [], + deletedUserJourneyIds: [], + deletedUserTeamIds: [], + logs + } + } +} diff --git a/apis/api-journeys-modern/src/schema/userDelete/service/index.ts b/apis/api-journeys-modern/src/schema/userDelete/service/index.ts new file mode 100644 index 00000000000..2a41f425bef --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userDelete/service/index.ts @@ -0,0 +1,6 @@ +export { checkJourneysData } from './checkJourneysData' +export type { CheckJourneysDataResult } from './checkJourneysData' +export { deleteJourneysData } from './deleteJourneysData' +export type { DeleteJourneysDataResult } from './deleteJourneysData' +export type { LogEntry } from './types' +export { createLog } from './types' diff --git a/apis/api-journeys-modern/src/schema/userDelete/service/types.ts b/apis/api-journeys-modern/src/schema/userDelete/service/types.ts new file mode 100644 index 00000000000..5c6c6367b67 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userDelete/service/types.ts @@ -0,0 +1,12 @@ +export interface LogEntry { + message: string + level: 'info' | 'warn' | 'error' + timestamp: string +} + +export function createLog( + message: string, + level: 'info' | 'warn' | 'error' = 'info' +): LogEntry { + return { message, level, timestamp: new Date().toISOString() } +} diff --git a/apis/api-journeys-modern/src/schema/userDelete/types.ts b/apis/api-journeys-modern/src/schema/userDelete/types.ts new file mode 100644 index 00000000000..fe1e4515fb7 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userDelete/types.ts @@ -0,0 +1,2 @@ +export type { LogEntry } from './service' +export { createLog } from './service' diff --git a/apis/api-journeys-modern/src/schema/userDelete/userDeleteJourneysCheck.ts b/apis/api-journeys-modern/src/schema/userDelete/userDeleteJourneysCheck.ts new file mode 100644 index 00000000000..01553ed2ee3 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userDelete/userDeleteJourneysCheck.ts @@ -0,0 +1,61 @@ +import { builder } from '../builder' + +import { type LogEntry, checkJourneysData } from './service' + +const UserDeleteJourneysLogEntry = builder.objectRef( + 'UserDeleteJourneysLogEntry' +) + +builder.objectType(UserDeleteJourneysLogEntry, { + fields: (t) => ({ + message: t.exposeString('message', { nullable: false }), + level: t.exposeString('level', { nullable: false }), + timestamp: t.exposeString('timestamp', { nullable: false }) + }) +}) + +interface UserDeleteJourneysCheckResultShape { + journeysToDelete: number + journeysToTransfer: number + journeysToRemove: number + teamsToDelete: number + teamsToTransfer: number + teamsToRemove: number + logs: LogEntry[] +} + +const UserDeleteJourneysCheckResult = + builder.objectRef( + 'UserDeleteJourneysCheckResult' + ) + +builder.objectType(UserDeleteJourneysCheckResult, { + fields: (t) => ({ + journeysToDelete: t.exposeInt('journeysToDelete', { nullable: false }), + journeysToTransfer: t.exposeInt('journeysToTransfer', { nullable: false }), + journeysToRemove: t.exposeInt('journeysToRemove', { nullable: false }), + teamsToDelete: t.exposeInt('teamsToDelete', { nullable: false }), + teamsToTransfer: t.exposeInt('teamsToTransfer', { nullable: false }), + teamsToRemove: t.exposeInt('teamsToRemove', { nullable: false }), + logs: t.field({ + type: [UserDeleteJourneysLogEntry], + nullable: false, + resolve: (parent) => parent.logs + }) + }) +}) + +export { UserDeleteJourneysLogEntry } + +builder.mutationField('userDeleteJourneysCheck', (t) => + t.withAuth({ isValidInterop: true }).field({ + type: UserDeleteJourneysCheckResult, + nullable: false, + args: { + userId: t.arg.string({ required: true }) + }, + resolve: async (_parent, { userId }) => { + return await checkJourneysData(userId) + } + }) +) diff --git a/apis/api-journeys-modern/src/schema/userDelete/userDeleteJourneysConfirm.ts b/apis/api-journeys-modern/src/schema/userDelete/userDeleteJourneysConfirm.ts new file mode 100644 index 00000000000..9f781c86653 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userDelete/userDeleteJourneysConfirm.ts @@ -0,0 +1,52 @@ +import { builder } from '../builder' + +import { type LogEntry, deleteJourneysData } from './service' +import { UserDeleteJourneysLogEntry } from './userDeleteJourneysCheck' + +interface UserDeleteJourneysConfirmResultShape { + success: boolean + deletedJourneyIds: string[] + deletedTeamIds: string[] + deletedUserJourneyIds: string[] + deletedUserTeamIds: string[] + logs: LogEntry[] +} + +const UserDeleteJourneysConfirmResult = + builder.objectRef( + 'UserDeleteJourneysConfirmResult' + ) + +builder.objectType(UserDeleteJourneysConfirmResult, { + fields: (t) => ({ + success: t.exposeBoolean('success', { nullable: false }), + deletedJourneyIds: t.exposeStringList('deletedJourneyIds', { + nullable: false + }), + deletedTeamIds: t.exposeStringList('deletedTeamIds', { nullable: false }), + deletedUserJourneyIds: t.exposeStringList('deletedUserJourneyIds', { + nullable: false + }), + deletedUserTeamIds: t.exposeStringList('deletedUserTeamIds', { + nullable: false + }), + logs: t.field({ + type: [UserDeleteJourneysLogEntry], + nullable: false, + resolve: (parent) => parent.logs + }) + }) +}) + +builder.mutationField('userDeleteJourneysConfirm', (t) => + t.withAuth({ isValidInterop: true }).field({ + type: UserDeleteJourneysConfirmResult, + nullable: false, + args: { + userId: t.arg.string({ required: true }) + }, + resolve: async (_parent, { userId }) => { + return await deleteJourneysData(userId) + } + }) +) diff --git a/apis/api-users/infrastructure/locals.tf b/apis/api-users/infrastructure/locals.tf index 49f4247bc03..98fc8cfd5de 100644 --- a/apis/api-users/infrastructure/locals.tf +++ b/apis/api-users/infrastructure/locals.tf @@ -5,6 +5,7 @@ locals { "AWS_SECRET_ACCESS_KEY", "EXAMPLE_EMAIL_TOKEN", "GATEWAY_HMAC_SECRET", + "GATEWAY_URL", "GOOGLE_APPLICATION_JSON", "INTEROP_TOKEN", "JESUS_FILM_PROJECT_VERIFY_URL", diff --git a/apis/api-users/schema.graphql b/apis/api-users/schema.graphql index 0696bea15b8..d9c238e9b4c 100644 --- a/apis/api-users/schema.graphql +++ b/apis/api-users/schema.graphql @@ -39,6 +39,7 @@ type Mutation { userImpersonate(email: String!): String createVerificationRequest(input: CreateVerificationRequestInput): Boolean validateEmail(email: String!, token: String!): AuthenticatedUser + userDeleteCheck(idType: UserDeleteIdType!, id: String!): UserDeleteCheckResult! } type Query { @@ -47,8 +48,42 @@ type Query { userByEmail(email: String!): AuthenticatedUser } +type Subscription { + userDeleteConfirm(idType: UserDeleteIdType!, id: String!): UserDeleteConfirmProgress! +} + interface User @key(fields: "id") { id: ID! +} + +type UserDeleteCheckResult { + userId: String! + userEmail: String + userFirstName: String! + journeysToDelete: Int! + journeysToTransfer: Int! + journeysToRemove: Int! + teamsToDelete: Int! + teamsToTransfer: Int! + teamsToRemove: Int! + logs: [UserDeleteLogEntry!]! +} + +type UserDeleteConfirmProgress { + log: UserDeleteLogEntry! + done: Boolean! + success: Boolean +} + +enum UserDeleteIdType { + databaseId + email +} + +type UserDeleteLogEntry { + message: String! + level: String! + timestamp: String! } \ No newline at end of file diff --git a/apis/api-users/src/schema/builder.ts b/apis/api-users/src/schema/builder.ts index 93565780fa3..2d61bf327f7 100644 --- a/apis/api-users/src/schema/builder.ts +++ b/apis/api-users/src/schema/builder.ts @@ -74,6 +74,14 @@ export const builder = new SchemaBuilder<{ const user = await prisma.user.findUnique({ where: { userId: context.currentUser.id } }) + console.log( + '[authScopes] type=%s firebaseUid=%s email=%s dbUser=%s superAdmin=%s', + context.type, + context.currentUser.id, + context.currentUser.email ?? 'null', + user != null ? user.id : 'NOT_FOUND', + user?.superAdmin ?? 'N/A' + ) return { isAuthenticated: context.currentUser?.email != null, isAnonymous: @@ -90,6 +98,10 @@ export const builder = new SchemaBuilder<{ isValidInterop: true } default: + console.log( + '[authScopes] context type=%s — falling through to public', + context.type + ) return { isAuthenticated: false, isAnonymous: false, @@ -112,3 +124,4 @@ export const builder = new SchemaBuilder<{ builder.queryType({}) builder.mutationType({}) +builder.subscriptionType({}) diff --git a/apis/api-users/src/schema/schema.ts b/apis/api-users/src/schema/schema.ts index 312bf6e9b3e..21f3205763e 100644 --- a/apis/api-users/src/schema/schema.ts +++ b/apis/api-users/src/schema/schema.ts @@ -2,6 +2,7 @@ // and object type in the schema import './user' +import './userDelete' import { builder } from './builder' diff --git a/apis/api-users/src/schema/userDelete/index.ts b/apis/api-users/src/schema/userDelete/index.ts new file mode 100644 index 00000000000..dc083fb2ec6 --- /dev/null +++ b/apis/api-users/src/schema/userDelete/index.ts @@ -0,0 +1,2 @@ +import './userDeleteCheck' +import './userDeleteConfirm' diff --git a/apis/api-users/src/schema/userDelete/service/deleteUserData.ts b/apis/api-users/src/schema/userDelete/service/deleteUserData.ts new file mode 100644 index 00000000000..03a51849f27 --- /dev/null +++ b/apis/api-users/src/schema/userDelete/service/deleteUserData.ts @@ -0,0 +1,173 @@ +import { prisma } from '@core/prisma/users/client' +import { auth } from '@core/yoga/firebaseClient' + +import { LogEntry, createLog } from './types' + +interface DeleteUserDataInput { + userDbId: string + firebaseUserId: string + firebaseUidOverride: string | null + userEmail: string | null + callerUserId: string + callerEmail: string | null + deletedJourneyIds: string[] + deletedTeamIds: string[] + deletedUserJourneyIds: string[] + deletedUserTeamIds: string[] +} + +interface DeleteUserDataResult { + success: boolean + logs: LogEntry[] +} + +function isFirebaseNotFound(error: unknown): boolean { + return ( + error != null && + typeof error === 'object' && + 'code' in error && + error.code === 'auth/user-not-found' + ) +} + +export async function deleteFirebaseUser( + firebaseUserId: string, + firebaseUidOverride: string | null, + email: string | null +): Promise { + const logs: LogEntry[] = [] + const uidsToTry = new Set() + + // Primary UID from the DB + uidsToTry.add(firebaseUserId) + + // Override UID from the lookup's email-based Firebase check + if (firebaseUidOverride != null) { + uidsToTry.add(firebaseUidOverride) + } + + let anyDeleted = false + + for (const uid of uidsToTry) { + try { + await auth.deleteUser(uid) + logs.push(createLog(`🔥 Firebase auth record deleted (UID: ${uid})`)) + anyDeleted = true + } catch (error) { + if (isFirebaseNotFound(error)) { + logs.push(createLog(`⚠️ No Firebase auth record for UID: ${uid}`)) + } else { + const message = error instanceof Error ? error.message : 'Unknown error' + logs.push( + createLog( + `❌ Failed to delete Firebase auth record (UID: ${uid}): ${message}`, + 'error' + ) + ) + return logs + } + } + } + + // Final safety check: if nothing was deleted by UID, try by email + if (!anyDeleted && email != null) { + try { + const fbUser = await auth.getUserByEmail(email) + await auth.deleteUser(fbUser.uid) + logs.push( + createLog( + `🔥 Firebase auth record deleted via email fallback (UID: ${fbUser.uid})` + ) + ) + } catch (error) { + if (isFirebaseNotFound(error)) { + logs.push(createLog('🔥 No Firebase auth record found by email either')) + } else { + const message = error instanceof Error ? error.message : 'Unknown error' + logs.push( + createLog( + `❌ Failed to delete Firebase auth via email fallback: ${message}`, + 'error' + ) + ) + return logs + } + } + } + + return logs +} + +export async function deleteUserData( + input: DeleteUserDataInput +): Promise { + const logs: LogEntry[] = [] + + // 1. Delete Firebase auth record(s) + const fbLogs = await deleteFirebaseUser( + input.firebaseUserId, + input.firebaseUidOverride, + input.userEmail + ) + logs.push(...fbLogs) + + // Check if any Firebase delete produced a hard error + const hasFirebaseError = fbLogs.some((log) => log.level === 'error') + if (hasFirebaseError) { + return { success: false, logs } + } + + // 2. Create audit log with success: false + let auditLog: { id: string } | null = null + try { + auditLog = await prisma.userDeleteAuditLog.create({ + data: { + deletedUserId: input.userDbId, + callerUserId: input.callerUserId, + callerEmail: input.callerEmail, + deletedJourneyIds: input.deletedJourneyIds, + deletedTeamIds: input.deletedTeamIds, + deletedUserJourneyIds: input.deletedUserJourneyIds, + deletedUserTeamIds: input.deletedUserTeamIds, + success: false + } + }) + logs.push(createLog('📝 Audit log created')) + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error' + logs.push( + createLog( + `❌ Failed to create audit log: ${message}. Aborting deletion.`, + 'error' + ) + ) + return { success: false, logs } + } + + // 3. Delete User record + try { + await prisma.user.delete({ where: { id: input.userDbId } }) + logs.push(createLog('🗑️ User record deleted from database')) + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error' + if (auditLog != null) { + await prisma.userDeleteAuditLog.update({ + where: { id: auditLog.id }, + data: { errorMessage: `Failed to delete user record: ${message}` } + }) + } + logs.push(createLog(`❌ Failed to delete user record: ${message}`, 'error')) + return { success: false, logs } + } + + // 4. Update audit log to success + if (auditLog != null) { + await prisma.userDeleteAuditLog.update({ + where: { id: auditLog.id }, + data: { success: true } + }) + } + + logs.push(createLog('✅ User deleted successfully')) + return { success: true, logs } +} diff --git a/apis/api-users/src/schema/userDelete/service/index.ts b/apis/api-users/src/schema/userDelete/service/index.ts new file mode 100644 index 00000000000..2a78738c02a --- /dev/null +++ b/apis/api-users/src/schema/userDelete/service/index.ts @@ -0,0 +1,5 @@ +export { lookupUser } from './lookupUser' +export { deleteFirebaseUser, deleteUserData } from './deleteUserData' +export { callJourneysCheck, callJourneysConfirm } from './journeysInterop' +export type { LogEntry } from './types' +export { createLog } from './types' diff --git a/apis/api-users/src/schema/userDelete/service/journeysInterop.ts b/apis/api-users/src/schema/userDelete/service/journeysInterop.ts new file mode 100644 index 00000000000..1fb47a423e1 --- /dev/null +++ b/apis/api-users/src/schema/userDelete/service/journeysInterop.ts @@ -0,0 +1,137 @@ +import { gql } from '@apollo/client' + +import { createApolloClient } from '@core/yoga/apolloClient' + +import { LogEntry, createLog } from './types' + +const apolloClient = createApolloClient('api-users') + +interface JourneysCheckResult { + journeysToDelete: number + journeysToTransfer: number + journeysToRemove: number + teamsToDelete: number + teamsToTransfer: number + teamsToRemove: number + logs: LogEntry[] +} + +interface JourneysConfirmResult { + success: boolean + deletedJourneyIds: string[] + deletedTeamIds: string[] + deletedUserJourneyIds: string[] + deletedUserTeamIds: string[] + logs: LogEntry[] +} + +const USER_DELETE_JOURNEYS_CHECK = gql` + mutation UserDeleteJourneysCheck($userId: String!) { + userDeleteJourneysCheck(userId: $userId) { + journeysToDelete + journeysToTransfer + journeysToRemove + teamsToDelete + teamsToTransfer + teamsToRemove + logs { + message + level + timestamp + } + } + } +` + +const USER_DELETE_JOURNEYS_CONFIRM = gql` + mutation UserDeleteJourneysConfirm($userId: String!) { + userDeleteJourneysConfirm(userId: $userId) { + success + deletedJourneyIds + deletedTeamIds + deletedUserJourneyIds + deletedUserTeamIds + logs { + message + level + timestamp + } + } + } +` + +export async function callJourneysCheck( + userId: string +): Promise { + try { + const { data } = await apolloClient.mutate<{ + userDeleteJourneysCheck: JourneysCheckResult + }>({ + mutation: USER_DELETE_JOURNEYS_CHECK, + variables: { userId }, + fetchPolicy: 'no-cache' + }) + + if (data?.userDeleteJourneysCheck == null) { + return { + journeysToDelete: 0, + journeysToTransfer: 0, + journeysToRemove: 0, + teamsToDelete: 0, + teamsToTransfer: 0, + teamsToRemove: 0, + logs: [createLog('❌ No data returned from journeys check', 'error')] + } + } + + return data.userDeleteJourneysCheck + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error' + return { + journeysToDelete: 0, + journeysToTransfer: 0, + journeysToRemove: 0, + teamsToDelete: 0, + teamsToTransfer: 0, + teamsToRemove: 0, + logs: [createLog(`❌ Journeys check failed: ${message}`, 'error')] + } + } +} + +export async function callJourneysConfirm( + userId: string +): Promise { + try { + const { data } = await apolloClient.mutate<{ + userDeleteJourneysConfirm: JourneysConfirmResult + }>({ + mutation: USER_DELETE_JOURNEYS_CONFIRM, + variables: { userId }, + fetchPolicy: 'no-cache' + }) + + if (data?.userDeleteJourneysConfirm == null) { + return { + success: false, + deletedJourneyIds: [], + deletedTeamIds: [], + deletedUserJourneyIds: [], + deletedUserTeamIds: [], + logs: [createLog('❌ No data returned from journeys confirm', 'error')] + } + } + + return data.userDeleteJourneysConfirm + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error' + return { + success: false, + deletedJourneyIds: [], + deletedTeamIds: [], + deletedUserJourneyIds: [], + deletedUserTeamIds: [], + logs: [createLog(`❌ Journeys deletion failed: ${message}`, 'error')] + } + } +} diff --git a/apis/api-users/src/schema/userDelete/service/lookupUser.ts b/apis/api-users/src/schema/userDelete/service/lookupUser.ts new file mode 100644 index 00000000000..f9973d210fb --- /dev/null +++ b/apis/api-users/src/schema/userDelete/service/lookupUser.ts @@ -0,0 +1,164 @@ +import { GraphQLError } from 'graphql' + +import { User, prisma } from '@core/prisma/users/client' +import { auth } from '@core/yoga/firebaseClient' + +import { LogEntry, createLog } from './types' + +interface FirebaseStatus { + exists: boolean + uid: string | null + email: string | null + disabled: boolean + providerIds: string[] +} + +interface LookupResult { + user: User | null + firebase: FirebaseStatus + logs: LogEntry[] +} + +async function checkFirebaseUser( + userId: string, + email: string | null +): Promise<{ status: FirebaseStatus; logs: LogEntry[] }> { + const logs: LogEntry[] = [] + + // Try by UID first + try { + const fbUser = await auth.getUser(userId) + const providerIds = fbUser.providerData.map((p) => p.providerId) + logs.push( + createLog( + `🔥 Firebase user found by UID: ${fbUser.email ?? 'no email'} (providers: ${providerIds.join(', ') || 'none'}, disabled: ${fbUser.disabled})` + ) + ) + return { + status: { + exists: true, + uid: fbUser.uid, + email: fbUser.email ?? null, + disabled: fbUser.disabled, + providerIds + }, + logs + } + } catch (error) { + const isNotFound = + error != null && + typeof error === 'object' && + 'code' in error && + error.code === 'auth/user-not-found' + + if (!isNotFound) { + const message = error instanceof Error ? error.message : 'Unknown error' + logs.push( + createLog(`⚠️ Firebase lookup by UID failed: ${message}`, 'warn') + ) + } + } + + // UID not found — try by email as fallback + if (email != null) { + try { + const fbUser = await auth.getUserByEmail(email) + const providerIds = fbUser.providerData.map((p) => p.providerId) + logs.push( + createLog( + `⚠️ Firebase user NOT found by UID (${userId}) but FOUND by email (${email}). Firebase UID: ${fbUser.uid} (providers: ${providerIds.join(', ') || 'none'}, disabled: ${fbUser.disabled})`, + 'warn' + ) + ) + return { + status: { + exists: true, + uid: fbUser.uid, + email: fbUser.email ?? null, + disabled: fbUser.disabled, + providerIds + }, + logs + } + } catch (error) { + const isNotFound = + error != null && + typeof error === 'object' && + 'code' in error && + error.code === 'auth/user-not-found' + + if (!isNotFound) { + const message = error instanceof Error ? error.message : 'Unknown error' + logs.push( + createLog( + `⚠️ Firebase lookup by email also failed: ${message}`, + 'warn' + ) + ) + } + } + } + + logs.push(createLog('🔥 No Firebase auth record found')) + return { + status: { + exists: false, + uid: null, + email: null, + disabled: false, + providerIds: [] + }, + logs + } +} + +export async function lookupUser( + idType: 'databaseId' | 'email', + id: string +): Promise { + const logs: LogEntry[] = [] + logs.push(createLog(`🔍 Looking for user by ${idType}: ${id}`)) + + const user = + idType === 'email' + ? await prisma.user.findUnique({ where: { email: id } }) + : await prisma.user.findUnique({ where: { id } }) + + if (user != null) { + logs.push( + createLog( + `✅ User found: ${user.firstName} ${user.lastName ?? ''} (${user.email ?? 'no email'})` + ) + ) + + const { status: firebase, logs: fbLogs } = await checkFirebaseUser( + user.userId, + user.email + ) + logs.push(...fbLogs) + + return { user, firebase, logs } + } + + // No DB user — check Firebase directly (only possible with email lookup) + logs.push(createLog(`⚠️ No database user found for ${idType}: ${id}`, 'warn')) + + if (idType === 'email') { + const { status: firebase, logs: fbLogs } = await checkFirebaseUser('', id) + logs.push(...fbLogs) + + if (firebase.exists) { + logs.push( + createLog( + '⚠️ Firebase-only account detected — no database record, but Firebase auth exists', + 'warn' + ) + ) + return { user: null, firebase, logs } + } + } + + throw new GraphQLError(`User not found with ${idType}: ${id}`, { + extensions: { code: 'NOT_FOUND' } + }) +} diff --git a/apis/api-users/src/schema/userDelete/service/types.ts b/apis/api-users/src/schema/userDelete/service/types.ts new file mode 100644 index 00000000000..5c6c6367b67 --- /dev/null +++ b/apis/api-users/src/schema/userDelete/service/types.ts @@ -0,0 +1,12 @@ +export interface LogEntry { + message: string + level: 'info' | 'warn' | 'error' + timestamp: string +} + +export function createLog( + message: string, + level: 'info' | 'warn' | 'error' = 'info' +): LogEntry { + return { message, level, timestamp: new Date().toISOString() } +} diff --git a/apis/api-users/src/schema/userDelete/userDeleteCheck.ts b/apis/api-users/src/schema/userDelete/userDeleteCheck.ts new file mode 100644 index 00000000000..bf157c538d5 --- /dev/null +++ b/apis/api-users/src/schema/userDelete/userDeleteCheck.ts @@ -0,0 +1,114 @@ +import { builder } from '../builder' + +import { + type LogEntry, + callJourneysCheck, + createLog, + lookupUser +} from './service' + +const UserDeleteIdType = builder.enumType('UserDeleteIdType', { + values: ['databaseId', 'email'] as const +}) + +const UserDeleteLogEntry = builder.objectRef('UserDeleteLogEntry') + +builder.objectType(UserDeleteLogEntry, { + fields: (t) => ({ + message: t.exposeString('message', { nullable: false }), + level: t.exposeString('level', { nullable: false }), + timestamp: t.exposeString('timestamp', { nullable: false }) + }) +}) + +interface UserDeleteCheckResultShape { + userId: string + userEmail: string | null + userFirstName: string + journeysToDelete: number + journeysToTransfer: number + journeysToRemove: number + teamsToDelete: number + teamsToTransfer: number + teamsToRemove: number + logs: LogEntry[] +} + +const UserDeleteCheckResult = builder.objectRef( + 'UserDeleteCheckResult' +) + +builder.objectType(UserDeleteCheckResult, { + fields: (t) => ({ + userId: t.exposeString('userId', { nullable: false }), + userEmail: t.exposeString('userEmail'), + userFirstName: t.exposeString('userFirstName', { nullable: false }), + journeysToDelete: t.exposeInt('journeysToDelete', { nullable: false }), + journeysToTransfer: t.exposeInt('journeysToTransfer', { nullable: false }), + journeysToRemove: t.exposeInt('journeysToRemove', { nullable: false }), + teamsToDelete: t.exposeInt('teamsToDelete', { nullable: false }), + teamsToTransfer: t.exposeInt('teamsToTransfer', { nullable: false }), + teamsToRemove: t.exposeInt('teamsToRemove', { nullable: false }), + logs: t.field({ + type: [UserDeleteLogEntry], + nullable: false, + resolve: (parent) => parent.logs + }) + }) +}) + +builder.mutationField('userDeleteCheck', (t) => + t.withAuth({ isSuperAdmin: true }).field({ + type: UserDeleteCheckResult, + nullable: false, + args: { + idType: t.arg({ type: UserDeleteIdType, required: true }), + id: t.arg.string({ required: true }) + }, + resolve: async (_parent, { idType, id }) => { + const allLogs: LogEntry[] = [] + + const { user, firebase, logs: lookupLogs } = await lookupUser(idType, id) + allLogs.push(...lookupLogs) + + // Firebase-only account — no DB user, no journeys data + if (user == null) { + allLogs.push( + createLog('📋 Skipping journeys check — no database user to look up') + ) + return { + userId: firebase.uid ?? '', + userEmail: firebase.email, + userFirstName: '(Firebase only)', + journeysToDelete: 0, + journeysToTransfer: 0, + journeysToRemove: 0, + teamsToDelete: 0, + teamsToTransfer: 0, + teamsToRemove: 0, + logs: allLogs + } + } + + allLogs.push(createLog('📋 Checking journeys and teams...')) + + const journeysResult = await callJourneysCheck(user.userId) + allLogs.push(...journeysResult.logs) + + return { + userId: user.id, + userEmail: user.email, + userFirstName: user.firstName, + journeysToDelete: journeysResult.journeysToDelete, + journeysToTransfer: journeysResult.journeysToTransfer, + journeysToRemove: journeysResult.journeysToRemove, + teamsToDelete: journeysResult.teamsToDelete, + teamsToTransfer: journeysResult.teamsToTransfer, + teamsToRemove: journeysResult.teamsToRemove, + logs: allLogs + } + } + }) +) + +export { UserDeleteIdType, UserDeleteLogEntry } diff --git a/apis/api-users/src/schema/userDelete/userDeleteConfirm.ts b/apis/api-users/src/schema/userDelete/userDeleteConfirm.ts new file mode 100644 index 00000000000..e196f654996 --- /dev/null +++ b/apis/api-users/src/schema/userDelete/userDeleteConfirm.ts @@ -0,0 +1,169 @@ +import { GraphQLError } from 'graphql' + +import { prisma } from '@core/prisma/users/client' + +import { builder } from '../builder' + +import { + type LogEntry, + callJourneysConfirm, + createLog, + deleteFirebaseUser, + deleteUserData, + lookupUser +} from './service' +import { UserDeleteIdType, UserDeleteLogEntry } from './userDeleteCheck' + +interface UserDeleteConfirmProgressShape { + log: LogEntry + done: boolean + success: boolean | null +} + +const UserDeleteConfirmProgress = + builder.objectRef('UserDeleteConfirmProgress') + +builder.objectType(UserDeleteConfirmProgress, { + fields: (t) => ({ + log: t.field({ + type: UserDeleteLogEntry, + nullable: false, + resolve: (parent) => parent.log + }), + done: t.exposeBoolean('done', { nullable: false }), + success: t.boolean({ + nullable: true, + resolve: (parent) => parent.success + }) + }) +}) + +builder.subscriptionField('userDeleteConfirm', (t) => + t.withAuth({ isSuperAdmin: true }).field({ + type: UserDeleteConfirmProgress, + nullable: false, + args: { + idType: t.arg({ type: UserDeleteIdType, required: true }), + id: t.arg.string({ required: true }) + }, + subscribe: async function* (_parent, { idType, id }, ctx) { + // Look up the target user (also checks Firebase status) + const { user, firebase, logs: lookupLogs } = await lookupUser(idType, id) + for (const log of lookupLogs) { + yield { log, done: false, success: null } + } + + // Firebase-only account — no DB user, just delete Firebase auth + if (user == null) { + if (!firebase.exists || firebase.uid == null) { + yield { + log: createLog('❌ No Firebase account found to delete', 'error'), + done: true, + success: false + } + return + } + + yield { + log: createLog('🔥 Deleting Firebase-only account...'), + done: false, + success: null + } + + const fbLogs = await deleteFirebaseUser( + firebase.uid, + null, + firebase.email + ) + for (const log of fbLogs) { + yield { log, done: false, success: null } + } + + const hasError = fbLogs.some((log) => log.level === 'error') + yield { + log: createLog( + hasError + ? '❌ Firebase deletion failed' + : '✅ Firebase-only account deleted successfully' + ), + done: true, + success: !hasError + } + return + } + + // Look up the caller (superAdmin) for audit log + const caller = await prisma.user.findUnique({ + where: { userId: ctx.currentUser.id } + }) + if (caller == null) { + throw new GraphQLError('Caller user not found', { + extensions: { code: 'NOT_FOUND' } + }) + } + + // Phase 1: Journeys DB cleanup (via interop) + yield { + log: createLog('🔄 Starting journeys database cleanup...'), + done: false, + success: null + } + + const journeysResult = await callJourneysConfirm(user.userId) + for (const log of journeysResult.logs) { + yield { log, done: false, success: null } + } + + if (!journeysResult.success) { + yield { + log: createLog('❌ Journeys cleanup failed, aborting', 'error'), + done: true, + success: false + } + return + } + + // Phase 2: Users DB deletion + Firebase cleanup + yield { + log: createLog('🔄 Starting user record deletion...'), + done: false, + success: null + } + + // Use the Firebase UID from lookup if it differs from the DB userId + // (e.g. email-based fallback found a different UID) + const firebaseUidOverride = + firebase.uid != null && firebase.uid !== user.userId + ? firebase.uid + : null + + const userResult = await deleteUserData({ + userDbId: user.id, + firebaseUserId: user.userId, + firebaseUidOverride, + userEmail: user.email, + callerUserId: caller.id, + callerEmail: caller.email, + deletedJourneyIds: journeysResult.deletedJourneyIds, + deletedTeamIds: journeysResult.deletedTeamIds, + deletedUserJourneyIds: journeysResult.deletedUserJourneyIds, + deletedUserTeamIds: journeysResult.deletedUserTeamIds + }) + + for (const log of userResult.logs) { + yield { log, done: false, success: null } + } + + yield { + log: createLog( + userResult.success + ? '✅ User deletion completed successfully' + : '❌ User deletion failed' + ), + done: true, + success: userResult.success + } + }, + resolve: (progress) => progress + }) +) diff --git a/apps/journeys-admin/__generated__/GoogleSheetsSyncsForDoneScreen.ts b/apps/journeys-admin/__generated__/GoogleSheetsSyncsForDoneScreen.ts new file mode 100644 index 00000000000..d5a7147e851 --- /dev/null +++ b/apps/journeys-admin/__generated__/GoogleSheetsSyncsForDoneScreen.ts @@ -0,0 +1,24 @@ +/* tslint:disable */ +/* eslint-disable */ +// @generated +// This file was automatically generated and should not be edited. + +import { GoogleSheetsSyncsFilter } from "./globalTypes"; + +// ==================================================== +// GraphQL query operation: GoogleSheetsSyncsForDoneScreen +// ==================================================== + +export interface GoogleSheetsSyncsForDoneScreen_googleSheetsSyncs { + __typename: "GoogleSheetsSync"; + id: string; + deletedAt: any | null; +} + +export interface GoogleSheetsSyncsForDoneScreen { + googleSheetsSyncs: GoogleSheetsSyncsForDoneScreen_googleSheetsSyncs[]; +} + +export interface GoogleSheetsSyncsForDoneScreenVariables { + filter: GoogleSheetsSyncsFilter; +} diff --git a/apps/journeys-admin/__generated__/UserDeleteCheck.ts b/apps/journeys-admin/__generated__/UserDeleteCheck.ts new file mode 100644 index 00000000000..ecc0247fde5 --- /dev/null +++ b/apps/journeys-admin/__generated__/UserDeleteCheck.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +// @generated +// This file was automatically generated and should not be edited. + +import { UserDeleteIdType } from "./globalTypes"; + +// ==================================================== +// GraphQL mutation operation: UserDeleteCheck +// ==================================================== + +export interface UserDeleteCheck_userDeleteCheck_logs { + __typename: "UserDeleteLogEntry"; + message: string; + level: string; + timestamp: string; +} + +export interface UserDeleteCheck_userDeleteCheck { + __typename: "UserDeleteCheckResult"; + userId: string; + userEmail: string | null; + userFirstName: string; + journeysToDelete: number; + journeysToTransfer: number; + journeysToRemove: number; + teamsToDelete: number; + teamsToTransfer: number; + teamsToRemove: number; + logs: UserDeleteCheck_userDeleteCheck_logs[]; +} + +export interface UserDeleteCheck { + userDeleteCheck: UserDeleteCheck_userDeleteCheck; +} + +export interface UserDeleteCheckVariables { + idType: UserDeleteIdType; + id: string; +} diff --git a/apps/journeys-admin/__generated__/UserDeleteConfirmSubscription.ts b/apps/journeys-admin/__generated__/UserDeleteConfirmSubscription.ts new file mode 100644 index 00000000000..47dff478978 --- /dev/null +++ b/apps/journeys-admin/__generated__/UserDeleteConfirmSubscription.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +// @generated +// This file was automatically generated and should not be edited. + +import { UserDeleteIdType } from "./globalTypes"; + +// ==================================================== +// GraphQL subscription operation: UserDeleteConfirmSubscription +// ==================================================== + +export interface UserDeleteConfirmSubscription_userDeleteConfirm_log { + __typename: "UserDeleteLogEntry"; + message: string; + level: string; + timestamp: string; +} + +export interface UserDeleteConfirmSubscription_userDeleteConfirm { + __typename: "UserDeleteConfirmProgress"; + log: UserDeleteConfirmSubscription_userDeleteConfirm_log; + done: boolean; + success: boolean | null; +} + +export interface UserDeleteConfirmSubscription { + userDeleteConfirm: UserDeleteConfirmSubscription_userDeleteConfirm; +} + +export interface UserDeleteConfirmSubscriptionVariables { + idType: UserDeleteIdType; + id: string; +} diff --git a/apps/journeys-admin/__generated__/globalTypes.ts b/apps/journeys-admin/__generated__/globalTypes.ts index 9f82c7f43ec..44fb4496624 100644 --- a/apps/journeys-admin/__generated__/globalTypes.ts +++ b/apps/journeys-admin/__generated__/globalTypes.ts @@ -325,6 +325,11 @@ export enum TypographyVariant { subtitle2 = "subtitle2", } +export enum UserDeleteIdType { + databaseId = "databaseId", + email = "email", +} + export enum UserJourneyRole { editor = "editor", inviteRequested = "inviteRequested", diff --git a/apps/journeys-admin/pages/users/delete.tsx b/apps/journeys-admin/pages/users/delete.tsx new file mode 100644 index 00000000000..8bbd2180d70 --- /dev/null +++ b/apps/journeys-admin/pages/users/delete.tsx @@ -0,0 +1,49 @@ +import { GetServerSidePropsContext } from 'next' +import { useTranslation } from 'next-i18next' +import { NextSeo } from 'next-seo' +import { ReactElement } from 'react' + +import { PageWrapper } from '../../src/components/PageWrapper' +import { UserDelete } from '../../src/components/UserDelete' +import { useAuth } from '../../src/libs/auth' +import { + getAuthTokens, + redirectToLogin, + toUser +} from '../../src/libs/auth/getAuthTokens' +import { initAndAuthApp } from '../../src/libs/initAndAuthApp' + +export default function UserDeletePage(): ReactElement { + const { t } = useTranslation('apps-journeys-admin') + const { user } = useAuth() + + return ( + <> + + + + + + ) +} + +export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { + const tokens = await getAuthTokens(ctx) + if (tokens == null) return redirectToLogin(ctx) + const user = toUser(tokens) + + const { redirect, translations } = await initAndAuthApp({ + user, + locale: ctx.locale, + resolvedUrl: ctx.resolvedUrl + }) + + if (redirect != null) return { redirect } + + return { + props: { + userSerialized: JSON.stringify(user), + ...translations + } + } +} diff --git a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.tsx b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.tsx index e9cbc4cada3..79e46f6e221 100644 --- a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.tsx +++ b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.tsx @@ -12,6 +12,7 @@ import { useTranslation } from 'next-i18next' import { MouseEvent, ReactElement, useEffect, useState } from 'react' import BoxIcon from '@core/shared/ui/icons/Box' +import Trash2Icon from '@core/shared/ui/icons/Trash2' import UserProfile3Icon from '@core/shared/ui/icons/UserProfile3' import { GetMe } from '../../../../../__generated__/GetMe' @@ -147,6 +148,23 @@ export function UserNavigation({ /> )} + {data.me?.__typename === 'AuthenticatedUser' && + data.me.superAdmin === true && ( + + + + + + + )} {data.me?.__typename === 'AuthenticatedUser' && ( { + constructor(props: UserDeleteErrorBoundaryProps) { + super(props) + this.state = { hasError: false, error: null } + } + + static getDerivedStateFromError(error: Error): UserDeleteErrorBoundaryState { + return { hasError: true, error } + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo): void { + console.error('UserDelete error:', error, errorInfo) + } + + render(): ReactNode { + if (this.state.hasError) { + return + } + return this.props.children + } +} + +function UserDeleteErrorFallback({ + error +}: { + error: Error | null +}): ReactElement { + const { t } = useTranslation('apps-journeys-admin') + return ( + + + {t('Something went wrong')} + + {error?.message ?? t('An unexpected error occurred.')} + + + + ) +} + +export function UserDeleteWithErrorBoundary(): ReactElement { + return ( + + + + ) +} + +interface ConfirmVars { + idType: UserDeleteIdType + id: string +} + +function UserDeleteContent(): ReactElement { + const { t } = useTranslation('apps-journeys-admin') + const router = useRouter() + const { enqueueSnackbar } = useSnackbar() + const { data } = useSuspenseQuery(GET_ME, { + variables: { input: { redirect: router?.query?.redirect } } + }) + + const [idType, setIdType] = useState(UserDeleteIdType.email) + const [userId, setUserId] = useState('') + const [logs, setLogs] = useState([]) + const [checkComplete, setCheckComplete] = useState(false) + const [confirmOpen, setConfirmOpen] = useState(false) + const [deleteComplete, setDeleteComplete] = useState(false) + const [confirmVars, setConfirmVars] = useState(null) + + const [userDeleteCheck, { loading: checkLoading }] = useMutation< + UserDeleteCheck, + UserDeleteCheckVariables + >(USER_DELETE_CHECK) + + useSubscription< + UserDeleteConfirmSubscription, + UserDeleteConfirmSubscriptionVariables + >(USER_DELETE_CONFIRM, { + skip: confirmVars == null, + variables: confirmVars ?? { idType: UserDeleteIdType.email, id: '' }, + onData: ({ data: subData }) => { + const progress = subData.data?.userDeleteConfirm + if (progress == null) return + + setLogs((prev) => [...prev, progress.log]) + + if (progress.done) { + setDeleteComplete(true) + setConfirmVars(null) + + if (progress.success === true) { + enqueueSnackbar(t('User deleted successfully'), { + variant: 'success' + }) + setCheckComplete(false) + } else { + enqueueSnackbar(t('User deletion failed. Check logs for details.'), { + variant: 'error' + }) + } + } + }, + onError: (error) => { + const message = error.graphQLErrors[0]?.message ?? error.message + setLogs((prev) => [ + ...prev, + { + message: `Error: ${message}`, + level: 'error', + timestamp: new Date().toISOString() + } + ]) + enqueueSnackbar(message, { variant: 'error', preventDuplicate: true }) + setConfirmVars(null) + } + }) + + const confirmLoading = confirmVars != null && !deleteComplete + + const isSuperAdmin = + data.me?.__typename === 'AuthenticatedUser' && data.me.superAdmin === true + + useEffect(() => { + if (!isSuperAdmin) { + void router.push('/') + } + }, [isSuperAdmin, router]) + + const logBoxRef = useRef(null) + + useEffect(() => { + const el = logBoxRef.current + if (el != null) { + el.scrollTop = el.scrollHeight + } + }, [logs]) + + const logText = useMemo( + () => + logs + .map((log) => { + const time = new Date(log.timestamp).toLocaleTimeString() + const prefix = + log.level === 'error' + ? 'ERROR' + : log.level === 'warn' + ? 'WARN' + : 'INFO' + return `[${time}] ${prefix}: ${log.message}` + }) + .join('\n'), + [logs] + ) + + const handleCheck = useCallback(async () => { + if (userId.trim() === '') return + + setLogs([]) + setCheckComplete(false) + setDeleteComplete(false) + + try { + const { data: checkData } = await userDeleteCheck({ + variables: { idType, id: userId.trim() } + }) + + if (checkData?.userDeleteCheck != null) { + setLogs(checkData.userDeleteCheck.logs) + setCheckComplete(true) + } + } catch (error) { + if (error instanceof ApolloError) { + const message = error.graphQLErrors[0]?.message ?? error.message + setLogs((prev) => [ + ...prev, + { + message: `Error: ${message}`, + level: 'error', + timestamp: new Date().toISOString() + } + ]) + enqueueSnackbar(message, { variant: 'error', preventDuplicate: true }) + } + } + }, [idType, userId, userDeleteCheck, enqueueSnackbar]) + + const handleConfirmDelete = useCallback(() => { + setConfirmOpen(false) + setDeleteComplete(false) + setConfirmVars({ idType, id: userId.trim() }) + }, [idType, userId]) + + if (!isSuperAdmin) return <> + + return ( + + + {t('Delete User')} + + + + {t('Warning')} + + {t( + 'This action permanently deletes a user and their associated data. This cannot be undone. Always run a check first to review what will be deleted.' + )} + + + + + + {t('Lookup By')} + + + + { + setUserId(e.target.value) + setCheckComplete(false) + }} + onKeyDown={(e) => { + if (e.key === 'Enter') void handleCheck() + }} + disabled={checkLoading || confirmLoading} + fullWidth + /> + + + + + + + + + + + setConfirmOpen(false)} + aria-labelledby="confirm-delete-title" + > + + {t('Confirm User Deletion')} + + + + {t( + 'Are you sure you want to permanently delete this user? This action cannot be undone. All associated data including journeys, team memberships, and related records will be permanently removed.' + )} + + + + + + + + + ) +} diff --git a/apps/journeys-admin/src/components/UserDelete/index.ts b/apps/journeys-admin/src/components/UserDelete/index.ts new file mode 100644 index 00000000000..6f5d664123e --- /dev/null +++ b/apps/journeys-admin/src/components/UserDelete/index.ts @@ -0,0 +1 @@ +export { UserDeleteWithErrorBoundary as UserDelete } from './UserDelete' diff --git a/libs/locales/en/apps-journeys-admin.json b/libs/locales/en/apps-journeys-admin.json index 5d4ce416c91..0b9e396b82a 100644 --- a/libs/locales/en/apps-journeys-admin.json +++ b/libs/locales/en/apps-journeys-admin.json @@ -38,6 +38,7 @@ "Customize Template": "Customize Template", "Journey not found. Redirected to templates.": "Journey not found. Redirected to templates.", "Journey Templates": "Journey Templates", + "Delete User": "Delete User", "Sign In": "Sign In", "Create New Account or Log in": "Create New Account or Log in", "A question about sign in": "A question about sign in", @@ -1070,6 +1071,22 @@ "End User License Agreement": "End User License Agreement", "Community Guidelines": "Community Guidelines", "I agree with listed above conditions and requirements": "I agree with listed above conditions and requirements", + "Something went wrong": "Something went wrong", + "An unexpected error occurred.": "An unexpected error occurred.", + "User deleted successfully": "User deleted successfully", + "User deletion failed. Check logs for details.": "User deletion failed. Check logs for details.", + "This action permanently deletes a user and their associated data. This cannot be undone. Always run a check first to review what will be deleted.": "This action permanently deletes a user and their associated data. This cannot be undone. Always run a check first to review what will be deleted.", + "Lookup By": "Lookup By", + "Database ID": "Database ID", + "User email to delete": "User email to delete", + "Database ID to delete": "Database ID to delete", + "User Email": "User Email", + "Checking...": "Checking...", + "Logs": "Logs", + "Deleting...": "Deleting...", + "Confirm User Deletion": "Confirm User Deletion", + "Are you sure you want to permanently delete this user? This action cannot be undone. All associated data including journeys, team memberships, and related records will be permanently removed.": "Are you sure you want to permanently delete this user? This action cannot be undone. All associated data including journeys, team memberships, and related records will be permanently removed.", + "Delete Permanently": "Delete Permanently", "Open conversation": "Open conversation", "Chat Platform": "Chat Platform", "Username": "Username", diff --git a/libs/prisma/users/db/migrations/20260317034905_20260317034904/migration.sql b/libs/prisma/users/db/migrations/20260317034905_20260317034904/migration.sql new file mode 100644 index 00000000000..a569f075eec --- /dev/null +++ b/libs/prisma/users/db/migrations/20260317034905_20260317034904/migration.sql @@ -0,0 +1,30 @@ +-- CreateTable +CREATE TABLE "UserDeleteAuditLog" ( + "id" TEXT NOT NULL, + "deletedUserId" TEXT NOT NULL, + "deletedUserEmail" TEXT, + "deletedUserFirstName" TEXT NOT NULL, + "deletedUserLastName" TEXT, + "callerUserId" TEXT NOT NULL, + "callerEmail" TEXT, + "callerFirstName" TEXT NOT NULL, + "callerLastName" TEXT, + "deletedJourneyIds" TEXT[], + "deletedTeamIds" TEXT[], + "deletedUserJourneyIds" TEXT[], + "deletedUserTeamIds" TEXT[], + "success" BOOLEAN NOT NULL DEFAULT false, + "errorMessage" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserDeleteAuditLog_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "UserDeleteAuditLog_deletedUserId_idx" ON "UserDeleteAuditLog"("deletedUserId"); + +-- CreateIndex +CREATE INDEX "UserDeleteAuditLog_callerUserId_idx" ON "UserDeleteAuditLog"("callerUserId"); + +-- CreateIndex +CREATE INDEX "UserDeleteAuditLog_createdAt_idx" ON "UserDeleteAuditLog"("createdAt"); diff --git a/libs/prisma/users/db/migrations/20260330000000_remove_pii_from_user_delete_audit_log/migration.sql b/libs/prisma/users/db/migrations/20260330000000_remove_pii_from_user_delete_audit_log/migration.sql new file mode 100644 index 00000000000..dc1be9ac9af --- /dev/null +++ b/libs/prisma/users/db/migrations/20260330000000_remove_pii_from_user_delete_audit_log/migration.sql @@ -0,0 +1,7 @@ +-- AlterTable +ALTER TABLE "UserDeleteAuditLog" +DROP COLUMN "deletedUserEmail", +DROP COLUMN "deletedUserFirstName", +DROP COLUMN "deletedUserLastName", +DROP COLUMN "callerFirstName", +DROP COLUMN "callerLastName"; diff --git a/libs/prisma/users/db/migrations/migration_lock.toml b/libs/prisma/users/db/migrations/migration_lock.toml index fbffa92c2bb..044d57cdb0d 100644 --- a/libs/prisma/users/db/migrations/migration_lock.toml +++ b/libs/prisma/users/db/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/libs/prisma/users/db/schema.prisma b/libs/prisma/users/db/schema.prisma index 61f00ba3b1f..4f4cbf7b4f9 100644 --- a/libs/prisma/users/db/schema.prisma +++ b/libs/prisma/users/db/schema.prisma @@ -27,3 +27,21 @@ model User { superAdmin Boolean @default(false) emailVerified Boolean @default(true) } + +model UserDeleteAuditLog { + id String @id @default(uuid()) + deletedUserId String + callerUserId String + callerEmail String? + deletedJourneyIds String[] + deletedTeamIds String[] + deletedUserJourneyIds String[] + deletedUserTeamIds String[] + success Boolean @default(false) + errorMessage String? + createdAt DateTime @default(now()) + + @@index(deletedUserId) + @@index(callerUserId) + @@index(createdAt) +} diff --git a/libs/prisma/users/src/__generated__/pothos-types.ts b/libs/prisma/users/src/__generated__/pothos-types.ts index b53d874e40e..5d901aa1559 100644 --- a/libs/prisma/users/src/__generated__/pothos-types.ts +++ b/libs/prisma/users/src/__generated__/pothos-types.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -import type { Prisma, User } from "./client/client.js"; +import type { Prisma, User, UserDeleteAuditLog } from "./client/client.js"; import type { PothosPrismaDatamodel } from "@pothos/plugin-prisma"; export default interface PrismaTypes { User: { @@ -16,5 +16,19 @@ export default interface PrismaTypes { ListRelations: never; Relations: {}; }; + UserDeleteAuditLog: { + Name: "UserDeleteAuditLog"; + Shape: UserDeleteAuditLog; + Include: never; + Select: Prisma.UserDeleteAuditLogSelect; + OrderBy: Prisma.UserDeleteAuditLogOrderByWithRelationInput; + WhereUnique: Prisma.UserDeleteAuditLogWhereUniqueInput; + Where: Prisma.UserDeleteAuditLogWhereInput; + Create: {}; + Update: {}; + RelationName: never; + ListRelations: never; + Relations: {}; + }; } -export function getDatamodel(): PothosPrismaDatamodel { return JSON.parse("{\"datamodel\":{\"models\":{\"User\":{\"fields\":[{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"id\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":true,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"userId\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":true,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"firstName\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"lastName\",\"isRequired\":false,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"email\",\"isRequired\":false,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":true,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"imageUrl\",\"isRequired\":false,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"DateTime\",\"kind\":\"scalar\",\"name\":\"createdAt\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"Boolean\",\"kind\":\"scalar\",\"name\":\"superAdmin\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"Boolean\",\"kind\":\"scalar\",\"name\":\"emailVerified\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueIndexes\":[]}}}}"); } \ No newline at end of file +export function getDatamodel(): PothosPrismaDatamodel { return JSON.parse("{\"datamodel\":{\"models\":{\"User\":{\"fields\":[{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"id\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":true,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"userId\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":true,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"firstName\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"lastName\",\"isRequired\":false,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"email\",\"isRequired\":false,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":true,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"imageUrl\",\"isRequired\":false,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"DateTime\",\"kind\":\"scalar\",\"name\":\"createdAt\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"Boolean\",\"kind\":\"scalar\",\"name\":\"superAdmin\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"Boolean\",\"kind\":\"scalar\",\"name\":\"emailVerified\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueIndexes\":[]},\"UserDeleteAuditLog\":{\"fields\":[{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"id\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":true,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"deletedUserId\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"callerUserId\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"callerEmail\",\"isRequired\":false,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"deletedJourneyIds\",\"isRequired\":true,\"isList\":true,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"deletedTeamIds\",\"isRequired\":true,\"isList\":true,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"deletedUserJourneyIds\",\"isRequired\":true,\"isList\":true,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"deletedUserTeamIds\",\"isRequired\":true,\"isList\":true,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"Boolean\",\"kind\":\"scalar\",\"name\":\"success\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"String\",\"kind\":\"scalar\",\"name\":\"errorMessage\",\"isRequired\":false,\"isList\":false,\"hasDefaultValue\":false,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false},{\"type\":\"DateTime\",\"kind\":\"scalar\",\"name\":\"createdAt\",\"isRequired\":true,\"isList\":false,\"hasDefaultValue\":true,\"isUnique\":false,\"isId\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueIndexes\":[]}}}}"); } \ No newline at end of file diff --git a/libs/shared/gql/src/__generated__/graphql-env.d.ts b/libs/shared/gql/src/__generated__/graphql-env.d.ts index a41852c2af0..6f5270767db 100644 --- a/libs/shared/gql/src/__generated__/graphql-env.d.ts +++ b/libs/shared/gql/src/__generated__/graphql-env.d.ts @@ -181,7 +181,7 @@ export type introspection_types = { 'MultiselectOptionBlockUpdateInput': { kind: 'INPUT_OBJECT'; name: 'MultiselectOptionBlockUpdateInput'; isOneOf: false; inputFields: [{ name: 'parentBlockId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'label'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'MultiselectSubmissionEvent': { kind: 'OBJECT'; name: 'MultiselectSubmissionEvent'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyId': { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'label': { name: 'label'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'value': { name: 'value'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; }; 'MultiselectSubmissionEventCreateInput': { kind: 'INPUT_OBJECT'; name: 'MultiselectSubmissionEventCreateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'blockId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'stepId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'label'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'values'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; }; defaultValue: null }]; }; - 'Mutation': { kind: 'OBJECT'; name: 'Mutation'; fields: { 'audioPreviewCreate': { name: 'audioPreviewCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'audioPreviewDelete': { name: 'audioPreviewDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'audioPreviewUpdate': { name: 'audioPreviewUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'bibleCitationCreate': { name: 'bibleCitationCreate'; type: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; } }; 'bibleCitationDelete': { name: 'bibleCitationDelete'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'bibleCitationUpdate': { name: 'bibleCitationUpdate'; type: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; } }; 'blockDelete': { name: 'blockDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockDeleteAction': { name: 'blockDeleteAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; } }; 'blockDuplicate': { name: 'blockDuplicate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockOrderUpdate': { name: 'blockOrderUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockRestore': { name: 'blockRestore'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockUpdateAction': { name: 'blockUpdateAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Action'; ofType: null; }; } }; 'blockUpdateChatAction': { name: 'blockUpdateChatAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatAction'; ofType: null; }; } }; 'blockUpdateEmailAction': { name: 'blockUpdateEmailAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'EmailAction'; ofType: null; }; } }; 'blockUpdateLinkAction': { name: 'blockUpdateLinkAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'LinkAction'; ofType: null; }; } }; 'blockUpdateNavigateToBlockAction': { name: 'blockUpdateNavigateToBlockAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'NavigateToBlockAction'; ofType: null; }; } }; 'blockUpdatePhoneAction': { name: 'blockUpdatePhoneAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PhoneAction'; ofType: null; }; } }; 'buttonBlockCreate': { name: 'buttonBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ButtonBlock'; ofType: null; }; } }; 'buttonBlockUpdate': { name: 'buttonBlockUpdate'; type: { kind: 'OBJECT'; name: 'ButtonBlock'; ofType: null; } }; 'buttonClickEventCreate': { name: 'buttonClickEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ButtonClickEvent'; ofType: null; }; } }; 'cardBlockCreate': { name: 'cardBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CardBlock'; ofType: null; }; } }; 'cardBlockUpdate': { name: 'cardBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CardBlock'; ofType: null; }; } }; 'chatButtonCreate': { name: 'chatButtonCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatButtonRemove': { name: 'chatButtonRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatButtonUpdate': { name: 'chatButtonUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatOpenEventCreate': { name: 'chatOpenEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatOpenEvent'; ofType: null; }; } }; 'cloudflareR2CompleteMultipart': { name: 'cloudflareR2CompleteMultipart'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2Create': { name: 'cloudflareR2Create'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2Delete': { name: 'cloudflareR2Delete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2MultipartPrepare': { name: 'cloudflareR2MultipartPrepare'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2MultipartPrepared'; ofType: null; }; } }; 'cloudflareUploadComplete': { name: 'cloudflareUploadComplete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'createCloudflareImageFromPrompt': { name: 'createCloudflareImageFromPrompt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createCloudflareUploadByFile': { name: 'createCloudflareUploadByFile'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createCloudflareUploadByUrl': { name: 'createCloudflareUploadByUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createImageBySegmindPrompt': { name: 'createImageBySegmindPrompt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createJourneyEventsExportLog': { name: 'createJourneyEventsExportLog'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyEventsExportLog'; ofType: null; }; } }; 'createKeyword': { name: 'createKeyword'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Keyword'; ofType: null; }; } }; 'createMuxVideoAndQueueUpload': { name: 'createMuxVideoAndQueueUpload'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createMuxVideoUploadByFile': { name: 'createMuxVideoUploadByFile'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createMuxVideoUploadByUrl': { name: 'createMuxVideoUploadByUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createVerificationRequest': { name: 'createVerificationRequest'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'customDomainCheck': { name: 'customDomainCheck'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomainCheck'; ofType: null; }; } }; 'customDomainCreate': { name: 'customDomainCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomainDelete': { name: 'customDomainDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomainUpdate': { name: 'customDomainUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'deleteCloudflareImage': { name: 'deleteCloudflareImage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'deleteMuxVideo': { name: 'deleteMuxVideo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'enableMuxDownload': { name: 'enableMuxDownload'; type: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; } }; 'fixVideoLanguages': { name: 'fixVideoLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'googleSheetsSyncBackfill': { name: 'googleSheetsSyncBackfill'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'googleSheetsSyncCreate': { name: 'googleSheetsSyncCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'googleSheetsSyncDelete': { name: 'googleSheetsSyncDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'hostCreate': { name: 'hostCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'hostDelete': { name: 'hostDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'hostUpdate': { name: 'hostUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'iconBlockCreate': { name: 'iconBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IconBlock'; ofType: null; }; } }; 'iconBlockUpdate': { name: 'iconBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IconBlock'; ofType: null; }; } }; 'imageBlockCreate': { name: 'imageBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ImageBlock'; ofType: null; }; } }; 'imageBlockUpdate': { name: 'imageBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ImageBlock'; ofType: null; }; } }; 'integrationDelete': { name: 'integrationDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Integration'; ofType: null; }; } }; 'integrationGoogleCreate': { name: 'integrationGoogleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGoogle'; ofType: null; }; } }; 'integrationGoogleUpdate': { name: 'integrationGoogleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGoogle'; ofType: null; }; } }; 'integrationGrowthSpacesCreate': { name: 'integrationGrowthSpacesCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; ofType: null; }; } }; 'integrationGrowthSpacesUpdate': { name: 'integrationGrowthSpacesUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; ofType: null; }; } }; 'journeyAiTranslateCreate': { name: 'journeyAiTranslateCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCollectionCreate': { name: 'journeyCollectionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollectionDelete': { name: 'journeyCollectionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollectionUpdate': { name: 'journeyCollectionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCreate': { name: 'journeyCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCustomizationFieldPublisherUpdate': { name: 'journeyCustomizationFieldPublisherUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCustomizationField'; ofType: null; }; }; }; } }; 'journeyCustomizationFieldUserUpdate': { name: 'journeyCustomizationFieldUserUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCustomizationField'; ofType: null; }; }; }; } }; 'journeyDuplicate': { name: 'journeyDuplicate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyFeature': { name: 'journeyFeature'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeyLanguageAiDetect': { name: 'journeyLanguageAiDetect'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'journeyNotificationUpdate': { name: 'journeyNotificationUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; }; } }; 'journeyProfileCreate': { name: 'journeyProfileCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; }; } }; 'journeyProfileUpdate': { name: 'journeyProfileUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; }; } }; 'journeyPublish': { name: 'journeyPublish'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeySimpleUpdate': { name: 'journeySimpleUpdate'; type: { kind: 'SCALAR'; name: 'Json'; ofType: null; } }; 'journeyTemplate': { name: 'journeyTemplate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyThemeCreate': { name: 'journeyThemeCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyThemeDelete': { name: 'journeyThemeDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyThemeUpdate': { name: 'journeyThemeUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyUpdate': { name: 'journeyUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyViewEventCreate': { name: 'journeyViewEventCreate'; type: { kind: 'OBJECT'; name: 'JourneyViewEvent'; ofType: null; } }; 'journeyVisitorExportToGoogleSheet': { name: 'journeyVisitorExportToGoogleSheet'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyVisitorGoogleSheetExportResult'; ofType: null; }; } }; 'journeysArchive': { name: 'journeysArchive'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysDelete': { name: 'journeysDelete'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysRestore': { name: 'journeysRestore'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysTrash': { name: 'journeysTrash'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'multiselectBlockCreate': { name: 'multiselectBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectBlock'; ofType: null; }; } }; 'multiselectBlockUpdate': { name: 'multiselectBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectBlock'; ofType: null; }; } }; 'multiselectOptionBlockCreate': { name: 'multiselectOptionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectOptionBlock'; ofType: null; }; } }; 'multiselectOptionBlockUpdate': { name: 'multiselectOptionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectOptionBlock'; ofType: null; }; } }; 'multiselectSubmissionEventCreate': { name: 'multiselectSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectSubmissionEvent'; ofType: null; }; } }; 'playlistCreate': { name: 'playlistCreate'; type: { kind: 'UNION'; name: 'MutationPlaylistCreateResult'; ofType: null; } }; 'playlistDelete': { name: 'playlistDelete'; type: { kind: 'UNION'; name: 'MutationPlaylistDeleteResult'; ofType: null; } }; 'playlistItemAdd': { name: 'playlistItemAdd'; type: { kind: 'UNION'; name: 'MutationPlaylistItemAddResult'; ofType: null; } }; 'playlistItemAddWithVideoAndLanguageIds': { name: 'playlistItemAddWithVideoAndLanguageIds'; type: { kind: 'UNION'; name: 'MutationPlaylistItemAddWithVideoAndLanguageIdsResult'; ofType: null; } }; 'playlistItemRemove': { name: 'playlistItemRemove'; type: { kind: 'UNION'; name: 'MutationPlaylistItemRemoveResult'; ofType: null; } }; 'playlistItemsReorder': { name: 'playlistItemsReorder'; type: { kind: 'UNION'; name: 'MutationPlaylistItemsReorderResult'; ofType: null; } }; 'playlistUpdate': { name: 'playlistUpdate'; type: { kind: 'UNION'; name: 'MutationPlaylistUpdateResult'; ofType: null; } }; 'qrCodeCreate': { name: 'qrCodeCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodeDelete': { name: 'qrCodeDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodeUpdate': { name: 'qrCodeUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'radioOptionBlockCreate': { name: 'radioOptionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioOptionBlock'; ofType: null; }; } }; 'radioOptionBlockUpdate': { name: 'radioOptionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioOptionBlock'; ofType: null; }; } }; 'radioQuestionBlockCreate': { name: 'radioQuestionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionBlock'; ofType: null; }; } }; 'radioQuestionBlockUpdate': { name: 'radioQuestionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionBlock'; ofType: null; }; } }; 'radioQuestionSubmissionEventCreate': { name: 'radioQuestionSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionSubmissionEvent'; ofType: null; }; } }; 'shortLinkCreate': { name: 'shortLinkCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkCreateResult'; ofType: null; }; } }; 'shortLinkDelete': { name: 'shortLinkDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDeleteResult'; ofType: null; }; } }; 'shortLinkDomainCreate': { name: 'shortLinkDomainCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainCreateResult'; ofType: null; }; } }; 'shortLinkDomainDelete': { name: 'shortLinkDomainDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainDeleteResult'; ofType: null; }; } }; 'shortLinkDomainUpdate': { name: 'shortLinkDomainUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainUpdateResult'; ofType: null; }; } }; 'shortLinkUpdate': { name: 'shortLinkUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkUpdateResult'; ofType: null; }; } }; 'signUpBlockCreate': { name: 'signUpBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SignUpBlock'; ofType: null; }; } }; 'signUpBlockUpdate': { name: 'signUpBlockUpdate'; type: { kind: 'OBJECT'; name: 'SignUpBlock'; ofType: null; } }; 'signUpSubmissionEventCreate': { name: 'signUpSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SignUpSubmissionEvent'; ofType: null; }; } }; 'siteCreate': { name: 'siteCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationSiteCreateResult'; ofType: null; }; } }; 'spacerBlockCreate': { name: 'spacerBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SpacerBlock'; ofType: null; }; } }; 'spacerBlockUpdate': { name: 'spacerBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SpacerBlock'; ofType: null; }; } }; 'stepBlockCreate': { name: 'stepBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; } }; 'stepBlockPositionUpdate': { name: 'stepBlockPositionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; }; }; } }; 'stepBlockUpdate': { name: 'stepBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; } }; 'stepNextEventCreate': { name: 'stepNextEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepNextEvent'; ofType: null; }; } }; 'stepPreviousEventCreate': { name: 'stepPreviousEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepPreviousEvent'; ofType: null; }; } }; 'stepViewEventCreate': { name: 'stepViewEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepViewEvent'; ofType: null; }; } }; 'teamCreate': { name: 'teamCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'teamUpdate': { name: 'teamUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'textResponseBlockCreate': { name: 'textResponseBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TextResponseBlock'; ofType: null; }; } }; 'textResponseBlockUpdate': { name: 'textResponseBlockUpdate'; type: { kind: 'OBJECT'; name: 'TextResponseBlock'; ofType: null; } }; 'textResponseSubmissionEventCreate': { name: 'textResponseSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TextResponseSubmissionEvent'; ofType: null; }; } }; 'triggerUnsplashDownload': { name: 'triggerUnsplashDownload'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'typographyBlockCreate': { name: 'typographyBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TypographyBlock'; ofType: null; }; } }; 'typographyBlockUpdate': { name: 'typographyBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TypographyBlock'; ofType: null; }; } }; 'updateJourneysEmailPreference': { name: 'updateJourneysEmailPreference'; type: { kind: 'OBJECT'; name: 'JourneysEmailPreference'; ofType: null; } }; 'updateVideoAlgoliaIndex': { name: 'updateVideoAlgoliaIndex'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'updateVideoVariantAlgoliaIndex': { name: 'updateVideoVariantAlgoliaIndex'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'userImpersonate': { name: 'userImpersonate'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'userInviteAcceptAll': { name: 'userInviteAcceptAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; }; }; } }; 'userInviteCreate': { name: 'userInviteCreate'; type: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; } }; 'userInviteRemove': { name: 'userInviteRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; } }; 'userJourneyApprove': { name: 'userJourneyApprove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyOpen': { name: 'userJourneyOpen'; type: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; } }; 'userJourneyPromote': { name: 'userJourneyPromote'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyRemove': { name: 'userJourneyRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyRemoveAll': { name: 'userJourneyRemoveAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; }; }; } }; 'userJourneyRequest': { name: 'userJourneyRequest'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userMediaProfileUpdate': { name: 'userMediaProfileUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserMediaProfile'; ofType: null; }; } }; 'userTeamDelete': { name: 'userTeamDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'userTeamInviteAcceptAll': { name: 'userTeamInviteAcceptAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; }; }; } }; 'userTeamInviteCreate': { name: 'userTeamInviteCreate'; type: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; } }; 'userTeamInviteRemove': { name: 'userTeamInviteRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; } }; 'userTeamUpdate': { name: 'userTeamUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'validateEmail': { name: 'validateEmail'; type: { kind: 'OBJECT'; name: 'AuthenticatedUser'; ofType: null; } }; 'videoBlockCreate': { name: 'videoBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoBlock'; ofType: null; }; } }; 'videoBlockUpdate': { name: 'videoBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoBlock'; ofType: null; }; } }; 'videoCollapseEventCreate': { name: 'videoCollapseEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoCollapseEvent'; ofType: null; }; } }; 'videoCompleteEventCreate': { name: 'videoCompleteEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoCompleteEvent'; ofType: null; }; } }; 'videoCreate': { name: 'videoCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoDelete': { name: 'videoDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoDescriptionCreate': { name: 'videoDescriptionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoDescriptionDelete': { name: 'videoDescriptionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoDescriptionUpdate': { name: 'videoDescriptionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoEditionCreate': { name: 'videoEditionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoEditionDelete': { name: 'videoEditionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoEditionUpdate': { name: 'videoEditionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoExpandEventCreate': { name: 'videoExpandEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoExpandEvent'; ofType: null; }; } }; 'videoImageAltCreate': { name: 'videoImageAltCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoImageAltDelete': { name: 'videoImageAltDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoImageAltUpdate': { name: 'videoImageAltUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoOriginCreate': { name: 'videoOriginCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoOriginDelete': { name: 'videoOriginDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoOriginUpdate': { name: 'videoOriginUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoPauseEventCreate': { name: 'videoPauseEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPauseEvent'; ofType: null; }; } }; 'videoPlayEventCreate': { name: 'videoPlayEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPlayEvent'; ofType: null; }; } }; 'videoProgressEventCreate': { name: 'videoProgressEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoProgressEvent'; ofType: null; }; } }; 'videoPublishChildren': { name: 'videoPublishChildren'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPublishChildrenResult'; ofType: null; }; } }; 'videoPublishChildrenAndLanguages': { name: 'videoPublishChildrenAndLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPublishChildrenAndLanguagesResult'; ofType: null; }; } }; 'videoSnippetCreate': { name: 'videoSnippetCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoSnippetDelete': { name: 'videoSnippetDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoSnippetUpdate': { name: 'videoSnippetUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoStartEventCreate': { name: 'videoStartEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStartEvent'; ofType: null; }; } }; 'videoStudyQuestionCreate': { name: 'videoStudyQuestionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoStudyQuestionDelete': { name: 'videoStudyQuestionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoStudyQuestionUpdate': { name: 'videoStudyQuestionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoSubtitleCreate': { name: 'videoSubtitleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoSubtitleDelete': { name: 'videoSubtitleDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoSubtitleUpdate': { name: 'videoSubtitleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoTitleCreate': { name: 'videoTitleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoTitleDelete': { name: 'videoTitleDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoTitleUpdate': { name: 'videoTitleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoUpdate': { name: 'videoUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoVariantCreate': { name: 'videoVariantCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariantDelete': { name: 'videoVariantDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariantDownloadCreate': { name: 'videoVariantDownloadCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantDownloadDelete': { name: 'videoVariantDownloadDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantDownloadUpdate': { name: 'videoVariantDownloadUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantUpdate': { name: 'videoVariantUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'visitorUpdate': { name: 'visitorUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; 'visitorUpdateForCurrentUser': { name: 'visitorUpdateForCurrentUser'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; }; }; + 'Mutation': { kind: 'OBJECT'; name: 'Mutation'; fields: { 'audioPreviewCreate': { name: 'audioPreviewCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'audioPreviewDelete': { name: 'audioPreviewDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'audioPreviewUpdate': { name: 'audioPreviewUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'bibleCitationCreate': { name: 'bibleCitationCreate'; type: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; } }; 'bibleCitationDelete': { name: 'bibleCitationDelete'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'bibleCitationUpdate': { name: 'bibleCitationUpdate'; type: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; } }; 'blockDelete': { name: 'blockDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockDeleteAction': { name: 'blockDeleteAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; } }; 'blockDuplicate': { name: 'blockDuplicate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockOrderUpdate': { name: 'blockOrderUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockRestore': { name: 'blockRestore'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockUpdateAction': { name: 'blockUpdateAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Action'; ofType: null; }; } }; 'blockUpdateChatAction': { name: 'blockUpdateChatAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatAction'; ofType: null; }; } }; 'blockUpdateEmailAction': { name: 'blockUpdateEmailAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'EmailAction'; ofType: null; }; } }; 'blockUpdateLinkAction': { name: 'blockUpdateLinkAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'LinkAction'; ofType: null; }; } }; 'blockUpdateNavigateToBlockAction': { name: 'blockUpdateNavigateToBlockAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'NavigateToBlockAction'; ofType: null; }; } }; 'blockUpdatePhoneAction': { name: 'blockUpdatePhoneAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PhoneAction'; ofType: null; }; } }; 'buttonBlockCreate': { name: 'buttonBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ButtonBlock'; ofType: null; }; } }; 'buttonBlockUpdate': { name: 'buttonBlockUpdate'; type: { kind: 'OBJECT'; name: 'ButtonBlock'; ofType: null; } }; 'buttonClickEventCreate': { name: 'buttonClickEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ButtonClickEvent'; ofType: null; }; } }; 'cardBlockCreate': { name: 'cardBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CardBlock'; ofType: null; }; } }; 'cardBlockUpdate': { name: 'cardBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CardBlock'; ofType: null; }; } }; 'chatButtonCreate': { name: 'chatButtonCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatButtonRemove': { name: 'chatButtonRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatButtonUpdate': { name: 'chatButtonUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatOpenEventCreate': { name: 'chatOpenEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatOpenEvent'; ofType: null; }; } }; 'cloudflareR2CompleteMultipart': { name: 'cloudflareR2CompleteMultipart'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2Create': { name: 'cloudflareR2Create'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2Delete': { name: 'cloudflareR2Delete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2MultipartPrepare': { name: 'cloudflareR2MultipartPrepare'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2MultipartPrepared'; ofType: null; }; } }; 'cloudflareUploadComplete': { name: 'cloudflareUploadComplete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'createCloudflareImageFromPrompt': { name: 'createCloudflareImageFromPrompt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createCloudflareUploadByFile': { name: 'createCloudflareUploadByFile'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createCloudflareUploadByUrl': { name: 'createCloudflareUploadByUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createImageBySegmindPrompt': { name: 'createImageBySegmindPrompt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createJourneyEventsExportLog': { name: 'createJourneyEventsExportLog'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyEventsExportLog'; ofType: null; }; } }; 'createKeyword': { name: 'createKeyword'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Keyword'; ofType: null; }; } }; 'createMuxVideoAndQueueUpload': { name: 'createMuxVideoAndQueueUpload'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createMuxVideoUploadByFile': { name: 'createMuxVideoUploadByFile'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createMuxVideoUploadByUrl': { name: 'createMuxVideoUploadByUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createVerificationRequest': { name: 'createVerificationRequest'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'customDomainCheck': { name: 'customDomainCheck'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomainCheck'; ofType: null; }; } }; 'customDomainCreate': { name: 'customDomainCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomainDelete': { name: 'customDomainDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomainUpdate': { name: 'customDomainUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'deleteCloudflareImage': { name: 'deleteCloudflareImage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'deleteMuxVideo': { name: 'deleteMuxVideo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'enableMuxDownload': { name: 'enableMuxDownload'; type: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; } }; 'fixVideoLanguages': { name: 'fixVideoLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'googleSheetsSyncBackfill': { name: 'googleSheetsSyncBackfill'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'googleSheetsSyncCreate': { name: 'googleSheetsSyncCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'googleSheetsSyncDelete': { name: 'googleSheetsSyncDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'hostCreate': { name: 'hostCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'hostDelete': { name: 'hostDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'hostUpdate': { name: 'hostUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'iconBlockCreate': { name: 'iconBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IconBlock'; ofType: null; }; } }; 'iconBlockUpdate': { name: 'iconBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IconBlock'; ofType: null; }; } }; 'imageBlockCreate': { name: 'imageBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ImageBlock'; ofType: null; }; } }; 'imageBlockUpdate': { name: 'imageBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ImageBlock'; ofType: null; }; } }; 'integrationDelete': { name: 'integrationDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Integration'; ofType: null; }; } }; 'integrationGoogleCreate': { name: 'integrationGoogleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGoogle'; ofType: null; }; } }; 'integrationGoogleUpdate': { name: 'integrationGoogleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGoogle'; ofType: null; }; } }; 'integrationGrowthSpacesCreate': { name: 'integrationGrowthSpacesCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; ofType: null; }; } }; 'integrationGrowthSpacesUpdate': { name: 'integrationGrowthSpacesUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; ofType: null; }; } }; 'journeyAiTranslateCreate': { name: 'journeyAiTranslateCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCollectionCreate': { name: 'journeyCollectionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollectionDelete': { name: 'journeyCollectionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollectionUpdate': { name: 'journeyCollectionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCreate': { name: 'journeyCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCustomizationFieldPublisherUpdate': { name: 'journeyCustomizationFieldPublisherUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCustomizationField'; ofType: null; }; }; }; } }; 'journeyCustomizationFieldUserUpdate': { name: 'journeyCustomizationFieldUserUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCustomizationField'; ofType: null; }; }; }; } }; 'journeyDuplicate': { name: 'journeyDuplicate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyFeature': { name: 'journeyFeature'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeyLanguageAiDetect': { name: 'journeyLanguageAiDetect'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'journeyNotificationUpdate': { name: 'journeyNotificationUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; }; } }; 'journeyProfileCreate': { name: 'journeyProfileCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; }; } }; 'journeyProfileUpdate': { name: 'journeyProfileUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; }; } }; 'journeyPublish': { name: 'journeyPublish'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeySimpleUpdate': { name: 'journeySimpleUpdate'; type: { kind: 'SCALAR'; name: 'Json'; ofType: null; } }; 'journeyTemplate': { name: 'journeyTemplate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyThemeCreate': { name: 'journeyThemeCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyThemeDelete': { name: 'journeyThemeDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyThemeUpdate': { name: 'journeyThemeUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyUpdate': { name: 'journeyUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyViewEventCreate': { name: 'journeyViewEventCreate'; type: { kind: 'OBJECT'; name: 'JourneyViewEvent'; ofType: null; } }; 'journeyVisitorExportToGoogleSheet': { name: 'journeyVisitorExportToGoogleSheet'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyVisitorGoogleSheetExportResult'; ofType: null; }; } }; 'journeysArchive': { name: 'journeysArchive'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysDelete': { name: 'journeysDelete'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysRestore': { name: 'journeysRestore'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysTrash': { name: 'journeysTrash'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'multiselectBlockCreate': { name: 'multiselectBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectBlock'; ofType: null; }; } }; 'multiselectBlockUpdate': { name: 'multiselectBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectBlock'; ofType: null; }; } }; 'multiselectOptionBlockCreate': { name: 'multiselectOptionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectOptionBlock'; ofType: null; }; } }; 'multiselectOptionBlockUpdate': { name: 'multiselectOptionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectOptionBlock'; ofType: null; }; } }; 'multiselectSubmissionEventCreate': { name: 'multiselectSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectSubmissionEvent'; ofType: null; }; } }; 'playlistCreate': { name: 'playlistCreate'; type: { kind: 'UNION'; name: 'MutationPlaylistCreateResult'; ofType: null; } }; 'playlistDelete': { name: 'playlistDelete'; type: { kind: 'UNION'; name: 'MutationPlaylistDeleteResult'; ofType: null; } }; 'playlistItemAdd': { name: 'playlistItemAdd'; type: { kind: 'UNION'; name: 'MutationPlaylistItemAddResult'; ofType: null; } }; 'playlistItemAddWithVideoAndLanguageIds': { name: 'playlistItemAddWithVideoAndLanguageIds'; type: { kind: 'UNION'; name: 'MutationPlaylistItemAddWithVideoAndLanguageIdsResult'; ofType: null; } }; 'playlistItemRemove': { name: 'playlistItemRemove'; type: { kind: 'UNION'; name: 'MutationPlaylistItemRemoveResult'; ofType: null; } }; 'playlistItemsReorder': { name: 'playlistItemsReorder'; type: { kind: 'UNION'; name: 'MutationPlaylistItemsReorderResult'; ofType: null; } }; 'playlistUpdate': { name: 'playlistUpdate'; type: { kind: 'UNION'; name: 'MutationPlaylistUpdateResult'; ofType: null; } }; 'qrCodeCreate': { name: 'qrCodeCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodeDelete': { name: 'qrCodeDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodeUpdate': { name: 'qrCodeUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'radioOptionBlockCreate': { name: 'radioOptionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioOptionBlock'; ofType: null; }; } }; 'radioOptionBlockUpdate': { name: 'radioOptionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioOptionBlock'; ofType: null; }; } }; 'radioQuestionBlockCreate': { name: 'radioQuestionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionBlock'; ofType: null; }; } }; 'radioQuestionBlockUpdate': { name: 'radioQuestionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionBlock'; ofType: null; }; } }; 'radioQuestionSubmissionEventCreate': { name: 'radioQuestionSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionSubmissionEvent'; ofType: null; }; } }; 'shortLinkCreate': { name: 'shortLinkCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkCreateResult'; ofType: null; }; } }; 'shortLinkDelete': { name: 'shortLinkDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDeleteResult'; ofType: null; }; } }; 'shortLinkDomainCreate': { name: 'shortLinkDomainCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainCreateResult'; ofType: null; }; } }; 'shortLinkDomainDelete': { name: 'shortLinkDomainDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainDeleteResult'; ofType: null; }; } }; 'shortLinkDomainUpdate': { name: 'shortLinkDomainUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainUpdateResult'; ofType: null; }; } }; 'shortLinkUpdate': { name: 'shortLinkUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkUpdateResult'; ofType: null; }; } }; 'signUpBlockCreate': { name: 'signUpBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SignUpBlock'; ofType: null; }; } }; 'signUpBlockUpdate': { name: 'signUpBlockUpdate'; type: { kind: 'OBJECT'; name: 'SignUpBlock'; ofType: null; } }; 'signUpSubmissionEventCreate': { name: 'signUpSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SignUpSubmissionEvent'; ofType: null; }; } }; 'siteCreate': { name: 'siteCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationSiteCreateResult'; ofType: null; }; } }; 'spacerBlockCreate': { name: 'spacerBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SpacerBlock'; ofType: null; }; } }; 'spacerBlockUpdate': { name: 'spacerBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SpacerBlock'; ofType: null; }; } }; 'stepBlockCreate': { name: 'stepBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; } }; 'stepBlockPositionUpdate': { name: 'stepBlockPositionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; }; }; } }; 'stepBlockUpdate': { name: 'stepBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; } }; 'stepNextEventCreate': { name: 'stepNextEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepNextEvent'; ofType: null; }; } }; 'stepPreviousEventCreate': { name: 'stepPreviousEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepPreviousEvent'; ofType: null; }; } }; 'stepViewEventCreate': { name: 'stepViewEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepViewEvent'; ofType: null; }; } }; 'teamCreate': { name: 'teamCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'teamUpdate': { name: 'teamUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'textResponseBlockCreate': { name: 'textResponseBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TextResponseBlock'; ofType: null; }; } }; 'textResponseBlockUpdate': { name: 'textResponseBlockUpdate'; type: { kind: 'OBJECT'; name: 'TextResponseBlock'; ofType: null; } }; 'textResponseSubmissionEventCreate': { name: 'textResponseSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TextResponseSubmissionEvent'; ofType: null; }; } }; 'triggerUnsplashDownload': { name: 'triggerUnsplashDownload'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'typographyBlockCreate': { name: 'typographyBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TypographyBlock'; ofType: null; }; } }; 'typographyBlockUpdate': { name: 'typographyBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TypographyBlock'; ofType: null; }; } }; 'updateJourneysEmailPreference': { name: 'updateJourneysEmailPreference'; type: { kind: 'OBJECT'; name: 'JourneysEmailPreference'; ofType: null; } }; 'updateVideoAlgoliaIndex': { name: 'updateVideoAlgoliaIndex'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'updateVideoVariantAlgoliaIndex': { name: 'updateVideoVariantAlgoliaIndex'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'userDeleteCheck': { name: 'userDeleteCheck'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserDeleteCheckResult'; ofType: null; }; } }; 'userDeleteJourneysCheck': { name: 'userDeleteJourneysCheck'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserDeleteJourneysCheckResult'; ofType: null; }; } }; 'userDeleteJourneysConfirm': { name: 'userDeleteJourneysConfirm'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserDeleteJourneysConfirmResult'; ofType: null; }; } }; 'userImpersonate': { name: 'userImpersonate'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'userInviteAcceptAll': { name: 'userInviteAcceptAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; }; }; } }; 'userInviteCreate': { name: 'userInviteCreate'; type: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; } }; 'userInviteRemove': { name: 'userInviteRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; } }; 'userJourneyApprove': { name: 'userJourneyApprove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyOpen': { name: 'userJourneyOpen'; type: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; } }; 'userJourneyPromote': { name: 'userJourneyPromote'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyRemove': { name: 'userJourneyRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyRemoveAll': { name: 'userJourneyRemoveAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; }; }; } }; 'userJourneyRequest': { name: 'userJourneyRequest'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userMediaProfileUpdate': { name: 'userMediaProfileUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserMediaProfile'; ofType: null; }; } }; 'userTeamDelete': { name: 'userTeamDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'userTeamInviteAcceptAll': { name: 'userTeamInviteAcceptAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; }; }; } }; 'userTeamInviteCreate': { name: 'userTeamInviteCreate'; type: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; } }; 'userTeamInviteRemove': { name: 'userTeamInviteRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; } }; 'userTeamUpdate': { name: 'userTeamUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'validateEmail': { name: 'validateEmail'; type: { kind: 'OBJECT'; name: 'AuthenticatedUser'; ofType: null; } }; 'videoBlockCreate': { name: 'videoBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoBlock'; ofType: null; }; } }; 'videoBlockUpdate': { name: 'videoBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoBlock'; ofType: null; }; } }; 'videoCollapseEventCreate': { name: 'videoCollapseEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoCollapseEvent'; ofType: null; }; } }; 'videoCompleteEventCreate': { name: 'videoCompleteEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoCompleteEvent'; ofType: null; }; } }; 'videoCreate': { name: 'videoCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoDelete': { name: 'videoDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoDescriptionCreate': { name: 'videoDescriptionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoDescriptionDelete': { name: 'videoDescriptionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoDescriptionUpdate': { name: 'videoDescriptionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoEditionCreate': { name: 'videoEditionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoEditionDelete': { name: 'videoEditionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoEditionUpdate': { name: 'videoEditionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoExpandEventCreate': { name: 'videoExpandEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoExpandEvent'; ofType: null; }; } }; 'videoImageAltCreate': { name: 'videoImageAltCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoImageAltDelete': { name: 'videoImageAltDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoImageAltUpdate': { name: 'videoImageAltUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoOriginCreate': { name: 'videoOriginCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoOriginDelete': { name: 'videoOriginDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoOriginUpdate': { name: 'videoOriginUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoPauseEventCreate': { name: 'videoPauseEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPauseEvent'; ofType: null; }; } }; 'videoPlayEventCreate': { name: 'videoPlayEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPlayEvent'; ofType: null; }; } }; 'videoProgressEventCreate': { name: 'videoProgressEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoProgressEvent'; ofType: null; }; } }; 'videoPublishChildren': { name: 'videoPublishChildren'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPublishChildrenResult'; ofType: null; }; } }; 'videoPublishChildrenAndLanguages': { name: 'videoPublishChildrenAndLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPublishChildrenAndLanguagesResult'; ofType: null; }; } }; 'videoSnippetCreate': { name: 'videoSnippetCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoSnippetDelete': { name: 'videoSnippetDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoSnippetUpdate': { name: 'videoSnippetUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoStartEventCreate': { name: 'videoStartEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStartEvent'; ofType: null; }; } }; 'videoStudyQuestionCreate': { name: 'videoStudyQuestionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoStudyQuestionDelete': { name: 'videoStudyQuestionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoStudyQuestionUpdate': { name: 'videoStudyQuestionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoSubtitleCreate': { name: 'videoSubtitleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoSubtitleDelete': { name: 'videoSubtitleDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoSubtitleUpdate': { name: 'videoSubtitleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoTitleCreate': { name: 'videoTitleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoTitleDelete': { name: 'videoTitleDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoTitleUpdate': { name: 'videoTitleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoUpdate': { name: 'videoUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoVariantCreate': { name: 'videoVariantCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariantDelete': { name: 'videoVariantDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariantDownloadCreate': { name: 'videoVariantDownloadCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantDownloadDelete': { name: 'videoVariantDownloadDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantDownloadUpdate': { name: 'videoVariantDownloadUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantUpdate': { name: 'videoVariantUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'visitorUpdate': { name: 'visitorUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; 'visitorUpdateForCurrentUser': { name: 'visitorUpdateForCurrentUser'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; }; }; 'MutationAudioPreviewCreateInput': { kind: 'INPUT_OBJECT'; name: 'MutationAudioPreviewCreateInput'; isOneOf: false; inputFields: [{ name: 'languageId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'value'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'duration'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'size'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'bitrate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'codec'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; 'MutationAudioPreviewUpdateInput': { kind: 'INPUT_OBJECT'; name: 'MutationAudioPreviewUpdateInput'; isOneOf: false; inputFields: [{ name: 'languageId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'value'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'duration'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'size'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'bitrate'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'codec'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'MutationBibleCitationCreateInput': { kind: 'INPUT_OBJECT'; name: 'MutationBibleCitationCreateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'osisId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'videoId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'bibleBookId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'chapterStart'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'chapterEnd'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'verseStart'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'verseEnd'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'order'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }]; }; @@ -308,7 +308,7 @@ export type introspection_types = { 'StepViewEvent': { kind: 'OBJECT'; name: 'StepViewEvent'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyId': { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'label': { name: 'label'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'value': { name: 'value'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; }; 'StepViewEventCreateInput': { kind: 'INPUT_OBJECT'; name: 'StepViewEventCreateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'blockId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'value'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'String': unknown; - 'Subscription': { kind: 'OBJECT'; name: 'Subscription'; fields: { 'journeyAiTranslateCreateSubscription': { name: 'journeyAiTranslateCreateSubscription'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyAiTranslateProgress'; ofType: null; }; } }; }; }; + 'Subscription': { kind: 'OBJECT'; name: 'Subscription'; fields: { 'journeyAiTranslateCreateSubscription': { name: 'journeyAiTranslateCreateSubscription'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyAiTranslateProgress'; ofType: null; }; } }; 'userDeleteConfirm': { name: 'userDeleteConfirm'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserDeleteConfirmProgress'; ofType: null; }; } }; }; }; 'Tag': { kind: 'OBJECT'; name: 'Tag'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TagName'; ofType: null; }; }; }; } }; 'parentId': { name: 'parentId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; 'service': { name: 'service'; type: { kind: 'ENUM'; name: 'Service'; ofType: null; } }; }; }; 'TagName': { kind: 'OBJECT'; name: 'TagName'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'language': { name: 'language'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Language'; ofType: null; }; } }; 'primary': { name: 'primary'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'value': { name: 'value'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'Taxonomy': { kind: 'OBJECT'; name: 'Taxonomy'; fields: { 'category': { name: 'category'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TaxonomyName'; ofType: null; }; }; }; } }; 'term': { name: 'term'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; @@ -349,6 +349,13 @@ export type introspection_types = { 'UnsplashUserLinks': { kind: 'OBJECT'; name: 'UnsplashUserLinks'; fields: { 'followers': { name: 'followers'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'following': { name: 'following'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'html': { name: 'html'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'likes': { name: 'likes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'photos': { name: 'photos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'portfolio': { name: 'portfolio'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'self': { name: 'self'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'User': { kind: 'INTERFACE'; name: 'User'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; possibleTypes: 'AnonymousUser' | 'AuthenticatedUser'; }; 'UserAgent': { kind: 'OBJECT'; name: 'UserAgent'; fields: { 'browser': { name: 'browser'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Browser'; ofType: null; }; } }; 'device': { name: 'device'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Device'; ofType: null; }; } }; 'os': { name: 'os'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'OperatingSystem'; ofType: null; }; } }; }; }; + 'UserDeleteCheckResult': { kind: 'OBJECT'; name: 'UserDeleteCheckResult'; fields: { 'journeysToDelete': { name: 'journeysToDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeysToRemove': { name: 'journeysToRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeysToTransfer': { name: 'journeysToTransfer'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'logs': { name: 'logs'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserDeleteLogEntry'; ofType: null; }; }; }; } }; 'teamsToDelete': { name: 'teamsToDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'teamsToRemove': { name: 'teamsToRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'teamsToTransfer': { name: 'teamsToTransfer'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'userEmail': { name: 'userEmail'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'userFirstName': { name: 'userFirstName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'userId': { name: 'userId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; + 'UserDeleteConfirmProgress': { kind: 'OBJECT'; name: 'UserDeleteConfirmProgress'; fields: { 'done': { name: 'done'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'log': { name: 'log'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserDeleteLogEntry'; ofType: null; }; } }; 'success': { name: 'success'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; }; }; + 'UserDeleteIdType': { name: 'UserDeleteIdType'; enumValues: 'databaseId' | 'email'; }; + 'UserDeleteJourneysCheckResult': { kind: 'OBJECT'; name: 'UserDeleteJourneysCheckResult'; fields: { 'journeysToDelete': { name: 'journeysToDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeysToRemove': { name: 'journeysToRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeysToTransfer': { name: 'journeysToTransfer'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'logs': { name: 'logs'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserDeleteJourneysLogEntry'; ofType: null; }; }; }; } }; 'teamsToDelete': { name: 'teamsToDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'teamsToRemove': { name: 'teamsToRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'teamsToTransfer': { name: 'teamsToTransfer'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; }; }; + 'UserDeleteJourneysConfirmResult': { kind: 'OBJECT'; name: 'UserDeleteJourneysConfirmResult'; fields: { 'deletedJourneyIds': { name: 'deletedJourneyIds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; } }; 'deletedTeamIds': { name: 'deletedTeamIds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; } }; 'deletedUserJourneyIds': { name: 'deletedUserJourneyIds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; } }; 'deletedUserTeamIds': { name: 'deletedUserTeamIds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; } }; 'logs': { name: 'logs'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserDeleteJourneysLogEntry'; ofType: null; }; }; }; } }; 'success': { name: 'success'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; }; }; + 'UserDeleteJourneysLogEntry': { kind: 'OBJECT'; name: 'UserDeleteJourneysLogEntry'; fields: { 'level': { name: 'level'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'message': { name: 'message'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'timestamp': { name: 'timestamp'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; + 'UserDeleteLogEntry': { kind: 'OBJECT'; name: 'UserDeleteLogEntry'; fields: { 'level': { name: 'level'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'message': { name: 'message'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'timestamp': { name: 'timestamp'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'UserInvite': { kind: 'OBJECT'; name: 'UserInvite'; fields: { 'acceptedAt': { name: 'acceptedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'email': { name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyId': { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'removedAt': { name: 'removedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'senderId': { name: 'senderId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; 'UserInviteCreateInput': { kind: 'INPUT_OBJECT'; name: 'UserInviteCreateInput'; isOneOf: false; inputFields: [{ name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; 'UserJourney': { kind: 'OBJECT'; name: 'UserJourney'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journey': { name: 'journey'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeyId': { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyNotification': { name: 'journeyNotification'; type: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; } }; 'openedAt': { name: 'openedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'role': { name: 'role'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'UserJourneyRole'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'INTERFACE'; name: 'User'; ofType: null; } }; 'userId': { name: 'userId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; };