From 44e55207a59af952f3aacd4b4c7da19063c29ebc Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Wed, 26 Jan 2022 18:40:38 -0700 Subject: [PATCH 01/14] Added auth commands to expo/expo --- packages/expo/bin/cli.ts | 5 + packages/expo/cli/login/index.ts | 51 + packages/expo/cli/logout/index.ts | 38 + packages/expo/cli/register/index.ts | 38 + packages/expo/cli/register/registerAsync.ts | 28 + .../cli/utils/analytics/rudderstackClient.ts | 128 + packages/expo/cli/utils/api.ts | 70 + packages/expo/cli/utils/env.ts | 6 + packages/expo/cli/utils/errors.ts | 41 +- packages/expo/cli/utils/graphql/client.ts | 93 + packages/expo/cli/utils/graphql/generated.ts | 5753 +++++++++++++++++ .../cli/utils/graphql/queries/UserQuery.ts | 40 + packages/expo/cli/utils/link.ts | 36 +- packages/expo/cli/utils/prompts.ts | 28 +- packages/expo/cli/utils/user/UserSettings.ts | 42 + .../expo/cli/utils/user/__mocks__/user.ts | 2 + .../cli/utils/user/__tests__/actions-test.ts | 100 + .../expo/cli/utils/user/__tests__/otp-test.ts | 256 + .../user/__tests__/sessionStorage-test.ts | 66 + .../cli/utils/user/__tests__/user-test.ts | 120 + packages/expo/cli/utils/user/actions.ts | 98 + packages/expo/cli/utils/user/otp.ts | 178 + .../expo/cli/utils/user/sessionStorage.ts | 42 + packages/expo/cli/utils/user/user.ts | 96 + packages/expo/cli/whoami/index.ts | 38 + packages/expo/cli/whoami/whoamiAsync.ts | 13 + packages/expo/e2e/__tests__/login-test.ts | 63 + packages/expo/e2e/__tests__/logout-test.ts | 39 + packages/expo/e2e/__tests__/register-test.ts | 50 + packages/expo/e2e/__tests__/whoami-test.ts | 63 + packages/expo/package.json | 7 + yarn.lock | 197 +- 32 files changed, 7811 insertions(+), 14 deletions(-) create mode 100644 packages/expo/cli/login/index.ts create mode 100644 packages/expo/cli/logout/index.ts create mode 100644 packages/expo/cli/register/index.ts create mode 100644 packages/expo/cli/register/registerAsync.ts create mode 100644 packages/expo/cli/utils/analytics/rudderstackClient.ts create mode 100644 packages/expo/cli/utils/api.ts create mode 100644 packages/expo/cli/utils/graphql/client.ts create mode 100644 packages/expo/cli/utils/graphql/generated.ts create mode 100644 packages/expo/cli/utils/graphql/queries/UserQuery.ts create mode 100644 packages/expo/cli/utils/user/UserSettings.ts create mode 100644 packages/expo/cli/utils/user/__mocks__/user.ts create mode 100644 packages/expo/cli/utils/user/__tests__/actions-test.ts create mode 100644 packages/expo/cli/utils/user/__tests__/otp-test.ts create mode 100644 packages/expo/cli/utils/user/__tests__/sessionStorage-test.ts create mode 100644 packages/expo/cli/utils/user/__tests__/user-test.ts create mode 100644 packages/expo/cli/utils/user/actions.ts create mode 100644 packages/expo/cli/utils/user/otp.ts create mode 100644 packages/expo/cli/utils/user/sessionStorage.ts create mode 100644 packages/expo/cli/utils/user/user.ts create mode 100644 packages/expo/cli/whoami/index.ts create mode 100644 packages/expo/cli/whoami/whoamiAsync.ts create mode 100644 packages/expo/e2e/__tests__/login-test.ts create mode 100644 packages/expo/e2e/__tests__/logout-test.ts create mode 100644 packages/expo/e2e/__tests__/register-test.ts create mode 100644 packages/expo/e2e/__tests__/whoami-test.ts diff --git a/packages/expo/bin/cli.ts b/packages/expo/bin/cli.ts index fc832ea25e30b..035951689a7e5 100755 --- a/packages/expo/bin/cli.ts +++ b/packages/expo/bin/cli.ts @@ -11,6 +11,11 @@ const commands: { [command: string]: () => Promise } = { // Add a new command here prebuild: () => import('../cli/prebuild').then((i) => i.expoPrebuild), config: () => import('../cli/config').then((i) => i.expoConfig), + // Auth + login: () => import('../cli/login').then((i) => i.expoLogin), + logout: () => import('../cli/logout').then((i) => i.expoLogout), + register: () => import('../cli/register').then((i) => i.expoRegister), + whoami: () => import('../cli/whoami').then((i) => i.expoWhoami), }; const args = arg( diff --git a/packages/expo/cli/login/index.ts b/packages/expo/cli/login/index.ts new file mode 100644 index 0000000000000..2ae3c2f06b9b8 --- /dev/null +++ b/packages/expo/cli/login/index.ts @@ -0,0 +1,51 @@ +#!/usr/bin/env node +import chalk from 'chalk'; + +import { Command } from '../../bin/cli'; +import * as Log from '../log'; +import { assertArgs } from '../utils/args'; +import { logCmdError } from '../utils/errors'; + +export const expoLogin: Command = async (argv) => { + const args = assertArgs( + { + // Types + '--help': Boolean, + '--username': String, + '--password': String, + '--otp': String, + // Aliases + '-h': '--help', + '-u': '--username', + '-p': '--password', + }, + argv + ); + + if (args['--help']) { + Log.exit( + chalk` + {bold Description} + Login to an Expo account + + {bold Usage} + $ npx expo login + + Options + -u, --username Username + -p, --password Password + --otp One-time password from your 2FA device + -h, --help Output usage information + `, + 0 + ); + } + + const { showLoginPromptAsync } = await import('../utils/user/actions'); + return showLoginPromptAsync({ + // Parsed options + username: args['--username'], + password: args['--password'], + otp: args['--otp'], + }).catch(logCmdError); +}; diff --git a/packages/expo/cli/logout/index.ts b/packages/expo/cli/logout/index.ts new file mode 100644 index 0000000000000..603fd81384a6f --- /dev/null +++ b/packages/expo/cli/logout/index.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env node +import chalk from 'chalk'; + +import { Command } from '../../bin/cli'; +import * as Log from '../log'; +import { assertArgs } from '../utils/args'; +import { logCmdError } from '../utils/errors'; + +export const expoLogout: Command = async (argv) => { + const args = assertArgs( + { + // Types + '--help': Boolean, + // Aliases + '-h': '--help', + }, + argv + ); + + if (args['--help']) { + Log.exit( + chalk` + {bold Description} + Logout of an Expo account + + {bold Usage} + $ npx expo logout + + Options + -h, --help Output usage information + `, + 0 + ); + } + + const { logoutAsync } = await import('../utils/user/user'); + return logoutAsync().catch(logCmdError); +}; diff --git a/packages/expo/cli/register/index.ts b/packages/expo/cli/register/index.ts new file mode 100644 index 0000000000000..700f5bf7663de --- /dev/null +++ b/packages/expo/cli/register/index.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env node +import chalk from 'chalk'; + +import { Command } from '../../bin/cli'; +import * as Log from '../log'; +import { assertArgs } from '../utils/args'; +import { logCmdError } from '../utils/errors'; + +export const expoRegister: Command = async (argv) => { + const args = assertArgs( + { + // Types + '--help': Boolean, + // Aliases + '-h': '--help', + }, + argv + ); + + if (args['--help']) { + Log.exit( + chalk` + {bold Description} + Sign up for a new Expo account + + {bold Usage} + $ npx expo register + + Options + -h, --help Output usage information + `, + 0 + ); + } + + const { registerAsync } = await import('./registerAsync'); + return registerAsync().catch(logCmdError); +}; diff --git a/packages/expo/cli/register/registerAsync.ts b/packages/expo/cli/register/registerAsync.ts new file mode 100644 index 0000000000000..29d57613aac45 --- /dev/null +++ b/packages/expo/cli/register/registerAsync.ts @@ -0,0 +1,28 @@ +import openBrowserAsync from 'better-opn'; + +import { CI } from '../utils/env'; +import { CommandError } from '../utils/errors'; +import { ora } from '../utils/ora'; + +export async function registerAsync() { + const REGISTRATION_URL = `https://expo.dev/signup`; + if (CI) { + throw new CommandError( + 'NON_INTERACTIVE', + `Cannot register an account in CI. Register an account at: ${REGISTRATION_URL}` + ); + } + + const spinner = ora(`Opening ${REGISTRATION_URL}`).start(); + try { + const opened = openBrowserAsync(REGISTRATION_URL); + + if (opened) { + spinner.succeed(`Opened ${REGISTRATION_URL}`); + } + return; + } catch (error) { + spinner.fail(`Unable to open a web browser. Register an account at: ${REGISTRATION_URL}`); + throw error; + } +} diff --git a/packages/expo/cli/utils/analytics/rudderstackClient.ts b/packages/expo/cli/utils/analytics/rudderstackClient.ts new file mode 100644 index 0000000000000..9dc753e84a9f0 --- /dev/null +++ b/packages/expo/cli/utils/analytics/rudderstackClient.ts @@ -0,0 +1,128 @@ +import RudderAnalytics from '@expo/rudder-sdk-node'; +import os from 'os'; +import { URL } from 'url'; +import { v4 as uuidv4 } from 'uuid'; + +import UserSettings from '../user/UserSettings'; + +const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = { + darwin: 'Mac', + win32: 'Windows', + linux: 'Linux', +}; + +let rudderstackClient: RudderAnalytics | null = null; +let userIdentified = false; +let identifyData: { + userId: string; + deviceId: string; + traits: Record; +} | null = null; + +export async function initAsync(): Promise { + // TODO: remove after some time + const amplitudeEnabled = await UserSettings.getAsync('amplitudeEnabled', null); + if (amplitudeEnabled !== null) { + await UserSettings.setAsync('analyticsEnabled', amplitudeEnabled); + await UserSettings.deleteKeyAsync('amplitudeEnabled'); + } + const amplitudeDeviceId = await UserSettings.getAsync('amplitudeDeviceId', null); + if (amplitudeDeviceId !== null) { + await UserSettings.setAsync('analyticsDeviceId', amplitudeDeviceId); + await UserSettings.deleteKeyAsync('amplitudeDeviceId'); + } + // TODO: cut here + if (process.env.DISABLE_EAS_ANALYTICS) { + await UserSettings.setAsync('analyticsEnabled', false); + } + + const analyticsEnabled = await UserSettings.getAsync('analyticsEnabled', true); + if (analyticsEnabled) { + const config = + process.env.EXPO_STAGING || process.env.EXPO_LOCAL + ? { + // staging environment + rudderstackWriteKey: '1wpX20Da4ltFGSXbPFYUL00Chb7', + rudderstackDataPlaneURL: 'https://cdp.expo.dev', + } + : { + // prod environment + rudderstackWriteKey: '1wpXLFxmujq86etH6G6cc90hPcC', + rudderstackDataPlaneURL: 'https://cdp.expo.dev', + }; + + rudderstackClient = new RudderAnalytics( + config.rudderstackWriteKey, + new URL('/v1/batch', config.rudderstackDataPlaneURL).toString(), + { + flushInterval: 300, + } + ); + } +} + +export async function setUserDataAsync(userId: string, traits: Record): Promise { + const savedDeviceId = await UserSettings.getAsync('analyticsDeviceId', null); + const deviceId = savedDeviceId ?? uuidv4(); + if (!savedDeviceId) { + await UserSettings.setAsync('analyticsDeviceId', deviceId); + } + + identifyData = { + userId, + deviceId, + traits, + }; + + ensureUserIdentified(); +} + +export async function flushAsync(): Promise { + if (rudderstackClient) { + await rudderstackClient.flush(); + } +} + +export function logEvent(name: string, properties: Record = {}): void { + if (!rudderstackClient) { + return; + } + ensureUserIdentified(); + + const { userId, deviceId } = identifyData ?? {}; + const commonEventProperties = { source_version: process.env.__EXPO_VERSION, source: 'expo' }; + + const identity = { userId: userId ?? undefined, anonymousId: deviceId ?? uuidv4() }; + rudderstackClient.track({ + event: name, + properties: { ...properties, ...commonEventProperties }, + ...identity, + context: getRudderStackContext(), + }); +} + +function ensureUserIdentified(): void { + if (!rudderstackClient || userIdentified || !identifyData) { + return; + } + + rudderstackClient.identify({ + userId: identifyData.userId, + anonymousId: identifyData.deviceId, + traits: identifyData.traits, + }); + userIdentified = true; +} + +function getRudderStackContext(): Record { + const platform = PLATFORM_TO_ANALYTICS_PLATFORM[os.platform()] || os.platform(); + return { + os: { name: platform, version: os.release() }, + device: { type: platform, model: platform }, + app: { name: 'expo', version: process.env.__EXPO_VERSION ?? undefined }, + }; +} + +export enum AnalyticsEvent { + ACTION = 'action', // generic event type which is used to determine the 'daily active user' stat, include an `action: eas ${subcommand}` property inside of the event properties object +} diff --git a/packages/expo/cli/utils/api.ts b/packages/expo/cli/utils/api.ts new file mode 100644 index 0000000000000..bc887be04b374 --- /dev/null +++ b/packages/expo/cli/utils/api.ts @@ -0,0 +1,70 @@ +import got, { HTTPError, NormalizedOptions, RequestError } from 'got'; + +import { EXPO_LOCAL, EXPO_STAGING } from './env'; +import { ApiV2Error } from './errors'; +import { getAccessToken, getSessionSecret } from './user/sessionStorage'; + +export const apiClient = got.extend({ + prefixUrl: getExpoApiBaseUrl() + '/v2/', + hooks: { + beforeRequest: [ + (options: NormalizedOptions) => { + const token = getAccessToken(); + if (token) { + options.headers.authorization = `Bearer ${token}`; + return; + } + const sessionSecret = getSessionSecret(); + if (sessionSecret) { + options.headers['expo-session'] = sessionSecret; + } + }, + ], + beforeError: [ + (error: RequestError): RequestError => { + if (error instanceof HTTPError) { + let result: { [key: string]: any }; + try { + result = JSON.parse(error.response.body as string); + } catch (e2) { + return error; + } + if (result.errors?.length) { + return new ApiV2Error(error, result.errors[0]); + } + } + return error; + }, + ], + }, +}); + +export function getExpoApiBaseUrl(): string { + if (EXPO_STAGING) { + return `https://staging-api.expo.dev`; + } else if (EXPO_LOCAL) { + return `http://127.0.0.1:3000`; + } else { + return `https://api.expo.dev`; + } +} + +export function getExpoWebsiteBaseUrl(): string { + if (EXPO_STAGING) { + return `https://staging.expo.dev`; + } else if (EXPO_LOCAL) { + return `http://expo.test`; + } else { + return `https://expo.dev`; + } +} + +export function getEASUpdateURL(projectId: string): string { + if (EXPO_STAGING) { + return new URL(projectId, `https://staging-u.expo.dev`).href; + } else if (EXPO_LOCAL) { + return new URL(`expo-updates/${projectId}`, `http://127.0.0.1:3000`).href; + } else { + return new URL(projectId, `https://u.expo.dev`).href; + } +} diff --git a/packages/expo/cli/utils/env.ts b/packages/expo/cli/utils/env.ts index 8293a285348f1..497dc62e6eda1 100644 --- a/packages/expo/cli/utils/env.ts +++ b/packages/expo/cli/utils/env.ts @@ -8,5 +8,11 @@ export const EXPO_PROFILE = boolish('EXPO_PROFILE', false); /** Enable debug logging */ export const EXPO_DEBUG = boolish('EXPO_DEBUG', false); +/** Enable staging API environment */ +export const EXPO_STAGING = boolish('EXPO_STAGING', false); + +/** Enable local API environment */ +export const EXPO_LOCAL = boolish('EXPO_LOCAL', false); + /** Is running in non-interactive CI mode */ export const CI = boolish('CI', false); diff --git a/packages/expo/cli/utils/errors.ts b/packages/expo/cli/utils/errors.ts index 2a1b4f38714ec..1110e6a7e7467 100644 --- a/packages/expo/cli/utils/errors.ts +++ b/packages/expo/cli/utils/errors.ts @@ -1,4 +1,35 @@ +import { JSONValue } from '@expo/json-file'; +import { AssertionError } from 'assert'; +import chalk from 'chalk'; +import { HTTPError, RequestError } from 'got/dist/source'; + import { exit } from '../log'; +import { EXPO_DEBUG } from './env'; + +export class ApiV2Error extends RequestError { + readonly name = 'ApiV2Error'; + readonly expoApiV2ErrorCode: string; + readonly expoApiV2ErrorDetails?: JSONValue; + readonly expoApiV2ErrorServerStack?: string; + readonly expoApiV2ErrorMetadata?: object; + + constructor( + originalError: HTTPError, + response: { + message: string; + code: string; + stack?: string; + details?: JSONValue; + metadata?: object; + } + ) { + super(response.message, originalError, originalError.request); + this.expoApiV2ErrorCode = response.code; + this.expoApiV2ErrorDetails = response.details; + this.expoApiV2ErrorServerStack = response.stack; + this.expoApiV2ErrorMetadata = response.metadata; + } +} const ERROR_PREFIX = 'Error: '; @@ -48,6 +79,14 @@ export function logCmdError(error: Error): never { if (error instanceof AbortCommandError || error instanceof SilentError) { // Do nothing, this is used for prompts or other cases that were custom logged. process.exit(0); + } else if ( + error instanceof CommandError || + error instanceof AssertionError || + error instanceof ApiV2Error + ) { + // Print the stack trace in debug mode only. + exit(chalk.red(error.toString()) + (EXPO_DEBUG ? '\n' + chalk.gray(error.stack) : '')); } - exit(error.toString()); + + exit(chalk.red(error.toString()) + '\n' + chalk.gray(error.stack)); } diff --git a/packages/expo/cli/utils/graphql/client.ts b/packages/expo/cli/utils/graphql/client.ts new file mode 100644 index 0000000000000..6617c775ea05e --- /dev/null +++ b/packages/expo/cli/utils/graphql/client.ts @@ -0,0 +1,93 @@ +import { + cacheExchange, + Client, + CombinedError as GraphqlError, + createClient as createUrqlClient, + dedupExchange, + fetchExchange, + OperationContext, + OperationResult, + PromisifiedSource, + TypedDocumentNode, +} from '@urql/core'; +import { retryExchange } from '@urql/exchange-retry'; +import { DocumentNode } from 'graphql'; +import fetch from 'node-fetch'; + +import * as Log from '../../log'; +import { getExpoApiBaseUrl } from '../api'; +import { getAccessToken, getSessionSecret } from '../user/sessionStorage'; + +type AccessTokenHeaders = { + authorization: string; +}; + +type SessionHeaders = { + 'expo-session': string; +}; + +export const graphqlClient = createUrqlClient({ + url: getExpoApiBaseUrl() + '/graphql', + exchanges: [ + dedupExchange, + cacheExchange, + retryExchange({ + maxDelayMs: 4000, + retryIf: (err) => + !!(err && (err.networkError || err.graphQLErrors.some((e) => e?.extensions?.isTransient))), + }), + fetchExchange, + ], + // @ts-expect-error Type 'typeof fetch' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise'. + fetch, + fetchOptions: (): { headers?: AccessTokenHeaders | SessionHeaders } => { + const token = getAccessToken(); + if (token) { + return { + headers: { + authorization: `Bearer ${token}`, + }, + }; + } + const sessionSecret = getSessionSecret(); + if (sessionSecret) { + return { + headers: { + 'expo-session': sessionSecret, + }, + }; + } + return {}; + }, +}) as StricterClient; + +/* Please specify additionalTypenames in your Graphql queries */ +export interface StricterClient extends Client { + // eslint-disable-next-line @typescript-eslint/ban-types + query( + query: DocumentNode | TypedDocumentNode | string, + variables: Variables | undefined, + context: Partial & { additionalTypenames: string[] } + ): PromisifiedSource>; +} + +export async function withErrorHandlingAsync(promise: Promise>): Promise { + const { data, error } = await promise; + + if (error) { + if (error.graphQLErrors.some((e) => e?.extensions?.isTransient)) { + Log.error(`We've encountered a transient error, please try again shortly.`); + } + throw error; + } + + // Check for malfolmed response. This only checks the root query existence, + // It doesn't affect returning responses with empty resultset. + if (!data) { + throw new Error('Returned query result data is null!'); + } + + return data; +} + +export { GraphqlError }; diff --git a/packages/expo/cli/utils/graphql/generated.ts b/packages/expo/cli/utils/graphql/generated.ts new file mode 100644 index 0000000000000..c777daa604816 --- /dev/null +++ b/packages/expo/cli/utils/graphql/generated.ts @@ -0,0 +1,5753 @@ +/** + * This file was generated using GraphQL Codegen + * Command: yarn generate-graphql-code + * Run this during development for automatic type generation when editing GraphQL documents + * For more info and docs, visit https://graphql-code-generator.com/ + */ + +export type Maybe = T | null; +export type Exact = { [K in keyof T]: T[K] }; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; + /** Date custom scalar type */ + DateTime: any; + /** The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */ + JSON: any; + /** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */ + JSONObject: any; + /** The `Upload` scalar type represents a file upload. */ + Upload: any; +}; + +export type RootQuery = { + __typename?: 'RootQuery'; + /** + * This is a placeholder field + * @deprecated Not used. + */ + _doNotUse?: Maybe; + /** fetch all updates in a group */ + updatesByGroup: Array; + /** Top-level query object for querying Accounts. */ + account: AccountQuery; + /** Top-level query object for querying Actors. */ + actor: ActorQuery; + /** Top-level query object for querying Apple Device registration requests. */ + appleDeviceRegistrationRequest: AppleDeviceRegistrationRequestQuery; + /** Top-level query object for querying Apple Teams. */ + appleTeam: AppleTeamQuery; + app: AppQuery; + /** + * Look up app by app id + * @deprecated Use 'byId' field under 'app'. + */ + appByAppId?: Maybe; + /** + * Public apps in the app directory + * @deprecated Use 'all' field under 'app'. + */ + allPublicApps?: Maybe>>; + asset: AssetQuery; + /** Top-level query object for querying BuildPublicData publicly. */ + buildPublicData: BuildPublicDataQuery; + buildJobs: BuildJobQuery; + builds: BuildQuery; + clientBuilds: ClientBuildQuery; + /** Top-level query object for querying Experimentation configuration. */ + experimentation: ExperimentationQuery; + /** Top-level query object for querying Stripe Invoices. */ + invoice: InvoiceQuery; + project: ProjectQuery; + snack: SnackQuery; + submissions: SubmissionQuery; + /** Top-level query object for querying UserInvitationPublicData publicly. */ + userInvitationPublicData: UserInvitationPublicDataQuery; + /** Top-level query object for querying Users. */ + user: UserQuery; + /** @deprecated Use 'byId' field under 'user'. */ + userByUserId?: Maybe; + /** @deprecated Use 'byUsername' field under 'user'. */ + userByUsername?: Maybe; + /** + * If authenticated as a typical end user, this is the appropriate top-level + * query object + */ + me?: Maybe; + /** + * If authenticated as a typical end user, this is the appropriate top-level + * query object + */ + viewer?: Maybe; + /** + * If authenticated as a any type of Actor, this is the appropriate top-level + * query object + */ + meActor?: Maybe; + /** Top-level query object for querying Webhooks. */ + webhook: WebhookQuery; +}; + + +export type RootQueryUpdatesByGroupArgs = { + group: Scalars['ID']; +}; + + +export type RootQueryAppByAppIdArgs = { + appId: Scalars['String']; +}; + + +export type RootQueryAllPublicAppsArgs = { + filter: AppsFilter; + sort: AppSort; + offset?: Maybe; + limit?: Maybe; +}; + + +export type RootQueryUserByUserIdArgs = { + userId: Scalars['String']; +}; + + +export type RootQueryUserByUsernameArgs = { + username: Scalars['String']; +}; + +export type Update = ActivityTimelineProjectActivity & { + __typename?: 'Update'; + id: Scalars['ID']; + actor?: Maybe; + activityTimestamp: Scalars['DateTime']; + branchId: Scalars['ID']; + platform: Scalars['String']; + manifestFragment: Scalars['String']; + runtimeVersion: Scalars['String']; + group: Scalars['String']; + updatedAt: Scalars['DateTime']; + createdAt: Scalars['DateTime']; + message?: Maybe; + branch: UpdateBranch; + manifestPermalink: Scalars['String']; +}; + +export type ActivityTimelineProjectActivity = { + id: Scalars['ID']; + actor?: Maybe; + activityTimestamp: Scalars['DateTime']; +}; + +/** A user or robot that can authenticate with Expo services and be a member of accounts. */ +export type Actor = { + id: Scalars['ID']; + firstName?: Maybe; + created: Scalars['DateTime']; + isExpoAdmin: Scalars['Boolean']; + /** + * Best-effort human readable name for this actor for use in user interfaces during action attribution. + * For example, when displaying a sentence indicating that actor X created a build or published an update. + */ + displayName: Scalars['String']; + /** Associated accounts */ + accounts: Array; + /** Access Tokens belonging to this actor */ + accessTokens: Array; + /** + * Server feature gate values for this actor, optionally filtering by desired gates. + * Only resolves for the viewer. + */ + featureGates: Scalars['JSONObject']; +}; + + +/** A user or robot that can authenticate with Expo services and be a member of accounts. */ +export type ActorFeatureGatesArgs = { + filter?: Maybe>; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type Account = { + __typename?: 'Account'; + id: Scalars['ID']; + name: Scalars['String']; + isCurrent: Scalars['Boolean']; + pushSecurityEnabled: Scalars['Boolean']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; + /** Offers set on this account */ + offers?: Maybe>; + /** Snacks associated with this account */ + snacks: Array; + /** Apps associated with this account */ + apps: Array; + appCount: Scalars['Int']; + /** Build Jobs associated with this account */ + buildJobs: Array; + /** (EAS Build) Builds associated with this account */ + builds: Array; + /** + * Coalesced Build (EAS) or BuildJob (Classic) for all apps belonging to this account. + * @deprecated Use activityTimelineProjectActivities with filterTypes instead + */ + buildOrBuildJobs: Array; + /** Coalesced project activity for all apps belonging to this account. */ + activityTimelineProjectActivities: Array; + /** Owning User of this account if personal account */ + owner?: Maybe; + /** Actors associated with this account and permissions they hold */ + users: Array; + /** Pending user invitations for this account */ + userInvitations: Array; + /** Billing information */ + billing?: Maybe; + /** Subscription info visible to members that have VIEWER role */ + subscription?: Maybe; + /** iOS credentials for account */ + appleTeams: Array; + appleAppIdentifiers: Array; + appleDistributionCertificates: Array; + applePushKeys: Array; + appleProvisioningProfiles: Array; + appleDevices: Array; + appStoreConnectApiKeys: Array; + /** Android credentials for account */ + googleServiceAccountKeys: Array; + /** Environment secrets for an account */ + environmentSecrets: Array; + /** @deprecated Legacy access tokens are deprecated */ + accessTokens: Array>; + /** @deprecated Legacy access tokens are deprecated */ + requiresAccessTokenForPushSecurity: Scalars['Boolean']; + /** @deprecated See isCurrent */ + unlimitedBuilds: Scalars['Boolean']; + /** @deprecated Build packs are no longer supported */ + availableBuilds?: Maybe; + /** @deprecated No longer needed */ + subscriptionChangesPending?: Maybe; + /** @deprecated Build packs are no longer supported */ + willAutoRenewBuilds?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountSnacksArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountAppsArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; + includeUnpublished?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountBuildJobsArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; + status?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountBuildsArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; + status?: Maybe; + platform?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountBuildOrBuildJobsArgs = { + limit: Scalars['Int']; + createdBefore?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountActivityTimelineProjectActivitiesArgs = { + limit: Scalars['Int']; + createdBefore?: Maybe; + filterTypes?: Maybe>; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountAppleTeamsArgs = { + appleTeamIdentifier?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountAppleAppIdentifiersArgs = { + bundleIdentifier?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountAppleProvisioningProfilesArgs = { + appleAppIdentifierId?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountAppleDevicesArgs = { + identifier?: Maybe; +}; + + +/** + * An account is a container owning projects, credentials, billing and other organization + * data and settings. Actors may own and be members of accounts. + */ +export type AccountEnvironmentSecretsArgs = { + filterNames?: Maybe>; +}; + +export type Offer = { + __typename?: 'Offer'; + id: Scalars['ID']; + stripeId: Scalars['ID']; + price: Scalars['Int']; + quantity?: Maybe; + trialLength?: Maybe; + type: OfferType; + features?: Maybe>>; + prerequisite?: Maybe; +}; + +export enum OfferType { + /** Term subscription */ + Subscription = 'SUBSCRIPTION', + /** Advanced Purchase of Paid Resource */ + Prepaid = 'PREPAID', + /** Addon, or supplementary subscription */ + Addon = 'ADDON' +} + +export enum Feature { + /** Top Tier Support */ + Support = 'SUPPORT', + /** Share access to projects */ + Teams = 'TEAMS', + /** Priority Builds */ + Builds = 'BUILDS', + /** Funds support for open source development */ + OpenSource = 'OPEN_SOURCE' +} + +export type OfferPrerequisite = { + __typename?: 'OfferPrerequisite'; + type: Scalars['String']; + stripeIds: Array; +}; + +export type Snack = Project & { + __typename?: 'Snack'; + id: Scalars['ID']; + hashId: Scalars['String']; + /** Name of the Snack, e.g. "My Snack" */ + name: Scalars['String']; + /** Full name of the Snack, e.g. "@john/mysnack", "@snack/245631" */ + fullName: Scalars['String']; + /** Description of the Snack */ + description: Scalars['String']; + /** @deprecated Field no longer supported */ + iconUrl?: Maybe; + /** Slug name, e.g. "mysnack", "245631" */ + slug: Scalars['String']; + /** Name of the user that created the Snack, or "snack" when the Snack was saved anonymously */ + username: Scalars['String']; + /** Date and time the Snack was last updated */ + updated: Scalars['DateTime']; + published: Scalars['Boolean']; + /** Draft status, which is true when the Snack was not saved explicitly, but auto-saved */ + isDraft: Scalars['Boolean']; +}; + +export type Project = { + id: Scalars['ID']; + name: Scalars['String']; + fullName: Scalars['String']; + description: Scalars['String']; + slug: Scalars['String']; + updated: Scalars['DateTime']; + published: Scalars['Boolean']; + username: Scalars['String']; + /** @deprecated Field no longer supported */ + iconUrl?: Maybe; +}; + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type App = Project & { + __typename?: 'App'; + id: Scalars['ID']; + name: Scalars['String']; + fullName: Scalars['String']; + description: Scalars['String']; + slug: Scalars['String']; + ownerAccount: Account; + privacySetting: AppPrivacy; + pushSecurityEnabled: Scalars['Boolean']; + /** Whether there have been any classic update publishes */ + published: Scalars['Boolean']; + /** Time of last classic update publish */ + updated: Scalars['DateTime']; + /** ID of latest classic update release */ + latestReleaseId: Scalars['ID']; + /** Whether the latest classic update publish is using a deprecated SDK version */ + isDeprecated: Scalars['Boolean']; + /** SDK version of the latest classic update publish, 0.0.0 otherwise */ + sdkVersion: Scalars['String']; + /** Classic update release channel names (to be removed) */ + releaseChannels: Array; + /** Classic update release channel names that have at least one build */ + buildsReleaseChannels: Array; + /** githubUrl field from most recent classic update manifest */ + githubUrl?: Maybe; + /** android.playStoreUrl field from most recent classic update manifest */ + playStoreUrl?: Maybe; + /** ios.appStoreUrl field from most recent classic update manifest */ + appStoreUrl?: Maybe; + /** Info about the icon specified in the most recent classic update manifest */ + icon?: Maybe; + /** (EAS Build) Builds associated with this app */ + builds: Array; + buildJobs: Array; + /** + * Coalesced Build (EAS) or BuildJob (Classic) items for this app. + * @deprecated Use activityTimelineProjectActivities with filterTypes instead + */ + buildOrBuildJobs: Array; + /** EAS Submissions associated with this app */ + submissions: Array; + /** Deployments associated with this app */ + deployments: Array; + deployment?: Maybe; + /** iOS app credentials for the project */ + iosAppCredentials: Array; + /** Android app credentials for the project */ + androidAppCredentials: Array; + /** EAS channels owned by an app */ + updateChannels: Array; + /** get an EAS channel owned by the app by name */ + updateChannelByName?: Maybe; + /** EAS branches owned by an app */ + updateBranches: Array; + /** get an EAS branch owned by the app by name */ + updateBranchByName?: Maybe; + /** Coalesced project activity for an app */ + activityTimelineProjectActivities: Array; + /** Environment secrets for an app */ + environmentSecrets: Array; + /** Webhooks for an app */ + webhooks: Array; + /** @deprecated Use ownerAccount.name instead */ + username: Scalars['String']; + /** @deprecated Field no longer supported */ + iconUrl?: Maybe; + /** @deprecated Use 'privacySetting' instead. */ + privacy: Scalars['String']; + /** @deprecated Field no longer supported */ + lastPublishedTime: Scalars['DateTime']; + /** @deprecated Field no longer supported */ + packageUsername: Scalars['String']; + /** @deprecated Field no longer supported */ + packageName: Scalars['String']; + /** @deprecated Legacy access tokens are deprecated */ + accessTokens: Array>; + /** @deprecated Legacy access tokens are deprecated */ + requiresAccessTokenForPushSecurity: Scalars['Boolean']; + /** @deprecated 'likes' have been deprecated. */ + isLikedByMe: Scalars['Boolean']; + /** @deprecated 'likes' have been deprecated. */ + likeCount: Scalars['Int']; + /** @deprecated 'likes' have been deprecated. */ + trendScore: Scalars['Float']; + /** @deprecated 'likes' have been deprecated. */ + likedBy: Array>; + /** @deprecated Field no longer supported */ + users?: Maybe>>; + /** @deprecated Field no longer supported */ + releases: Array>; + latestReleaseForReleaseChannel?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppBuildsArgs = { + filter?: Maybe; + offset: Scalars['Int']; + limit: Scalars['Int']; + status?: Maybe; + platform?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppBuildJobsArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; + status?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppBuildOrBuildJobsArgs = { + limit: Scalars['Int']; + createdBefore?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppSubmissionsArgs = { + filter: SubmissionFilter; + offset: Scalars['Int']; + limit: Scalars['Int']; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppDeploymentsArgs = { + limit: Scalars['Int']; + mostRecentlyUpdatedAt?: Maybe; + options?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppDeploymentArgs = { + channel: Scalars['String']; + runtimeVersion: Scalars['String']; + options?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppIosAppCredentialsArgs = { + filter?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppAndroidAppCredentialsArgs = { + filter?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppUpdateChannelsArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppUpdateChannelByNameArgs = { + name: Scalars['String']; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppUpdateBranchesArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppUpdateBranchByNameArgs = { + name: Scalars['String']; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppActivityTimelineProjectActivitiesArgs = { + limit: Scalars['Int']; + createdBefore?: Maybe; + filterTypes?: Maybe>; + filterPlatforms?: Maybe>; + filterReleaseChannels?: Maybe>; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppEnvironmentSecretsArgs = { + filterNames?: Maybe>; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppWebhooksArgs = { + filter?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppLikedByArgs = { + offset?: Maybe; + limit?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppReleasesArgs = { + platform: AppPlatform; + offset?: Maybe; + limit?: Maybe; +}; + + +/** Represents an Exponent App (or Experience in legacy terms) */ +export type AppLatestReleaseForReleaseChannelArgs = { + platform: AppPlatform; + releaseChannel: Scalars['String']; +}; + +export enum AppPrivacy { + Public = 'PUBLIC', + Unlisted = 'UNLISTED', + Hidden = 'HIDDEN' +} + +export type AppIcon = { + __typename?: 'AppIcon'; + url: Scalars['String']; + primaryColor?: Maybe; + originalUrl: Scalars['String']; + /** Nullable color palette of the app icon. If null, color palette couldn't be retrieved from external service (imgix) */ + colorPalette?: Maybe; +}; + + +export type BuildFilter = { + platform?: Maybe; + status?: Maybe; + distribution?: Maybe; + channel?: Maybe; + appVersion?: Maybe; + appBuildVersion?: Maybe; + sdkVersion?: Maybe; + runtimeVersion?: Maybe; + appIdentifier?: Maybe; + buildProfile?: Maybe; + gitCommitHash?: Maybe; +}; + +export enum AppPlatform { + Ios = 'IOS', + Android = 'ANDROID' +} + +export enum BuildStatus { + New = 'NEW', + InQueue = 'IN_QUEUE', + InProgress = 'IN_PROGRESS', + Errored = 'ERRORED', + Finished = 'FINISHED', + Canceled = 'CANCELED' +} + +export enum DistributionType { + Store = 'STORE', + Internal = 'INTERNAL', + Simulator = 'SIMULATOR' +} + +/** Represents an EAS Build */ +export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & { + __typename?: 'Build'; + id: Scalars['ID']; + actor?: Maybe; + activityTimestamp: Scalars['DateTime']; + project: Project; + /** @deprecated User type is deprecated */ + initiatingUser?: Maybe; + initiatingActor?: Maybe; + cancelingActor?: Maybe; + artifacts?: Maybe; + logFiles: Array; + updatedAt?: Maybe; + createdAt?: Maybe; + status: BuildStatus; + expirationDate?: Maybe; + platform: AppPlatform; + appVersion?: Maybe; + appBuildVersion?: Maybe; + sdkVersion?: Maybe; + runtimeVersion?: Maybe; + reactNativeVersion?: Maybe; + releaseChannel?: Maybe; + channel?: Maybe; + metrics?: Maybe; + distribution?: Maybe; + iosEnterpriseProvisioning?: Maybe; + buildProfile?: Maybe; + gitCommitHash?: Maybe; + isGitWorkingTreeDirty?: Maybe; + error?: Maybe; + submissions: Array; +}; + +export type BuildOrBuildJob = { + id: Scalars['ID']; +}; + +/** Represents a human (not robot) actor. */ +export type User = Actor & { + __typename?: 'User'; + id: Scalars['ID']; + username: Scalars['String']; + email?: Maybe; + firstName?: Maybe; + lastName?: Maybe; + fullName?: Maybe; + profilePhoto: Scalars['String']; + created: Scalars['DateTime']; + industry?: Maybe; + location?: Maybe; + appCount: Scalars['Int']; + githubUsername?: Maybe; + twitterUsername?: Maybe; + appetizeCode?: Maybe; + emailVerified: Scalars['Boolean']; + isExpoAdmin: Scalars['Boolean']; + displayName: Scalars['String']; + isSecondFactorAuthenticationEnabled: Scalars['Boolean']; + /** Get all certified second factor authentication methods */ + secondFactorDevices: Array; + /** Associated accounts */ + primaryAccount: Account; + accounts: Array; + /** Access Tokens belonging to this actor */ + accessTokens: Array; + /** Snacks associated with this account */ + snacks: Array; + /** Apps this user has published */ + apps: Array; + /** Whether this user has any pending user invitations. Only resolves for the viewer. */ + hasPendingUserInvitations: Scalars['Boolean']; + /** Pending UserInvitations for this user. Only resolves for the viewer. */ + pendingUserInvitations: Array; + /** Coalesced project activity for all apps belonging to all accounts this user belongs to. Only resolves for the viewer. */ + activityTimelineProjectActivities: Array; + /** + * Server feature gate values for this actor, optionally filtering by desired gates. + * Only resolves for the viewer. + */ + featureGates: Scalars['JSONObject']; + /** @deprecated Field no longer supported */ + isEmailUnsubscribed: Scalars['Boolean']; + /** @deprecated Field no longer supported */ + lastPasswordReset?: Maybe; + /** @deprecated Field no longer supported */ + lastLogin?: Maybe; + /** @deprecated Field no longer supported */ + isOnboarded?: Maybe; + /** @deprecated Field no longer supported */ + isLegacy?: Maybe; + /** @deprecated Field no longer supported */ + wasLegacy?: Maybe; + /** @deprecated 'likes' have been deprecated. */ + likes?: Maybe>>; +}; + + +/** Represents a human (not robot) actor. */ +export type UserSnacksArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; +}; + + +/** Represents a human (not robot) actor. */ +export type UserAppsArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; + includeUnpublished?: Maybe; +}; + + +/** Represents a human (not robot) actor. */ +export type UserActivityTimelineProjectActivitiesArgs = { + limit: Scalars['Int']; + createdBefore?: Maybe; + filterTypes?: Maybe>; +}; + + +/** Represents a human (not robot) actor. */ +export type UserFeatureGatesArgs = { + filter?: Maybe>; +}; + + +/** Represents a human (not robot) actor. */ +export type UserLikesArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; +}; + +/** A second factor device belonging to a User */ +export type UserSecondFactorDevice = { + __typename?: 'UserSecondFactorDevice'; + id: Scalars['ID']; + user: User; + name: Scalars['String']; + isCertified: Scalars['Boolean']; + isPrimary: Scalars['Boolean']; + smsPhoneNumber?: Maybe; + method: SecondFactorMethod; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export enum SecondFactorMethod { + /** Google Authenticator (TOTP) */ + Authenticator = 'AUTHENTICATOR', + /** SMS */ + Sms = 'SMS' +} + +/** A method of authentication for an Actor */ +export type AccessToken = { + __typename?: 'AccessToken'; + id: Scalars['ID']; + visibleTokenPrefix: Scalars['String']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; + revokedAt?: Maybe; + lastUsedAt?: Maybe; + owner: Actor; + note?: Maybe; +}; + +/** An pending invitation sent to an email granting membership on an Account. */ +export type UserInvitation = { + __typename?: 'UserInvitation'; + id: Scalars['ID']; + /** Email to which this invitation was sent */ + email: Scalars['String']; + created: Scalars['DateTime']; + accountName: Scalars['String']; + /** Account permissions to be granted upon acceptance of this invitation */ + permissions: Array; + /** Role to be granted upon acceptance of this invitation */ + role: Role; +}; + +export enum Permission { + Own = 'OWN', + Admin = 'ADMIN', + Publish = 'PUBLISH', + View = 'VIEW' +} + +export enum Role { + Owner = 'OWNER', + Admin = 'ADMIN', + Developer = 'DEVELOPER', + ViewOnly = 'VIEW_ONLY', + Custom = 'CUSTOM', + HasAdmin = 'HAS_ADMIN', + NotAdmin = 'NOT_ADMIN' +} + +export enum ActivityTimelineProjectActivityType { + BuildJob = 'BUILD_JOB', + Build = 'BUILD', + Update = 'UPDATE', + Submission = 'SUBMISSION' +} + + +export type BuildArtifacts = { + __typename?: 'BuildArtifacts'; + buildUrl?: Maybe; + xcodeBuildLogsUrl?: Maybe; +}; + +export type BuildMetrics = { + __typename?: 'BuildMetrics'; + buildWaitTime?: Maybe; + buildQueueTime?: Maybe; + buildDuration?: Maybe; +}; + +export enum BuildIosEnterpriseProvisioning { + Adhoc = 'ADHOC', + Universal = 'UNIVERSAL' +} + +export type BuildError = { + __typename?: 'BuildError'; + errorCode: Scalars['String']; + message: Scalars['String']; + docsUrl?: Maybe; +}; + +/** Represents an EAS Submission */ +export type Submission = ActivityTimelineProjectActivity & { + __typename?: 'Submission'; + id: Scalars['ID']; + actor?: Maybe; + activityTimestamp: Scalars['DateTime']; + app: App; + initiatingActor?: Maybe; + submittedBuild?: Maybe; + platform: AppPlatform; + status: SubmissionStatus; + androidConfig?: Maybe; + iosConfig?: Maybe; + logsUrl?: Maybe; + error?: Maybe; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export enum SubmissionStatus { + AwaitingBuild = 'AWAITING_BUILD', + InQueue = 'IN_QUEUE', + InProgress = 'IN_PROGRESS', + Finished = 'FINISHED', + Errored = 'ERRORED', + Canceled = 'CANCELED' +} + +export type AndroidSubmissionConfig = { + __typename?: 'AndroidSubmissionConfig'; + /** @deprecated applicationIdentifier is deprecated and will be auto-detected on submit */ + applicationIdentifier?: Maybe; + /** @deprecated archiveType is deprecated and will be null */ + archiveType?: Maybe; + track: SubmissionAndroidTrack; + releaseStatus?: Maybe; +}; + +export enum SubmissionAndroidArchiveType { + Apk = 'APK', + Aab = 'AAB' +} + +export enum SubmissionAndroidTrack { + Production = 'PRODUCTION', + Internal = 'INTERNAL', + Alpha = 'ALPHA', + Beta = 'BETA' +} + +export enum SubmissionAndroidReleaseStatus { + Draft = 'DRAFT', + InProgress = 'IN_PROGRESS', + Halted = 'HALTED', + Completed = 'COMPLETED' +} + +export type IosSubmissionConfig = { + __typename?: 'IosSubmissionConfig'; + ascAppIdentifier: Scalars['String']; + appleIdUsername?: Maybe; + ascApiKeyId?: Maybe; +}; + +export type SubmissionError = { + __typename?: 'SubmissionError'; + errorCode?: Maybe; + message?: Maybe; +}; + +/** Represents an Standalone App build job */ +export type BuildJob = ActivityTimelineProjectActivity & BuildOrBuildJob & { + __typename?: 'BuildJob'; + id: Scalars['ID']; + actor?: Maybe; + activityTimestamp: Scalars['DateTime']; + app?: Maybe; + user?: Maybe; + release?: Maybe; + config?: Maybe; + artifacts?: Maybe; + logs?: Maybe; + created?: Maybe; + updated?: Maybe; + fullExperienceName?: Maybe; + status?: Maybe; + expirationDate?: Maybe; + platform: AppPlatform; + sdkVersion?: Maybe; + releaseChannel?: Maybe; +}; + +export type AppRelease = { + __typename?: 'AppRelease'; + id: Scalars['ID']; + hash: Scalars['String']; + publishedTime: Scalars['DateTime']; + publishingUsername: Scalars['String']; + sdkVersion: Scalars['String']; + runtimeVersion?: Maybe; + version: Scalars['String']; + s3Key: Scalars['String']; + s3Url: Scalars['String']; + manifest: Scalars['JSON']; +}; + +export type BuildArtifact = { + __typename?: 'BuildArtifact'; + url: Scalars['String']; + manifestPlistUrl?: Maybe; +}; + +export type BuildLogs = { + __typename?: 'BuildLogs'; + url?: Maybe; + format?: Maybe; +}; + +export enum BuildJobLogsFormat { + Raw = 'RAW', + Json = 'JSON' +} + +export enum BuildJobStatus { + Pending = 'PENDING', + Started = 'STARTED', + InProgress = 'IN_PROGRESS', + Errored = 'ERRORED', + Finished = 'FINISHED', + SentToQueue = 'SENT_TO_QUEUE' +} + +export type SubmissionFilter = { + platform?: Maybe; + status?: Maybe; +}; + +export type DeploymentOptions = { + /** Max number of associated builds to return */ + buildListMaxSize?: Maybe; +}; + +/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */ +export type Deployment = { + __typename?: 'Deployment'; + id: Scalars['ID']; + runtimeVersion: Scalars['String']; + /** + * The name of this deployment's associated channel. It is specified separately from the `channel` + * field to allow specifying a deployment before an EAS Update channel has been created. + */ + channelName: Scalars['String']; + channel?: Maybe; + recentBuilds: Array; + mostRecentlyUpdatedAt: Scalars['DateTime']; +}; + +export type UpdateChannel = { + __typename?: 'UpdateChannel'; + id: Scalars['ID']; + appId: Scalars['ID']; + name: Scalars['String']; + branchMapping: Scalars['String']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; + updateBranches: Array; +}; + + +export type UpdateChannelUpdateBranchesArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; +}; + +export type UpdateBranch = { + __typename?: 'UpdateBranch'; + id: Scalars['ID']; + appId: Scalars['ID']; + name: Scalars['String']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; + updates: Array; +}; + + +export type UpdateBranchUpdatesArgs = { + offset: Scalars['Int']; + limit: Scalars['Int']; + filter?: Maybe; +}; + +export type UpdatesFilter = { + platform?: Maybe; + runtimeVersions?: Maybe>; +}; + +export type IosAppCredentialsFilter = { + appleAppIdentifierId?: Maybe; +}; + +export type IosAppCredentials = { + __typename?: 'IosAppCredentials'; + id: Scalars['ID']; + app: App; + appleTeam?: Maybe; + appleAppIdentifier: AppleAppIdentifier; + iosAppBuildCredentialsList: Array; + pushKey?: Maybe; + appStoreConnectApiKeyForSubmissions?: Maybe; + /** @deprecated use iosAppBuildCredentialsList instead */ + iosAppBuildCredentialsArray: Array; +}; + + +export type IosAppCredentialsIosAppBuildCredentialsListArgs = { + filter?: Maybe; +}; + + +export type IosAppCredentialsIosAppBuildCredentialsArrayArgs = { + filter?: Maybe; +}; + +export type AppleTeam = { + __typename?: 'AppleTeam'; + id: Scalars['ID']; + account: Account; + appleTeamIdentifier: Scalars['String']; + appleTeamName?: Maybe; + appleAppIdentifiers: Array; + appleDistributionCertificates: Array; + applePushKeys: Array; + appleProvisioningProfiles: Array; + appleDevices: Array; +}; + + +export type AppleTeamAppleAppIdentifiersArgs = { + bundleIdentifier?: Maybe; +}; + + +export type AppleTeamAppleProvisioningProfilesArgs = { + appleAppIdentifierId?: Maybe; +}; + +export type AppleAppIdentifier = { + __typename?: 'AppleAppIdentifier'; + id: Scalars['ID']; + account: Account; + appleTeam?: Maybe; + bundleIdentifier: Scalars['String']; + parentAppleAppIdentifier?: Maybe; +}; + +export type AppleDistributionCertificate = { + __typename?: 'AppleDistributionCertificate'; + id: Scalars['ID']; + account: Account; + appleTeam?: Maybe; + serialNumber: Scalars['String']; + validityNotBefore: Scalars['DateTime']; + validityNotAfter: Scalars['DateTime']; + developerPortalIdentifier?: Maybe; + certificateP12?: Maybe; + certificatePassword?: Maybe; + certificatePrivateSigningKey?: Maybe; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; + iosAppBuildCredentialsList: Array; +}; + +export type IosAppBuildCredentials = { + __typename?: 'IosAppBuildCredentials'; + id: Scalars['ID']; + distributionCertificate?: Maybe; + provisioningProfile?: Maybe; + iosDistributionType: IosDistributionType; + iosAppCredentials: IosAppCredentials; + /** @deprecated Get Apple Devices from AppleProvisioningProfile instead */ + appleDevices?: Maybe>>; +}; + +export type AppleProvisioningProfile = { + __typename?: 'AppleProvisioningProfile'; + id: Scalars['ID']; + account: Account; + appleTeam?: Maybe; + expiration: Scalars['DateTime']; + appleAppIdentifier: AppleAppIdentifier; + developerPortalIdentifier?: Maybe; + provisioningProfile?: Maybe; + appleUUID: Scalars['String']; + status: Scalars['String']; + appleDevices: Array; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export type AppleDevice = { + __typename?: 'AppleDevice'; + id: Scalars['ID']; + account: Account; + appleTeam: AppleTeam; + identifier: Scalars['String']; + name?: Maybe; + model?: Maybe; + deviceClass?: Maybe; + softwareVersion?: Maybe; + enabled?: Maybe; +}; + +export enum AppleDeviceClass { + Ipad = 'IPAD', + Iphone = 'IPHONE' +} + +export enum IosDistributionType { + AppStore = 'APP_STORE', + Enterprise = 'ENTERPRISE', + AdHoc = 'AD_HOC', + Development = 'DEVELOPMENT' +} + +export type ApplePushKey = { + __typename?: 'ApplePushKey'; + id: Scalars['ID']; + account: Account; + appleTeam?: Maybe; + keyIdentifier: Scalars['String']; + keyP8: Scalars['String']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; + iosAppCredentialsList: Array; +}; + +export type IosAppBuildCredentialsFilter = { + iosDistributionType?: Maybe; +}; + +export type AppStoreConnectApiKey = { + __typename?: 'AppStoreConnectApiKey'; + id: Scalars['ID']; + account: Account; + appleTeam?: Maybe; + issuerIdentifier: Scalars['String']; + keyIdentifier: Scalars['String']; + name?: Maybe; + roles?: Maybe>; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export enum AppStoreConnectUserRole { + Admin = 'ADMIN', + Finance = 'FINANCE', + Technical = 'TECHNICAL', + AccountHolder = 'ACCOUNT_HOLDER', + ReadOnly = 'READ_ONLY', + Sales = 'SALES', + Marketing = 'MARKETING', + AppManager = 'APP_MANAGER', + Developer = 'DEVELOPER', + AccessToReports = 'ACCESS_TO_REPORTS', + CustomerSupport = 'CUSTOMER_SUPPORT', + CreateApps = 'CREATE_APPS', + CloudManagedDeveloperId = 'CLOUD_MANAGED_DEVELOPER_ID', + CloudManagedAppDistribution = 'CLOUD_MANAGED_APP_DISTRIBUTION', + ImageManager = 'IMAGE_MANAGER', + Unknown = 'UNKNOWN' +} + +export type AndroidAppCredentialsFilter = { + legacyOnly?: Maybe; + applicationIdentifier?: Maybe; +}; + +export type AndroidAppCredentials = { + __typename?: 'AndroidAppCredentials'; + id: Scalars['ID']; + app: App; + applicationIdentifier?: Maybe; + androidFcm?: Maybe; + googleServiceAccountKeyForSubmissions?: Maybe; + androidAppBuildCredentialsList: Array; + isLegacy: Scalars['Boolean']; + /** @deprecated use androidAppBuildCredentialsList instead */ + androidAppBuildCredentialsArray: Array; +}; + +export type AndroidFcm = { + __typename?: 'AndroidFcm'; + id: Scalars['ID']; + account: Account; + snippet: FcmSnippet; + /** + * Legacy FCM: returns the Cloud Messaging token, parses to a String + * FCM v1: returns the Service Account Key file, parses to an Object + */ + credential: Scalars['JSON']; + version: AndroidFcmVersion; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export type FcmSnippet = FcmSnippetLegacy | FcmSnippetV1; + +export type FcmSnippetLegacy = { + __typename?: 'FcmSnippetLegacy'; + firstFourCharacters: Scalars['String']; + lastFourCharacters: Scalars['String']; +}; + +export type FcmSnippetV1 = { + __typename?: 'FcmSnippetV1'; + projectId: Scalars['String']; + keyId: Scalars['String']; + serviceAccountEmail: Scalars['String']; + clientId?: Maybe; +}; + +export enum AndroidFcmVersion { + Legacy = 'LEGACY', + V1 = 'V1' +} + +export type GoogleServiceAccountKey = { + __typename?: 'GoogleServiceAccountKey'; + id: Scalars['ID']; + account: Account; + projectIdentifier: Scalars['String']; + privateKeyIdentifier: Scalars['String']; + clientEmail: Scalars['String']; + clientIdentifier: Scalars['String']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export type AndroidAppBuildCredentials = { + __typename?: 'AndroidAppBuildCredentials'; + id: Scalars['ID']; + name: Scalars['String']; + androidKeystore?: Maybe; + isDefault: Scalars['Boolean']; + isLegacy: Scalars['Boolean']; +}; + +export type AndroidKeystore = { + __typename?: 'AndroidKeystore'; + id: Scalars['ID']; + account: Account; + type: AndroidKeystoreType; + keystore: Scalars['String']; + keystorePassword: Scalars['String']; + keyAlias: Scalars['String']; + keyPassword?: Maybe; + md5CertificateFingerprint?: Maybe; + sha1CertificateFingerprint?: Maybe; + sha256CertificateFingerprint?: Maybe; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export enum AndroidKeystoreType { + Jks = 'JKS', + Pkcs12 = 'PKCS12', + Unknown = 'UNKNOWN' +} + +export type EnvironmentSecret = { + __typename?: 'EnvironmentSecret'; + id: Scalars['ID']; + name: Scalars['String']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export type WebhookFilter = { + event?: Maybe; +}; + +export enum WebhookType { + Build = 'BUILD', + Submit = 'SUBMIT' +} + +export type Webhook = { + __typename?: 'Webhook'; + id: Scalars['ID']; + appId: Scalars['ID']; + event: WebhookType; + url: Scalars['String']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; +}; + +export type UserPermission = { + __typename?: 'UserPermission'; + permissions: Array; + role?: Maybe; + /** @deprecated User type is deprecated */ + user?: Maybe; + actor: Actor; +}; + +export type Billing = { + __typename?: 'Billing'; + id: Scalars['ID']; + payment?: Maybe; + subscription?: Maybe; + /** History of invoices */ + charges?: Maybe>>; +}; + +export type PaymentDetails = { + __typename?: 'PaymentDetails'; + id: Scalars['ID']; + card?: Maybe; + address?: Maybe
; +}; + +export type Card = { + __typename?: 'Card'; + cardHolder?: Maybe; + brand?: Maybe; + last4?: Maybe; + expYear?: Maybe; + expMonth?: Maybe; +}; + +export type Address = { + __typename?: 'Address'; + line1?: Maybe; + city?: Maybe; + state?: Maybe; + zip?: Maybe; + country?: Maybe; +}; + +export type SubscriptionDetails = { + __typename?: 'SubscriptionDetails'; + id: Scalars['ID']; + planId?: Maybe; + addons: Array; + name?: Maybe; + price: Scalars['Int']; + nextInvoice?: Maybe; + cancelledAt?: Maybe; + willCancel?: Maybe; + endedAt?: Maybe; + trialEnd?: Maybe; + status?: Maybe; + isDowngrading?: Maybe; +}; + +export type AddonDetails = { + __typename?: 'AddonDetails'; + id: Scalars['ID']; + planId: Scalars['String']; + name: Scalars['String']; + nextInvoice?: Maybe; + willCancel?: Maybe; +}; + +export type Charge = { + __typename?: 'Charge'; + id: Scalars['ID']; + paid?: Maybe; + invoiceId?: Maybe; + createdAt?: Maybe; + amount?: Maybe; + wasRefunded?: Maybe; + receiptUrl?: Maybe; +}; + +export type AccountQuery = { + __typename?: 'AccountQuery'; + /** Query an Account by ID */ + byId: Account; + /** Query an Account by name */ + byName: Account; +}; + + +export type AccountQueryByIdArgs = { + accountId: Scalars['String']; +}; + + +export type AccountQueryByNameArgs = { + accountName: Scalars['String']; +}; + +export type ActorQuery = { + __typename?: 'ActorQuery'; + /** Query an Actor by ID */ + byId: Actor; +}; + + +export type ActorQueryByIdArgs = { + id: Scalars['ID']; +}; + +export type AppleDeviceRegistrationRequestQuery = { + __typename?: 'AppleDeviceRegistrationRequestQuery'; + byId: AppleDeviceRegistrationRequest; +}; + + +export type AppleDeviceRegistrationRequestQueryByIdArgs = { + id: Scalars['ID']; +}; + +export type AppleDeviceRegistrationRequest = { + __typename?: 'AppleDeviceRegistrationRequest'; + id: Scalars['ID']; + account: Account; + appleTeam: AppleTeam; +}; + +export type AppleTeamQuery = { + __typename?: 'AppleTeamQuery'; + byAppleTeamIdentifier?: Maybe; +}; + + +export type AppleTeamQueryByAppleTeamIdentifierArgs = { + accountId: Scalars['ID']; + identifier: Scalars['String']; +}; + +export type AppQuery = { + __typename?: 'AppQuery'; + /** Look up app by app id */ + byId: App; + byFullName: App; + /** + * Public apps in the app directory + * @deprecated App directory no longer supported + */ + all: Array; +}; + + +export type AppQueryByIdArgs = { + appId: Scalars['String']; +}; + + +export type AppQueryByFullNameArgs = { + fullName: Scalars['String']; +}; + + +export type AppQueryAllArgs = { + filter: AppsFilter; + sort: AppSort; + offset?: Maybe; + limit?: Maybe; +}; + +export enum AppsFilter { + /** Featured Projects */ + Featured = 'FEATURED', + /** New Projects */ + New = 'NEW' +} + +export enum AppSort { + /** Sort by recently published */ + RecentlyPublished = 'RECENTLY_PUBLISHED', + /** Sort by highest trendScore */ + Viewed = 'VIEWED' +} + +/** Check to see if assets with given storageKeys exist */ +export type AssetQuery = { + __typename?: 'AssetQuery'; + metadata: Array; +}; + + +/** Check to see if assets with given storageKeys exist */ +export type AssetQueryMetadataArgs = { + storageKeys: Array; +}; + +export type AssetMetadataResult = { + __typename?: 'AssetMetadataResult'; + status: AssetMetadataStatus; + storageKey: Scalars['String']; +}; + +export enum AssetMetadataStatus { + Exists = 'EXISTS', + DoesNotExist = 'DOES_NOT_EXIST' +} + +export type BuildPublicDataQuery = { + __typename?: 'BuildPublicDataQuery'; + /** Get BuildPublicData by ID */ + byId?: Maybe; +}; + + +export type BuildPublicDataQueryByIdArgs = { + id: Scalars['ID']; +}; + +/** Publicly visible data for a Build. */ +export type BuildPublicData = { + __typename?: 'BuildPublicData'; + id: Scalars['ID']; + status: BuildStatus; + artifacts: PublicArtifacts; + project: ProjectPublicData; + platform: AppPlatform; + distribution?: Maybe; +}; + +export type PublicArtifacts = { + __typename?: 'PublicArtifacts'; + buildUrl?: Maybe; +}; + +export type ProjectPublicData = { + __typename?: 'ProjectPublicData'; + id: Scalars['ID']; + fullName: Scalars['String']; +}; + +export type BuildJobQuery = { + __typename?: 'BuildJobQuery'; + /** + * get all build jobs by an optional filter + * @deprecated Prefer Account.buildJobs or App.buildJobs + */ + all: Array>; + byId: BuildJob; +}; + + +export type BuildJobQueryAllArgs = { + status?: Maybe; + username?: Maybe; + experienceSlug?: Maybe; + offset?: Maybe; + limit?: Maybe; + showAdminView?: Maybe; +}; + + +export type BuildJobQueryByIdArgs = { + buildId: Scalars['ID']; +}; + +export type BuildQuery = { + __typename?: 'BuildQuery'; + /** Look up EAS Build by build ID */ + byId: Build; + /** + * Get all builds for a specific app. + * They are sorted from latest to oldest. + * @deprecated Use App.builds instead + */ + allForApp: Array>; + /** + * Get all builds. + * By default, they are sorted from latest to oldest. + * Available only for admin users. + */ + all: Array; +}; + + +export type BuildQueryByIdArgs = { + buildId: Scalars['ID']; +}; + + +export type BuildQueryAllForAppArgs = { + appId: Scalars['String']; + status?: Maybe; + platform?: Maybe; + offset?: Maybe; + limit?: Maybe; +}; + + +export type BuildQueryAllArgs = { + statuses?: Maybe>; + offset?: Maybe; + limit?: Maybe; + order?: Maybe; +}; + +export enum Order { + Desc = 'DESC', + Asc = 'ASC' +} + +export type ClientBuildQuery = { + __typename?: 'ClientBuildQuery'; + byId: ClientBuild; +}; + + +export type ClientBuildQueryByIdArgs = { + requestId: Scalars['ID']; +}; + +/** Represents a client build request */ +export type ClientBuild = { + __typename?: 'ClientBuild'; + id: Scalars['ID']; + status?: Maybe; + userFacingErrorMessage?: Maybe; + buildJobId?: Maybe; + manifestPlistUrl?: Maybe; + userId?: Maybe; +}; + +export type ExperimentationQuery = { + __typename?: 'ExperimentationQuery'; + /** Get user experimentation config */ + userConfig: Scalars['JSONObject']; + /** Get device experimentation config */ + deviceConfig: Scalars['JSONObject']; + /** Get experimentation unit to use for device experiments. In this case, it is the IP address. */ + deviceExperimentationUnit: Scalars['ID']; +}; + +export type InvoiceQuery = { + __typename?: 'InvoiceQuery'; + /** Preview an upgrade subscription invoice, with proration */ + previewInvoiceForSubscriptionUpdate: Invoice; +}; + + +export type InvoiceQueryPreviewInvoiceForSubscriptionUpdateArgs = { + accountId: Scalars['String']; + newPlanIdentifier: Scalars['String']; + couponCode?: Maybe; +}; + +export type Invoice = { + __typename?: 'Invoice'; + id: Scalars['ID']; + /** The total amount due for the invoice, in cents */ + amountDue: Scalars['Int']; + /** The total amount that has been paid, considering any discounts or account credit. Value is in cents. */ + amountPaid: Scalars['Int']; + /** The total amount that needs to be paid, considering any discounts or account credit. Value is in cents. */ + amountRemaining: Scalars['Int']; + discount?: Maybe; + totalDiscountedAmount: Scalars['Int']; + lineItems: Array; + period: InvoicePeriod; + startingBalance: Scalars['Int']; + subtotal: Scalars['Int']; + total: Scalars['Int']; +}; + +export type InvoiceDiscount = { + __typename?: 'InvoiceDiscount'; + id: Scalars['ID']; + name: Scalars['String']; + type: InvoiceDiscountType; + duration: Scalars['String']; + durationInMonths?: Maybe; + /** The coupon's discount value, in percentage or in dollar amount */ + amount: Scalars['Int']; +}; + +export enum InvoiceDiscountType { + Percentage = 'PERCENTAGE', + Amount = 'AMOUNT' +} + +export type InvoiceLineItem = { + __typename?: 'InvoiceLineItem'; + id: Scalars['ID']; + description: Scalars['String']; + /** Line-item amount in cents */ + amount: Scalars['Int']; + period: InvoicePeriod; + proration: Scalars['Boolean']; + quantity: Scalars['Int']; +}; + +export type InvoicePeriod = { + __typename?: 'InvoicePeriod'; + start: Scalars['DateTime']; + end: Scalars['DateTime']; +}; + +export type ProjectQuery = { + __typename?: 'ProjectQuery'; + byAccountNameAndSlug: Project; + /** @deprecated See byAccountNameAndSlug */ + byUsernameAndSlug: Project; + /** @deprecated Field no longer supported */ + byPaths: Array>; +}; + + +export type ProjectQueryByAccountNameAndSlugArgs = { + accountName: Scalars['String']; + slug: Scalars['String']; + platform?: Maybe; + sdkVersions?: Maybe>>; +}; + + +export type ProjectQueryByUsernameAndSlugArgs = { + username: Scalars['String']; + slug: Scalars['String']; + platform?: Maybe; + sdkVersions?: Maybe>>; +}; + + +export type ProjectQueryByPathsArgs = { + paths?: Maybe>>; +}; + +export type SnackQuery = { + __typename?: 'SnackQuery'; + /** + * Get snack by hashId + * @deprecated Use byHashId + */ + byId: Snack; + /** Get snack by hashId */ + byHashId: Snack; +}; + + +export type SnackQueryByIdArgs = { + id: Scalars['ID']; +}; + + +export type SnackQueryByHashIdArgs = { + hashId: Scalars['ID']; +}; + +export type SubmissionQuery = { + __typename?: 'SubmissionQuery'; + /** Look up EAS Submission by submission ID */ + byId: Submission; +}; + + +export type SubmissionQueryByIdArgs = { + submissionId: Scalars['ID']; +}; + +export type UserInvitationPublicDataQuery = { + __typename?: 'UserInvitationPublicDataQuery'; + /** Get UserInvitationPublicData by token */ + byToken: UserInvitationPublicData; +}; + + +export type UserInvitationPublicDataQueryByTokenArgs = { + token: Scalars['ID']; +}; + +/** Publicly visible data for a UserInvitation. */ +export type UserInvitationPublicData = { + __typename?: 'UserInvitationPublicData'; + /** Email to which this invitation was sent */ + id: Scalars['ID']; + email: Scalars['String']; + created: Scalars['DateTime']; + accountName: Scalars['String']; +}; + +export type UserQuery = { + __typename?: 'UserQuery'; + /** Query a User by ID */ + byId: User; + /** Query a User by username */ + byUsername: User; +}; + + +export type UserQueryByIdArgs = { + userId: Scalars['String']; +}; + + +export type UserQueryByUsernameArgs = { + username: Scalars['String']; +}; + +export type WebhookQuery = { + __typename?: 'WebhookQuery'; + byId: Webhook; +}; + + +export type WebhookQueryByIdArgs = { + id: Scalars['ID']; +}; + +export type RootMutation = { + __typename?: 'RootMutation'; + /** + * This is a placeholder field + * @deprecated Not used. + */ + _doNotUse?: Maybe; + /** Mutations that create, read, update, and delete AccessTokens for Actors */ + accessToken: AccessTokenMutation; + /** Mutations that modify an Account */ + account?: Maybe; + /** Mutations that modify the build credentials for an Android app */ + androidAppBuildCredentials: AndroidAppBuildCredentialsMutation; + /** Mutations that modify the credentials for an Android app */ + androidAppCredentials: AndroidAppCredentialsMutation; + /** Mutations that modify an FCM credential */ + androidFcm: AndroidFcmMutation; + /** Mutations that modify a Keystore */ + androidKeystore: AndroidKeystoreMutation; + /** Mutations that modify a Google Service Account Key */ + googleServiceAccountKey: GoogleServiceAccountKeyMutation; + /** Mutations that modify an Identifier for an iOS App */ + appleAppIdentifier: AppleAppIdentifierMutation; + /** Mutations that modify an Apple Device */ + appleDevice: AppleDeviceMutation; + /** Mutations that modify an Apple Device registration request */ + appleDeviceRegistrationRequest: AppleDeviceRegistrationRequestMutation; + /** Mutations that modify a Distribution Certificate */ + appleDistributionCertificate: AppleDistributionCertificateMutation; + /** Mutations that modify a Provisioning Profile */ + appleProvisioningProfile: AppleProvisioningProfileMutation; + /** Mutations that modify an Apple Push Notification key */ + applePushKey: ApplePushKeyMutation; + /** Mutations that modify an Apple Team */ + appleTeam: AppleTeamMutation; + /** Mutations that modify an App Store Connect Api Key */ + appStoreConnectApiKey: AppStoreConnectApiKeyMutation; + /** Mutations that modify an App */ + app?: Maybe; + asset: AssetMutation; + /** Mutations that modify an BuildJob */ + buildJob?: Maybe; + /** Mutations that modify an EAS Build */ + build?: Maybe; + /** Mutations that modify the build credentials for an iOS app */ + iosAppBuildCredentials: IosAppBuildCredentialsMutation; + /** Mutations that modify the credentials for an iOS app */ + iosAppCredentials: IosAppCredentialsMutation; + keystoreGenerationUrl: KeystoreGenerationUrlMutation; + /** Mutations that create, update, and delete Robots */ + robot: RobotMutation; + /** Mutations that modify an EAS Submit submission */ + submission: SubmissionMutation; + updateChannel: UpdateChannelMutation; + update: UpdateMutation; + updateBranch: UpdateBranchMutation; + uploadSession: UploadSession; + /** Mutations that create, delete, and accept UserInvitations */ + userInvitation: UserInvitationMutation; + /** Mutations that modify the currently authenticated User */ + me?: Maybe; + /** Mutations that modify an EmailSubscription */ + emailSubscription: EmailSubscriptionMutation; + /** Mutations that create and delete EnvironmentSecrets */ + environmentSecret: EnvironmentSecretMutation; + /** Mutations that create, delete, update Webhooks */ + webhook: WebhookMutation; +}; + + +export type RootMutationAccountArgs = { + accountName: Scalars['ID']; +}; + + +export type RootMutationAppArgs = { + appId?: Maybe; +}; + + +export type RootMutationBuildJobArgs = { + buildId: Scalars['ID']; +}; + + +export type RootMutationBuildArgs = { + buildId?: Maybe; +}; + +export type AccessTokenMutation = { + __typename?: 'AccessTokenMutation'; + /** Create an AccessToken for an Actor */ + createAccessToken: CreateAccessTokenResponse; + /** Revoke an AccessToken */ + setAccessTokenRevoked: AccessToken; + /** Delete an AccessToken */ + deleteAccessToken: DeleteAccessTokenResult; +}; + + +export type AccessTokenMutationCreateAccessTokenArgs = { + createAccessTokenData: CreateAccessTokenInput; +}; + + +export type AccessTokenMutationSetAccessTokenRevokedArgs = { + id: Scalars['ID']; + revoked?: Maybe; +}; + + +export type AccessTokenMutationDeleteAccessTokenArgs = { + id: Scalars['ID']; +}; + +export type CreateAccessTokenInput = { + actorID: Scalars['ID']; + note?: Maybe; +}; + +export type CreateAccessTokenResponse = { + __typename?: 'CreateAccessTokenResponse'; + /** AccessToken created */ + accessToken?: Maybe; + /** Full token string to be used for authentication */ + token?: Maybe; +}; + +export type DeleteAccessTokenResult = { + __typename?: 'DeleteAccessTokenResult'; + id: Scalars['ID']; +}; + +export type AccountMutation = { + __typename?: 'AccountMutation'; + /** Add specified account Permissions for Actor. Actor must already have at least one permission on the account. */ + grantActorPermissions?: Maybe; + /** Revoke specified Permissions for Actor. Actor must already have at least one permission on the account. */ + revokeActorPermissions?: Maybe; + /** Add a subscription */ + subscribeToProduct?: Maybe; + /** Cancels the active subscription */ + cancelSubscription?: Maybe; + /** Upgrades or downgrades the active subscription to the newPlanIdentifier, which must be one of the EAS plans (i.e., Production or Enterprise). */ + changePlan: Account; + /** Cancel scheduled subscription change */ + cancelScheduledSubscriptionChange: Account; + /** Requests a refund for the specified charge. Returns true if auto-refund was possible, otherwise requests a manual refund from support and returns false. */ + requestRefund?: Maybe; + /** + * Makes a one time purchase + * @deprecated Build packs are no longer supported + */ + buyProduct?: Maybe; + /** + * Update setting to purchase new build packs when the current one is consumed + * @deprecated Build packs are no longer supported + */ + setBuildAutoRenew?: Maybe; + /** Set payment details */ + setPaymentSource?: Maybe; + /** Require authorization to send push notifications for experiences owned by this account */ + setPushSecurityEnabled?: Maybe; + /** Rename this account and the primary user's username if this account is a personal account */ + rename: Account; +}; + + +export type AccountMutationGrantActorPermissionsArgs = { + accountID: Scalars['ID']; + actorID: Scalars['ID']; + permissions?: Maybe>>; +}; + + +export type AccountMutationRevokeActorPermissionsArgs = { + accountID: Scalars['ID']; + actorID: Scalars['ID']; + permissions?: Maybe>>; +}; + + +export type AccountMutationSubscribeToProductArgs = { + accountName: Scalars['ID']; + productId: Scalars['ID']; + paymentSource: Scalars['ID']; +}; + + +export type AccountMutationCancelSubscriptionArgs = { + accountName: Scalars['ID']; +}; + + +export type AccountMutationChangePlanArgs = { + accountID: Scalars['ID']; + newPlanIdentifier: Scalars['String']; + couponCode?: Maybe; +}; + + +export type AccountMutationCancelScheduledSubscriptionChangeArgs = { + accountID: Scalars['ID']; +}; + + +export type AccountMutationRequestRefundArgs = { + accountID: Scalars['ID']; + chargeIdentifier: Scalars['ID']; +}; + + +export type AccountMutationBuyProductArgs = { + accountName: Scalars['ID']; + productId: Scalars['ID']; + paymentSource?: Maybe; + autoRenew?: Maybe; +}; + + +export type AccountMutationSetBuildAutoRenewArgs = { + accountName: Scalars['ID']; + autoRenew?: Maybe; +}; + + +export type AccountMutationSetPaymentSourceArgs = { + accountName: Scalars['ID']; + paymentSource: Scalars['ID']; +}; + + +export type AccountMutationSetPushSecurityEnabledArgs = { + accountID: Scalars['ID']; + pushSecurityEnabled: Scalars['Boolean']; +}; + + +export type AccountMutationRenameArgs = { + accountID: Scalars['ID']; + newName: Scalars['String']; +}; + +export type AndroidAppBuildCredentialsMutation = { + __typename?: 'AndroidAppBuildCredentialsMutation'; + /** Create a set of build credentials for an Android app */ + createAndroidAppBuildCredentials?: Maybe; + /** delete a set of build credentials for an Android app */ + deleteAndroidAppBuildCredentials?: Maybe; + /** Set the name of a set of build credentials to be used for an Android app */ + setName?: Maybe; + /** Set the keystore to be used for an Android app */ + setKeystore?: Maybe; + /** Set the build credentials to be the default for the Android app */ + setDefault?: Maybe; +}; + + +export type AndroidAppBuildCredentialsMutationCreateAndroidAppBuildCredentialsArgs = { + androidAppBuildCredentialsInput: AndroidAppBuildCredentialsInput; + androidAppCredentialsId: Scalars['ID']; +}; + + +export type AndroidAppBuildCredentialsMutationDeleteAndroidAppBuildCredentialsArgs = { + id: Scalars['ID']; +}; + + +export type AndroidAppBuildCredentialsMutationSetNameArgs = { + id: Scalars['ID']; + name: Scalars['String']; +}; + + +export type AndroidAppBuildCredentialsMutationSetKeystoreArgs = { + id: Scalars['ID']; + keystoreId: Scalars['ID']; +}; + + +export type AndroidAppBuildCredentialsMutationSetDefaultArgs = { + id: Scalars['ID']; + isDefault: Scalars['Boolean']; +}; + +/** @isDefault: if set, these build credentials will become the default for the Android app. All other build credentials will have their default status set to false. */ +export type AndroidAppBuildCredentialsInput = { + isDefault: Scalars['Boolean']; + name: Scalars['String']; + keystoreId: Scalars['ID']; +}; + +export type DeleteAndroidAppBuildCredentialsResult = { + __typename?: 'deleteAndroidAppBuildCredentialsResult'; + id: Scalars['ID']; +}; + +export type AndroidAppCredentialsMutation = { + __typename?: 'AndroidAppCredentialsMutation'; + /** Create a set of credentials for an Android app */ + createAndroidAppCredentials?: Maybe; + /** Set the FCM push key to be used in an Android app */ + setFcm?: Maybe; + /** Set the Google Service Account Key to be used for submitting an Android app */ + setGoogleServiceAccountKeyForSubmissions?: Maybe; +}; + + +export type AndroidAppCredentialsMutationCreateAndroidAppCredentialsArgs = { + androidAppCredentialsInput: AndroidAppCredentialsInput; + appId: Scalars['ID']; + applicationIdentifier: Scalars['String']; +}; + + +export type AndroidAppCredentialsMutationSetFcmArgs = { + id: Scalars['ID']; + fcmId: Scalars['ID']; +}; + + +export type AndroidAppCredentialsMutationSetGoogleServiceAccountKeyForSubmissionsArgs = { + id: Scalars['ID']; + googleServiceAccountKeyId: Scalars['ID']; +}; + +export type AndroidAppCredentialsInput = { + fcmId?: Maybe; + googleServiceAccountKeyForSubmissionsId?: Maybe; +}; + +export type AndroidFcmMutation = { + __typename?: 'AndroidFcmMutation'; + /** Create an FCM credential */ + createAndroidFcm: AndroidFcm; + /** Delete an FCM credential */ + deleteAndroidFcm: DeleteAndroidFcmResult; +}; + + +export type AndroidFcmMutationCreateAndroidFcmArgs = { + androidFcmInput: AndroidFcmInput; + accountId: Scalars['ID']; +}; + + +export type AndroidFcmMutationDeleteAndroidFcmArgs = { + id: Scalars['ID']; +}; + +export type AndroidFcmInput = { + credential: Scalars['String']; + version: AndroidFcmVersion; +}; + +export type DeleteAndroidFcmResult = { + __typename?: 'deleteAndroidFcmResult'; + id: Scalars['ID']; +}; + +export type AndroidKeystoreMutation = { + __typename?: 'AndroidKeystoreMutation'; + /** Create a Keystore */ + createAndroidKeystore?: Maybe; + /** Delete a Keystore */ + deleteAndroidKeystore: DeleteAndroidKeystoreResult; +}; + + +export type AndroidKeystoreMutationCreateAndroidKeystoreArgs = { + androidKeystoreInput: AndroidKeystoreInput; + accountId: Scalars['ID']; +}; + + +export type AndroidKeystoreMutationDeleteAndroidKeystoreArgs = { + id: Scalars['ID']; +}; + +export type AndroidKeystoreInput = { + base64EncodedKeystore: Scalars['String']; + keystorePassword: Scalars['String']; + keyAlias: Scalars['String']; + keyPassword?: Maybe; + type: AndroidKeystoreType; +}; + +export type DeleteAndroidKeystoreResult = { + __typename?: 'DeleteAndroidKeystoreResult'; + id: Scalars['ID']; +}; + +export type GoogleServiceAccountKeyMutation = { + __typename?: 'GoogleServiceAccountKeyMutation'; + /** Create a Google Service Account Key */ + createGoogleServiceAccountKey: GoogleServiceAccountKey; + /** Delete a Google Service Account Key */ + deleteGoogleServiceAccountKey: DeleteGoogleServiceAccountKeyResult; +}; + + +export type GoogleServiceAccountKeyMutationCreateGoogleServiceAccountKeyArgs = { + googleServiceAccountKeyInput: GoogleServiceAccountKeyInput; + accountId: Scalars['ID']; +}; + + +export type GoogleServiceAccountKeyMutationDeleteGoogleServiceAccountKeyArgs = { + id: Scalars['ID']; +}; + +export type GoogleServiceAccountKeyInput = { + jsonKey: Scalars['JSONObject']; +}; + +export type DeleteGoogleServiceAccountKeyResult = { + __typename?: 'DeleteGoogleServiceAccountKeyResult'; + id: Scalars['ID']; +}; + +export type AppleAppIdentifierMutation = { + __typename?: 'AppleAppIdentifierMutation'; + /** Create an Identifier for an iOS App */ + createAppleAppIdentifier?: Maybe; +}; + + +export type AppleAppIdentifierMutationCreateAppleAppIdentifierArgs = { + appleAppIdentifierInput: AppleAppIdentifierInput; + accountId: Scalars['ID']; +}; + +export type AppleAppIdentifierInput = { + bundleIdentifier: Scalars['String']; + appleTeamId?: Maybe; + parentAppleAppId?: Maybe; +}; + +export type AppleDeviceMutation = { + __typename?: 'AppleDeviceMutation'; + /** Create an Apple Device */ + createAppleDevice: AppleDevice; + /** Delete an Apple Device */ + deleteAppleDevice: DeleteAppleDeviceResult; +}; + + +export type AppleDeviceMutationCreateAppleDeviceArgs = { + appleDeviceInput: AppleDeviceInput; + accountId: Scalars['ID']; +}; + + +export type AppleDeviceMutationDeleteAppleDeviceArgs = { + id: Scalars['ID']; +}; + +export type AppleDeviceInput = { + appleTeamId: Scalars['ID']; + identifier: Scalars['String']; + name?: Maybe; + model?: Maybe; + deviceClass?: Maybe; + softwareVersion?: Maybe; + enabled?: Maybe; +}; + +export type DeleteAppleDeviceResult = { + __typename?: 'DeleteAppleDeviceResult'; + id: Scalars['ID']; +}; + +export type AppleDeviceRegistrationRequestMutation = { + __typename?: 'AppleDeviceRegistrationRequestMutation'; + /** Create an Apple Device registration request */ + createAppleDeviceRegistrationRequest: AppleDeviceRegistrationRequest; +}; + + +export type AppleDeviceRegistrationRequestMutationCreateAppleDeviceRegistrationRequestArgs = { + appleTeamId: Scalars['ID']; + accountId: Scalars['ID']; +}; + +export type AppleDistributionCertificateMutation = { + __typename?: 'AppleDistributionCertificateMutation'; + /** Create a Distribution Certificate */ + createAppleDistributionCertificate?: Maybe; + /** Delete a Distribution Certificate */ + deleteAppleDistributionCertificate: DeleteAppleDistributionCertificateResult; +}; + + +export type AppleDistributionCertificateMutationCreateAppleDistributionCertificateArgs = { + appleDistributionCertificateInput: AppleDistributionCertificateInput; + accountId: Scalars['ID']; +}; + + +export type AppleDistributionCertificateMutationDeleteAppleDistributionCertificateArgs = { + id: Scalars['ID']; +}; + +export type AppleDistributionCertificateInput = { + certP12: Scalars['String']; + certPassword: Scalars['String']; + certPrivateSigningKey?: Maybe; + developerPortalIdentifier?: Maybe; + appleTeamId?: Maybe; +}; + +export type DeleteAppleDistributionCertificateResult = { + __typename?: 'DeleteAppleDistributionCertificateResult'; + id: Scalars['ID']; +}; + +export type AppleProvisioningProfileMutation = { + __typename?: 'AppleProvisioningProfileMutation'; + /** Create a Provisioning Profile */ + createAppleProvisioningProfile: AppleProvisioningProfile; + /** Update a Provisioning Profile */ + updateAppleProvisioningProfile: AppleProvisioningProfile; + /** Delete a Provisioning Profile */ + deleteAppleProvisioningProfile: DeleteAppleProvisioningProfileResult; + /** Delete Provisioning Profiles */ + deleteAppleProvisioningProfiles: Array; +}; + + +export type AppleProvisioningProfileMutationCreateAppleProvisioningProfileArgs = { + appleProvisioningProfileInput: AppleProvisioningProfileInput; + accountId: Scalars['ID']; + appleAppIdentifierId: Scalars['ID']; +}; + + +export type AppleProvisioningProfileMutationUpdateAppleProvisioningProfileArgs = { + id: Scalars['ID']; + appleProvisioningProfileInput: AppleProvisioningProfileInput; +}; + + +export type AppleProvisioningProfileMutationDeleteAppleProvisioningProfileArgs = { + id: Scalars['ID']; +}; + + +export type AppleProvisioningProfileMutationDeleteAppleProvisioningProfilesArgs = { + ids: Array; +}; + +export type AppleProvisioningProfileInput = { + appleProvisioningProfile: Scalars['String']; + developerPortalIdentifier?: Maybe; +}; + +export type DeleteAppleProvisioningProfileResult = { + __typename?: 'DeleteAppleProvisioningProfileResult'; + id: Scalars['ID']; +}; + +export type ApplePushKeyMutation = { + __typename?: 'ApplePushKeyMutation'; + /** Create an Apple Push Notification key */ + createApplePushKey: ApplePushKey; + /** Delete an Apple Push Notification key */ + deleteApplePushKey: DeleteApplePushKeyResult; +}; + + +export type ApplePushKeyMutationCreateApplePushKeyArgs = { + applePushKeyInput: ApplePushKeyInput; + accountId: Scalars['ID']; +}; + + +export type ApplePushKeyMutationDeleteApplePushKeyArgs = { + id: Scalars['ID']; +}; + +export type ApplePushKeyInput = { + keyP8: Scalars['String']; + keyIdentifier: Scalars['String']; + appleTeamId?: Maybe; +}; + +export type DeleteApplePushKeyResult = { + __typename?: 'deleteApplePushKeyResult'; + id: Scalars['ID']; +}; + +export type AppleTeamMutation = { + __typename?: 'AppleTeamMutation'; + /** Create an Apple Team */ + createAppleTeam: AppleTeam; +}; + + +export type AppleTeamMutationCreateAppleTeamArgs = { + appleTeamInput: AppleTeamInput; + accountId: Scalars['ID']; +}; + +export type AppleTeamInput = { + appleTeamIdentifier: Scalars['String']; + appleTeamName?: Maybe; +}; + +export type AppStoreConnectApiKeyMutation = { + __typename?: 'AppStoreConnectApiKeyMutation'; + /** Create an App Store Connect Api Key for an Apple Team */ + createAppStoreConnectApiKey: AppStoreConnectApiKey; + /** Delete an App Store Connect Api Key */ + deleteAppStoreConnectApiKey: DeleteAppStoreConnectApiKeyResult; +}; + + +export type AppStoreConnectApiKeyMutationCreateAppStoreConnectApiKeyArgs = { + appStoreConnectApiKeyInput: AppStoreConnectApiKeyInput; + accountId: Scalars['ID']; +}; + + +export type AppStoreConnectApiKeyMutationDeleteAppStoreConnectApiKeyArgs = { + id: Scalars['ID']; +}; + +export type AppStoreConnectApiKeyInput = { + issuerIdentifier: Scalars['String']; + keyIdentifier: Scalars['String']; + keyP8: Scalars['String']; + name?: Maybe; + roles?: Maybe>; + appleTeamId?: Maybe; +}; + +export type DeleteAppStoreConnectApiKeyResult = { + __typename?: 'deleteAppStoreConnectApiKeyResult'; + id: Scalars['ID']; +}; + +export type AppMutation = { + __typename?: 'AppMutation'; + /** Create an unpublished app */ + createApp: App; + /** @deprecated Field no longer supported */ + grantAccess?: Maybe; + /** Require api token to send push notifs for experience */ + setPushSecurityEnabled?: Maybe; +}; + + +export type AppMutationCreateAppArgs = { + appInput: AppInput; +}; + + +export type AppMutationGrantAccessArgs = { + toUser: Scalars['ID']; + accessLevel?: Maybe; +}; + + +export type AppMutationSetPushSecurityEnabledArgs = { + appId: Scalars['ID']; + pushSecurityEnabled: Scalars['Boolean']; +}; + +export type AppInput = { + accountId: Scalars['ID']; + projectName: Scalars['String']; + privacy: AppPrivacy; +}; + +export type AssetMutation = { + __typename?: 'AssetMutation'; + /** + * Returns an array of specifications for upload. Each URL is valid for an hour. + * The content type of the asset you wish to upload must be specified. + */ + getSignedAssetUploadSpecifications?: Maybe; +}; + + +export type AssetMutationGetSignedAssetUploadSpecificationsArgs = { + assetContentTypes: Array>; +}; + +export type GetSignedAssetUploadSpecificationsResult = { + __typename?: 'GetSignedAssetUploadSpecificationsResult'; + specifications: Array>; +}; + +export type BuildJobMutation = { + __typename?: 'BuildJobMutation'; + cancel?: Maybe; + del?: Maybe; + restart?: Maybe; +}; + +export type BuildMutation = { + __typename?: 'BuildMutation'; + /** Cancel an EAS Build build */ + cancelBuild: Build; + /** Delete an EAS Build build */ + deleteBuild: Build; + /** + * Cancel an EAS Build build + * @deprecated Use cancelBuild instead + */ + cancel: Build; + /** Create an Android build */ + createAndroidBuild: CreateBuildResult; + /** Create an iOS build */ + createIosBuild: CreateBuildResult; +}; + + +export type BuildMutationCancelBuildArgs = { + buildId: Scalars['ID']; +}; + + +export type BuildMutationDeleteBuildArgs = { + buildId: Scalars['ID']; +}; + + +export type BuildMutationCreateAndroidBuildArgs = { + appId: Scalars['ID']; + job: AndroidJobInput; + metadata?: Maybe; +}; + + +export type BuildMutationCreateIosBuildArgs = { + appId: Scalars['ID']; + job: IosJobInput; + metadata?: Maybe; +}; + +export type AndroidJobInput = { + type: BuildWorkflow; + projectArchive: ProjectArchiveSourceInput; + projectRootDirectory: Scalars['String']; + releaseChannel?: Maybe; + updates?: Maybe; + developmentClient?: Maybe; + secrets?: Maybe; + builderEnvironment?: Maybe; + cache?: Maybe; + gradleCommand?: Maybe; + artifactPath?: Maybe; + buildType?: Maybe; + username?: Maybe; +}; + +export enum BuildWorkflow { + Generic = 'GENERIC', + Managed = 'MANAGED' +} + +export type ProjectArchiveSourceInput = { + type: ProjectArchiveSourceType; + bucketKey?: Maybe; + url?: Maybe; +}; + +export enum ProjectArchiveSourceType { + S3 = 'S3', + Url = 'URL' +} + +export type BuildUpdatesInput = { + channel?: Maybe; +}; + +export type AndroidJobSecretsInput = { + buildCredentials?: Maybe; + environmentSecrets?: Maybe; +}; + +export type AndroidJobBuildCredentialsInput = { + keystore: AndroidJobKeystoreInput; +}; + +export type AndroidJobKeystoreInput = { + dataBase64: Scalars['String']; + keystorePassword: Scalars['String']; + keyAlias: Scalars['String']; + keyPassword?: Maybe; +}; + +export type AndroidBuilderEnvironmentInput = { + image?: Maybe; + node?: Maybe; + yarn?: Maybe; + ndk?: Maybe; + expoCli?: Maybe; + env?: Maybe; +}; + +export type BuildCacheInput = { + disabled?: Maybe; + clear?: Maybe; + key?: Maybe; + cacheDefaultPaths?: Maybe; + customPaths?: Maybe>>; +}; + +export enum AndroidBuildType { + Apk = 'APK', + AppBundle = 'APP_BUNDLE', + /** @deprecated Use developmentClient option instead. */ + DevelopmentClient = 'DEVELOPMENT_CLIENT' +} + +export type BuildMetadataInput = { + trackingContext?: Maybe; + appVersion?: Maybe; + appBuildVersion?: Maybe; + cliVersion?: Maybe; + workflow?: Maybe; + credentialsSource?: Maybe; + sdkVersion?: Maybe; + runtimeVersion?: Maybe; + reactNativeVersion?: Maybe; + releaseChannel?: Maybe; + channel?: Maybe; + distribution?: Maybe; + iosEnterpriseProvisioning?: Maybe; + appName?: Maybe; + appIdentifier?: Maybe; + buildProfile?: Maybe; + gitCommitHash?: Maybe; + isGitWorkingTreeDirty?: Maybe; + username?: Maybe; +}; + +export enum BuildCredentialsSource { + Local = 'LOCAL', + Remote = 'REMOTE' +} + +export type CreateBuildResult = { + __typename?: 'CreateBuildResult'; + build: Build; + deprecationInfo?: Maybe; +}; + +export type EasBuildDeprecationInfo = { + __typename?: 'EASBuildDeprecationInfo'; + type: EasBuildDeprecationInfoType; + message: Scalars['String']; +}; + +export enum EasBuildDeprecationInfoType { + UserFacing = 'USER_FACING', + Internal = 'INTERNAL' +} + +export type IosJobInput = { + type: BuildWorkflow; + projectArchive: ProjectArchiveSourceInput; + projectRootDirectory: Scalars['String']; + releaseChannel?: Maybe; + updates?: Maybe; + /** @deprecated */ + distribution?: Maybe; + simulator?: Maybe; + developmentClient?: Maybe; + secrets?: Maybe; + builderEnvironment?: Maybe; + cache?: Maybe; + scheme?: Maybe; + buildConfiguration?: Maybe; + artifactPath?: Maybe; + /** @deprecated */ + buildType?: Maybe; + username?: Maybe; +}; + +export type IosJobSecretsInput = { + buildCredentials?: Maybe>>; + environmentSecrets?: Maybe; +}; + +export type IosJobTargetCredentialsInput = { + targetName: Scalars['String']; + provisioningProfileBase64: Scalars['String']; + distributionCertificate: IosJobDistributionCertificateInput; +}; + +export type IosJobDistributionCertificateInput = { + dataBase64: Scalars['String']; + password: Scalars['String']; +}; + +export type IosBuilderEnvironmentInput = { + image?: Maybe; + node?: Maybe; + yarn?: Maybe; + bundler?: Maybe; + fastlane?: Maybe; + cocoapods?: Maybe; + expoCli?: Maybe; + env?: Maybe; +}; + +/** @deprecated Use developmentClient option instead. */ +export enum IosBuildType { + Release = 'RELEASE', + DevelopmentClient = 'DEVELOPMENT_CLIENT' +} + +export type IosAppBuildCredentialsMutation = { + __typename?: 'IosAppBuildCredentialsMutation'; + /** Create a set of build credentials for an iOS app */ + createIosAppBuildCredentials: IosAppBuildCredentials; + /** Set the distribution certificate to be used for an iOS app */ + setDistributionCertificate: IosAppBuildCredentials; + /** Set the provisioning profile to be used for an iOS app */ + setProvisioningProfile: IosAppBuildCredentials; +}; + + +export type IosAppBuildCredentialsMutationCreateIosAppBuildCredentialsArgs = { + iosAppBuildCredentialsInput: IosAppBuildCredentialsInput; + iosAppCredentialsId: Scalars['ID']; +}; + + +export type IosAppBuildCredentialsMutationSetDistributionCertificateArgs = { + id: Scalars['ID']; + distributionCertificateId: Scalars['ID']; +}; + + +export type IosAppBuildCredentialsMutationSetProvisioningProfileArgs = { + id: Scalars['ID']; + provisioningProfileId: Scalars['ID']; +}; + +export type IosAppBuildCredentialsInput = { + iosDistributionType: IosDistributionType; + distributionCertificateId: Scalars['ID']; + provisioningProfileId: Scalars['ID']; +}; + +export type IosAppCredentialsMutation = { + __typename?: 'IosAppCredentialsMutation'; + /** Create a set of credentials for an iOS app */ + createIosAppCredentials: IosAppCredentials; + /** Set the push key to be used in an iOS app */ + setPushKey: IosAppCredentials; + /** Set the App Store Connect Api Key to be used for submitting an iOS app */ + setAppStoreConnectApiKeyForSubmissions: IosAppCredentials; +}; + + +export type IosAppCredentialsMutationCreateIosAppCredentialsArgs = { + iosAppCredentialsInput: IosAppCredentialsInput; + appId: Scalars['ID']; + appleAppIdentifierId: Scalars['ID']; +}; + + +export type IosAppCredentialsMutationSetPushKeyArgs = { + id: Scalars['ID']; + pushKeyId: Scalars['ID']; +}; + + +export type IosAppCredentialsMutationSetAppStoreConnectApiKeyForSubmissionsArgs = { + id: Scalars['ID']; + ascApiKeyId: Scalars['ID']; +}; + +export type IosAppCredentialsInput = { + appleTeamId?: Maybe; + pushKeyId?: Maybe; + appStoreConnectApiKeyForSubmissionsId?: Maybe; +}; + +export type KeystoreGenerationUrlMutation = { + __typename?: 'KeystoreGenerationUrlMutation'; + /** Create a Keystore Generation URL */ + createKeystoreGenerationUrl: KeystoreGenerationUrl; +}; + +export type KeystoreGenerationUrl = { + __typename?: 'KeystoreGenerationUrl'; + id: Scalars['ID']; + url: Scalars['String']; +}; + +export type RobotMutation = { + __typename?: 'RobotMutation'; + /** Create a Robot and grant it Permissions on an Account */ + createRobotForAccount: Robot; + /** Update a Robot */ + updateRobot: Robot; + /** Delete a Robot */ + deleteRobot: DeleteRobotResult; +}; + + +export type RobotMutationCreateRobotForAccountArgs = { + robotData?: Maybe; + accountID: Scalars['String']; + permissions: Array>; +}; + + +export type RobotMutationUpdateRobotArgs = { + id: Scalars['String']; + robotData: RobotDataInput; +}; + + +export type RobotMutationDeleteRobotArgs = { + id: Scalars['String']; +}; + +export type RobotDataInput = { + name?: Maybe; +}; + +/** Represents a robot (not human) actor. */ +export type Robot = Actor & { + __typename?: 'Robot'; + id: Scalars['ID']; + firstName?: Maybe; + created: Scalars['DateTime']; + isExpoAdmin: Scalars['Boolean']; + displayName: Scalars['String']; + /** Associated accounts */ + accounts: Array; + /** Access Tokens belonging to this actor */ + accessTokens: Array; + /** + * Server feature gate values for this actor, optionally filtering by desired gates. + * Only resolves for the viewer. + */ + featureGates: Scalars['JSONObject']; +}; + + +/** Represents a robot (not human) actor. */ +export type RobotFeatureGatesArgs = { + filter?: Maybe>; +}; + +export type DeleteRobotResult = { + __typename?: 'DeleteRobotResult'; + id: Scalars['ID']; +}; + +export type SubmissionMutation = { + __typename?: 'SubmissionMutation'; + /** + * Create an EAS Submit submission + * @deprecated Use createIosSubmission / createAndroidSubmission instead + */ + createSubmission: CreateSubmissionResult; + /** Create an iOS EAS Submit submission */ + createIosSubmission: CreateSubmissionResult; + /** Create an Android EAS Submit submission */ + createAndroidSubmission: CreateSubmissionResult; +}; + + +export type SubmissionMutationCreateSubmissionArgs = { + input: CreateSubmissionInput; +}; + + +export type SubmissionMutationCreateIosSubmissionArgs = { + input: CreateIosSubmissionInput; +}; + + +export type SubmissionMutationCreateAndroidSubmissionArgs = { + input: CreateAndroidSubmissionInput; +}; + +export type CreateSubmissionInput = { + appId: Scalars['ID']; + platform: AppPlatform; + config: Scalars['JSONObject']; + submittedBuildId?: Maybe; +}; + +export type CreateSubmissionResult = { + __typename?: 'CreateSubmissionResult'; + /** Created submission */ + submission: Submission; +}; + +export type CreateIosSubmissionInput = { + appId: Scalars['ID']; + config: IosSubmissionConfigInput; + submittedBuildId?: Maybe; +}; + +export type IosSubmissionConfigInput = { + appleAppSpecificPassword?: Maybe; + ascApiKey?: Maybe; + ascApiKeyId?: Maybe; + archiveUrl?: Maybe; + appleIdUsername?: Maybe; + ascAppIdentifier: Scalars['String']; +}; + +export type AscApiKeyInput = { + keyP8: Scalars['String']; + keyIdentifier: Scalars['String']; + issuerIdentifier: Scalars['String']; +}; + +export type CreateAndroidSubmissionInput = { + appId: Scalars['ID']; + config: AndroidSubmissionConfigInput; + submittedBuildId?: Maybe; +}; + +export type AndroidSubmissionConfigInput = { + googleServiceAccountKeyId?: Maybe; + googleServiceAccountKeyJson?: Maybe; + archiveUrl?: Maybe; + applicationIdentifier?: Maybe; + track: SubmissionAndroidTrack; + releaseStatus?: Maybe; + changesNotSentForReview?: Maybe; +}; + +export type UpdateChannelMutation = { + __typename?: 'UpdateChannelMutation'; + /** + * Create an EAS channel for an app. + * + * In order to work with GraphQL formatting, the branchMapping should be a + * stringified JSON supplied to the mutation as a variable. + */ + createUpdateChannelForApp?: Maybe; + /** + * Edit an EAS channel. + * + * In order to work with GraphQL formatting, the branchMapping should be a + * stringified JSON supplied to the mutation as a variable. + */ + editUpdateChannel?: Maybe; + /** delete an EAS channel that doesn't point to any branches */ + deleteUpdateChannel: DeleteUpdateChannelResult; +}; + + +export type UpdateChannelMutationCreateUpdateChannelForAppArgs = { + appId: Scalars['ID']; + name: Scalars['String']; + branchMapping?: Maybe; +}; + + +export type UpdateChannelMutationEditUpdateChannelArgs = { + channelId: Scalars['ID']; + branchMapping: Scalars['String']; +}; + + +export type UpdateChannelMutationDeleteUpdateChannelArgs = { + channelId: Scalars['ID']; +}; + +export type DeleteUpdateChannelResult = { + __typename?: 'DeleteUpdateChannelResult'; + id: Scalars['ID']; +}; + +export type UpdateMutation = { + __typename?: 'UpdateMutation'; + /** Delete an EAS update group */ + deleteUpdateGroup: DeleteUpdateGroupResult; +}; + + +export type UpdateMutationDeleteUpdateGroupArgs = { + group: Scalars['ID']; +}; + +export type DeleteUpdateGroupResult = { + __typename?: 'DeleteUpdateGroupResult'; + group: Scalars['ID']; +}; + +export type UpdateBranchMutation = { + __typename?: 'UpdateBranchMutation'; + /** Create an EAS branch for an app */ + createUpdateBranchForApp?: Maybe; + /** + * Edit an EAS branch. The branch can be specified either by its ID or + * with the combination of (appId, name). + */ + editUpdateBranch: UpdateBranch; + /** Delete an EAS branch and all of its updates as long as the branch is not being used by any channels */ + deleteUpdateBranch: DeleteUpdateBranchResult; + /** Publish an update group to a branch */ + publishUpdateGroups: Array; +}; + + +export type UpdateBranchMutationCreateUpdateBranchForAppArgs = { + appId: Scalars['ID']; + name: Scalars['String']; +}; + + +export type UpdateBranchMutationEditUpdateBranchArgs = { + input: EditUpdateBranchInput; +}; + + +export type UpdateBranchMutationDeleteUpdateBranchArgs = { + branchId: Scalars['ID']; +}; + + +export type UpdateBranchMutationPublishUpdateGroupsArgs = { + publishUpdateGroupsInput: Array; +}; + +export type EditUpdateBranchInput = { + id?: Maybe; + appId?: Maybe; + name?: Maybe; + newName: Scalars['String']; +}; + +export type DeleteUpdateBranchResult = { + __typename?: 'DeleteUpdateBranchResult'; + id: Scalars['ID']; +}; + +export type PublishUpdateGroupInput = { + branchId: Scalars['String']; + updateInfoGroup: UpdateInfoGroup; + runtimeVersion: Scalars['String']; + message?: Maybe; +}; + +export type UpdateInfoGroup = { + android?: Maybe; + ios?: Maybe; + web?: Maybe; +}; + +export type PartialManifest = { + launchAsset: PartialManifestAsset; + assets: Array>; + extra?: Maybe; +}; + +export type PartialManifestAsset = { + fileSHA256: Scalars['String']; + bundleKey: Scalars['String']; + contentType: Scalars['String']; + fileExtension?: Maybe; + storageKey: Scalars['String']; +}; + +export type UploadSession = { + __typename?: 'UploadSession'; + /** Create an Upload Session */ + createUploadSession: Scalars['JSONObject']; +}; + + +export type UploadSessionCreateUploadSessionArgs = { + type: UploadSessionType; +}; + +export enum UploadSessionType { + EasBuildProjectSources = 'EAS_BUILD_PROJECT_SOURCES', + EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE' +} + +export type UserInvitationMutation = { + __typename?: 'UserInvitationMutation'; + /** + * Create a UserInvitation for an email that when accepted grants + * the specified permissions on an Account + */ + createUserInvitationForAccount: UserInvitation; + /** Re-send UserInivitation by ID */ + resendUserInvitation: UserInvitation; + /** Rescind UserInvitation by ID */ + deleteUserInvitation: RescindUserInvitationResult; + /** + * Delete UserInvitation by token. Note that the viewer's email is not required to match + * the email on the invitation. + */ + deleteUserInvitationByToken: RescindUserInvitationResult; + /** Accept UserInvitation by ID. Viewer must have matching email and email must be verified. */ + acceptUserInvitationAsViewer: AcceptUserInvitationResult; + /** + * Accept UserInvitation by token. Note that the viewer's email is not required to match + * the email on the invitation. If viewer's email does match that of the invitation, + * their email will also be verified. + */ + acceptUserInvitationByTokenAsViewer: AcceptUserInvitationResult; +}; + + +export type UserInvitationMutationCreateUserInvitationForAccountArgs = { + accountID: Scalars['ID']; + email: Scalars['String']; + permissions: Array>; +}; + + +export type UserInvitationMutationResendUserInvitationArgs = { + id: Scalars['ID']; +}; + + +export type UserInvitationMutationDeleteUserInvitationArgs = { + id: Scalars['ID']; +}; + + +export type UserInvitationMutationDeleteUserInvitationByTokenArgs = { + token: Scalars['ID']; +}; + + +export type UserInvitationMutationAcceptUserInvitationAsViewerArgs = { + id: Scalars['ID']; +}; + + +export type UserInvitationMutationAcceptUserInvitationByTokenAsViewerArgs = { + token: Scalars['ID']; +}; + +export type RescindUserInvitationResult = { + __typename?: 'RescindUserInvitationResult'; + id: Scalars['ID']; +}; + +export type AcceptUserInvitationResult = { + __typename?: 'AcceptUserInvitationResult'; + success?: Maybe; +}; + +export type MeMutation = { + __typename?: 'MeMutation'; + /** Update the current user's data */ + updateProfile?: Maybe; + /** Update an App that the current user owns */ + updateApp?: Maybe; + /** Unpublish an App that the current user owns */ + unpublishApp?: Maybe; + /** Transfer project to a different Account */ + transferApp: App; + /** Delete a Snack that the current user owns */ + deleteSnack?: Maybe; + /** Create a new Account and grant this User the owner Role */ + createAccount?: Maybe; + /** Delete an Account created via createAccount */ + deleteAccount: DeleteAccountResult; + /** Leave an Account (revoke own permissions on Account) */ + leaveAccount: LeaveAccountResult; + /** Initiate setup of two-factor authentication for the current user */ + initiateSecondFactorAuthentication: SecondFactorInitiationResult; + /** Purge unfinished two-factor authentication setup for the current user if not fully-set-up */ + purgeUnfinishedSecondFactorAuthentication: SecondFactorBooleanResult; + /** Regenerate backup codes for the current user */ + regenerateSecondFactorBackupCodes: SecondFactorRegenerateBackupCodesResult; + /** Send SMS OTP to a second factor device for use during device setup or during change confirmation */ + sendSMSOTPToSecondFactorDevice: SecondFactorBooleanResult; + /** Certify an initiated second factor authentication method for the current user */ + certifySecondFactorDevice: SecondFactorBooleanResult; + /** Set the user's primary second factor device */ + setPrimarySecondFactorDevice: SecondFactorBooleanResult; + /** Add an additional second factor device */ + addSecondFactorDevice: SecondFactorDeviceConfigurationResult; + /** Delete a second factor device */ + deleteSecondFactorDevice: SecondFactorBooleanResult; + /** Disable all second factor authentication for the current user */ + disableSecondFactorAuthentication: SecondFactorBooleanResult; +}; + + +export type MeMutationUpdateProfileArgs = { + userData: UserDataInput; +}; + + +export type MeMutationUpdateAppArgs = { + appData: AppDataInput; +}; + + +export type MeMutationUnpublishAppArgs = { + appId: Scalars['ID']; +}; + + +export type MeMutationTransferAppArgs = { + appId: Scalars['ID']; + destinationAccountId: Scalars['ID']; +}; + + +export type MeMutationDeleteSnackArgs = { + snackId: Scalars['ID']; +}; + + +export type MeMutationCreateAccountArgs = { + accountData: AccountDataInput; +}; + + +export type MeMutationDeleteAccountArgs = { + accountId: Scalars['ID']; +}; + + +export type MeMutationLeaveAccountArgs = { + accountId: Scalars['ID']; +}; + + +export type MeMutationInitiateSecondFactorAuthenticationArgs = { + deviceConfigurations: Array>; + recaptchaResponseToken?: Maybe; +}; + + +export type MeMutationRegenerateSecondFactorBackupCodesArgs = { + otp: Scalars['String']; +}; + + +export type MeMutationSendSmsotpToSecondFactorDeviceArgs = { + userSecondFactorDeviceId: Scalars['ID']; +}; + + +export type MeMutationCertifySecondFactorDeviceArgs = { + otp: Scalars['String']; +}; + + +export type MeMutationSetPrimarySecondFactorDeviceArgs = { + userSecondFactorDeviceId: Scalars['ID']; +}; + + +export type MeMutationAddSecondFactorDeviceArgs = { + deviceConfiguration: SecondFactorDeviceConfiguration; + otp: Scalars['String']; +}; + + +export type MeMutationDeleteSecondFactorDeviceArgs = { + userSecondFactorDeviceId: Scalars['ID']; + otp: Scalars['String']; +}; + + +export type MeMutationDisableSecondFactorAuthenticationArgs = { + otp: Scalars['String']; +}; + +export type UserDataInput = { + id?: Maybe; + username?: Maybe; + industry?: Maybe; + location?: Maybe; + githubUsername?: Maybe; + twitterUsername?: Maybe; + email?: Maybe; + firstName?: Maybe; + lastName?: Maybe; + fullName?: Maybe; + profilePhoto?: Maybe; + isOnboarded?: Maybe; + isLegacy?: Maybe; + isEmailUnsubscribed?: Maybe; + wasLegacy?: Maybe; + appetizeCode?: Maybe; +}; + +export type AppDataInput = { + id: Scalars['ID']; + privacy?: Maybe; +}; + +export type AccountDataInput = { + name: Scalars['String']; +}; + +export type DeleteAccountResult = { + __typename?: 'DeleteAccountResult'; + id: Scalars['ID']; +}; + +export type LeaveAccountResult = { + __typename?: 'LeaveAccountResult'; + success: Scalars['Boolean']; +}; + +export type SecondFactorDeviceConfiguration = { + method: SecondFactorMethod; + smsPhoneNumber?: Maybe; + name: Scalars['String']; + isPrimary: Scalars['Boolean']; +}; + +export type SecondFactorInitiationResult = { + __typename?: 'SecondFactorInitiationResult'; + configurationResults: Array>; + plaintextBackupCodes: Array>; +}; + +export type SecondFactorDeviceConfigurationResult = { + __typename?: 'SecondFactorDeviceConfigurationResult'; + secondFactorDevice: UserSecondFactorDevice; + secret: Scalars['String']; + keyURI: Scalars['String']; +}; + +export type SecondFactorBooleanResult = { + __typename?: 'SecondFactorBooleanResult'; + success: Scalars['Boolean']; +}; + +export type SecondFactorRegenerateBackupCodesResult = { + __typename?: 'SecondFactorRegenerateBackupCodesResult'; + plaintextBackupCodes: Array>; +}; + +export type EmailSubscriptionMutation = { + __typename?: 'EmailSubscriptionMutation'; + addUser?: Maybe; +}; + + +export type EmailSubscriptionMutationAddUserArgs = { + addUserInput: AddUserInput; +}; + +export type AddUserInput = { + email: Scalars['String']; + tags?: Maybe>; + audience?: Maybe; +}; + +export enum MailchimpTag { + EasMasterList = 'EAS_MASTER_LIST', + DevClientUsers = 'DEV_CLIENT_USERS' +} + +export enum MailchimpAudience { + ExpoDevelopers = 'EXPO_DEVELOPERS' +} + +export type AddUserPayload = { + __typename?: 'AddUserPayload'; + id?: Maybe; + email_address?: Maybe; + status?: Maybe; + timestamp_signup?: Maybe; + tags?: Maybe>; + list_id?: Maybe; +}; + +export type MailchimpTagPayload = { + __typename?: 'MailchimpTagPayload'; + id?: Maybe; + name?: Maybe; +}; + +export type EnvironmentSecretMutation = { + __typename?: 'EnvironmentSecretMutation'; + /** Create an environment secret for an Account */ + createEnvironmentSecretForAccount: EnvironmentSecret; + /** Create an environment secret for an App */ + createEnvironmentSecretForApp: EnvironmentSecret; + /** Delete an environment secret */ + deleteEnvironmentSecret: DeleteEnvironmentSecretResult; +}; + + +export type EnvironmentSecretMutationCreateEnvironmentSecretForAccountArgs = { + environmentSecretData: CreateEnvironmentSecretInput; + accountId: Scalars['String']; +}; + + +export type EnvironmentSecretMutationCreateEnvironmentSecretForAppArgs = { + environmentSecretData: CreateEnvironmentSecretInput; + appId: Scalars['String']; +}; + + +export type EnvironmentSecretMutationDeleteEnvironmentSecretArgs = { + id: Scalars['String']; +}; + +export type CreateEnvironmentSecretInput = { + name: Scalars['String']; + value: Scalars['String']; +}; + +export type DeleteEnvironmentSecretResult = { + __typename?: 'DeleteEnvironmentSecretResult'; + id: Scalars['ID']; +}; + +export type WebhookMutation = { + __typename?: 'WebhookMutation'; + /** Create a Webhook */ + createWebhook: Webhook; + /** Update a Webhook */ + updateWebhook: Webhook; + /** Delete a Webhook */ + deleteWebhook: DeleteWebhookResult; +}; + + +export type WebhookMutationCreateWebhookArgs = { + appId: Scalars['String']; + webhookInput: WebhookInput; +}; + + +export type WebhookMutationUpdateWebhookArgs = { + webhookId: Scalars['ID']; + webhookInput: WebhookInput; +}; + + +export type WebhookMutationDeleteWebhookArgs = { + webhookId: Scalars['ID']; +}; + +export type WebhookInput = { + url: Scalars['String']; + secret: Scalars['String']; + event: WebhookType; +}; + +export type DeleteWebhookResult = { + __typename?: 'DeleteWebhookResult'; + id: Scalars['ID']; +}; + +export enum StandardOffer { + /** $29 USD per month, 30 day trial */ + Default = 'DEFAULT', + /** $348 USD per year, 30 day trial */ + YearlySub = 'YEARLY_SUB', + /** $29 USD per month, 1 year trial */ + YcDeals = 'YC_DEALS', + /** $800 USD per month */ + Support = 'SUPPORT' +} + +export enum IosSchemeBuildConfiguration { + Release = 'RELEASE', + Debug = 'DEBUG' +} + +/** @deprecated Use developmentClient option instead. */ +export enum IosManagedBuildType { + Release = 'RELEASE', + DevelopmentClient = 'DEVELOPMENT_CLIENT' +} + +export enum CacheControlScope { + Public = 'PUBLIC', + Private = 'PRIVATE' +} + + +export type CreateUpdateBranchForAppMutationVariables = Exact<{ + appId: Scalars['ID']; + name: Scalars['String']; +}>; + + +export type CreateUpdateBranchForAppMutation = ( + { __typename?: 'RootMutation' } + & { updateBranch: ( + { __typename?: 'UpdateBranchMutation' } + & { createUpdateBranchForApp?: Maybe<( + { __typename?: 'UpdateBranch' } + & Pick + )> } + ) } +); + +export type GetBranchInfoQueryVariables = Exact<{ + appId: Scalars['String']; + name: Scalars['String']; +}>; + + +export type GetBranchInfoQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { updateBranchByName?: Maybe<( + { __typename?: 'UpdateBranch' } + & Pick + )> } + ) } + ) } +); + +export type DeleteUpdateBranchMutationVariables = Exact<{ + branchId: Scalars['ID']; +}>; + + +export type DeleteUpdateBranchMutation = ( + { __typename?: 'RootMutation' } + & { updateBranch: ( + { __typename?: 'UpdateBranchMutation' } + & { deleteUpdateBranch: ( + { __typename?: 'DeleteUpdateBranchResult' } + & Pick + ) } + ) } +); + +export type BranchesByAppQueryVariables = Exact<{ + appId: Scalars['String']; + limit: Scalars['Int']; +}>; + + +export type BranchesByAppQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { updateBranches: Array<( + { __typename?: 'UpdateBranch' } + & Pick + & UpdateBranchFragment + )> } + ) } + ) } +); + +export type EditUpdateBranchMutationVariables = Exact<{ + input: EditUpdateBranchInput; +}>; + + +export type EditUpdateBranchMutation = ( + { __typename?: 'RootMutation' } + & { updateBranch: ( + { __typename?: 'UpdateBranchMutation' } + & { editUpdateBranch: ( + { __typename?: 'UpdateBranch' } + & Pick + ) } + ) } +); + +export type ViewBranchQueryVariables = Exact<{ + appId: Scalars['String']; + name: Scalars['String']; + limit: Scalars['Int']; +}>; + + +export type ViewBranchQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { updateBranchByName?: Maybe<( + { __typename?: 'UpdateBranch' } + & Pick + & { updates: Array<( + { __typename?: 'Update' } + & Pick + & { actor?: Maybe<( + { __typename?: 'User' } + & Pick + ) | ( + { __typename?: 'Robot' } + & Pick + )> } + )> } + )> } + ) } + ) } +); + +export type CancelBuildMutationVariables = Exact<{ + buildId: Scalars['ID']; +}>; + + +export type CancelBuildMutation = ( + { __typename?: 'RootMutation' } + & { build?: Maybe<( + { __typename?: 'BuildMutation' } + & { cancel: ( + { __typename?: 'Build' } + & Pick + ) } + )> } +); + +export type CreateUpdateChannelOnAppMutationVariables = Exact<{ + appId: Scalars['ID']; + name: Scalars['String']; + branchMapping: Scalars['String']; +}>; + + +export type CreateUpdateChannelOnAppMutation = ( + { __typename?: 'RootMutation' } + & { updateChannel: ( + { __typename?: 'UpdateChannelMutation' } + & { createUpdateChannelForApp?: Maybe<( + { __typename?: 'UpdateChannel' } + & Pick + )> } + ) } +); + +export type GetChannelInfoQueryVariables = Exact<{ + appId: Scalars['String']; + name: Scalars['String']; +}>; + + +export type GetChannelInfoQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { updateChannelByName?: Maybe<( + { __typename?: 'UpdateChannel' } + & Pick + )> } + ) } + ) } +); + +export type DeleteUpdateChannelMutationVariables = Exact<{ + channelId: Scalars['ID']; +}>; + + +export type DeleteUpdateChannelMutation = ( + { __typename?: 'RootMutation' } + & { updateChannel: ( + { __typename?: 'UpdateChannelMutation' } + & { deleteUpdateChannel: ( + { __typename?: 'DeleteUpdateChannelResult' } + & Pick + ) } + ) } +); + +export type GetChannelByNameToEditQueryVariables = Exact<{ + appId: Scalars['String']; + channelName: Scalars['String']; +}>; + + +export type GetChannelByNameToEditQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { updateChannelByName?: Maybe<( + { __typename?: 'UpdateChannel' } + & Pick + & { updateBranches: Array<( + { __typename?: 'UpdateBranch' } + & Pick + )> } + )> } + ) } + ) } +); + +export type UpdateChannelBranchMappingMutationVariables = Exact<{ + channelId: Scalars['ID']; + branchMapping: Scalars['String']; +}>; + + +export type UpdateChannelBranchMappingMutation = ( + { __typename?: 'RootMutation' } + & { updateChannel: ( + { __typename?: 'UpdateChannelMutation' } + & { editUpdateChannel?: Maybe<( + { __typename?: 'UpdateChannel' } + & Pick + )> } + ) } +); + +export type GetAllChannelsForAppQueryVariables = Exact<{ + appId: Scalars['String']; + offset: Scalars['Int']; + limit: Scalars['Int']; +}>; + + +export type GetAllChannelsForAppQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { updateChannels: Array<( + { __typename?: 'UpdateChannel' } + & Pick + & { updateBranches: Array<( + { __typename?: 'UpdateBranch' } + & Pick + & { updates: Array<( + { __typename?: 'Update' } + & Pick + & { actor?: Maybe<( + { __typename?: 'User' } + & Pick + ) | ( + { __typename?: 'Robot' } + & Pick + )> } + )> } + )> } + )> } + ) } + ) } +); + +export type GetChannelByNameForAppQueryVariables = Exact<{ + appId: Scalars['String']; + channelName: Scalars['String']; +}>; + + +export type GetChannelByNameForAppQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { updateChannelByName?: Maybe<( + { __typename?: 'UpdateChannel' } + & Pick + & { updateBranches: Array<( + { __typename?: 'UpdateBranch' } + & Pick + & { updates: Array<( + { __typename?: 'Update' } + & Pick + & { actor?: Maybe<( + { __typename?: 'User' } + & Pick + ) | ( + { __typename?: 'Robot' } + & Pick + )> } + )> } + )> } + )> } + ) } + ) } +); + +export type AppInfoQueryVariables = Exact<{ + appId: Scalars['String']; +}>; + + +export type AppInfoQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + ) } + ) } +); + +export type DeleteUpdateGroupMutationVariables = Exact<{ + group: Scalars['ID']; +}>; + + +export type DeleteUpdateGroupMutation = ( + { __typename?: 'RootMutation' } + & { update: ( + { __typename?: 'UpdateMutation' } + & { deleteUpdateGroup: ( + { __typename?: 'DeleteUpdateGroupResult' } + & Pick + ) } + ) } +); + +export type GetUpdateGroupAsyncQueryVariables = Exact<{ + group: Scalars['ID']; +}>; + + +export type GetUpdateGroupAsyncQuery = ( + { __typename?: 'RootQuery' } + & { updatesByGroup: Array<( + { __typename?: 'Update' } + & Pick + )> } +); + +export type UpdatesByGroupQueryVariables = Exact<{ + groupId: Scalars['ID']; +}>; + + +export type UpdatesByGroupQuery = ( + { __typename?: 'RootQuery' } + & { updatesByGroup: Array<( + { __typename?: 'Update' } + & Pick + & { actor?: Maybe<( + { __typename?: 'User' } + & Pick + ) | ( + { __typename?: 'Robot' } + & Pick + )> } + )> } +); + +export type CreateAndroidAppBuildCredentialsMutationVariables = Exact<{ + androidAppBuildCredentialsInput: AndroidAppBuildCredentialsInput; + androidAppCredentialsId: Scalars['ID']; +}>; + + +export type CreateAndroidAppBuildCredentialsMutation = ( + { __typename?: 'RootMutation' } + & { androidAppBuildCredentials: ( + { __typename?: 'AndroidAppBuildCredentialsMutation' } + & { createAndroidAppBuildCredentials?: Maybe<( + { __typename?: 'AndroidAppBuildCredentials' } + & Pick + & AndroidAppBuildCredentialsFragment + )> } + ) } +); + +export type SetKeystoreMutationVariables = Exact<{ + androidAppBuildCredentialsId: Scalars['ID']; + keystoreId: Scalars['ID']; +}>; + + +export type SetKeystoreMutation = ( + { __typename?: 'RootMutation' } + & { androidAppBuildCredentials: ( + { __typename?: 'AndroidAppBuildCredentialsMutation' } + & { setKeystore?: Maybe<( + { __typename?: 'AndroidAppBuildCredentials' } + & Pick + & AndroidAppBuildCredentialsFragment + )> } + ) } +); + +export type CreateAndroidAppCredentialsMutationVariables = Exact<{ + androidAppCredentialsInput: AndroidAppCredentialsInput; + appId: Scalars['ID']; + applicationIdentifier: Scalars['String']; +}>; + + +export type CreateAndroidAppCredentialsMutation = ( + { __typename?: 'RootMutation' } + & { androidAppCredentials: ( + { __typename?: 'AndroidAppCredentialsMutation' } + & { createAndroidAppCredentials?: Maybe<( + { __typename?: 'AndroidAppCredentials' } + & Pick + & CommonAndroidAppCredentialsFragment + )> } + ) } +); + +export type SetFcmMutationVariables = Exact<{ + androidAppCredentialsId: Scalars['ID']; + fcmId: Scalars['ID']; +}>; + + +export type SetFcmMutation = ( + { __typename?: 'RootMutation' } + & { androidAppCredentials: ( + { __typename?: 'AndroidAppCredentialsMutation' } + & { setFcm?: Maybe<( + { __typename?: 'AndroidAppCredentials' } + & Pick + & CommonAndroidAppCredentialsFragment + )> } + ) } +); + +export type SetGoogleServiceAccountKeyForSubmissionsMutationVariables = Exact<{ + androidAppCredentialsId: Scalars['ID']; + googleServiceAccountKeyId: Scalars['ID']; +}>; + + +export type SetGoogleServiceAccountKeyForSubmissionsMutation = ( + { __typename?: 'RootMutation' } + & { androidAppCredentials: ( + { __typename?: 'AndroidAppCredentialsMutation' } + & { setGoogleServiceAccountKeyForSubmissions?: Maybe<( + { __typename?: 'AndroidAppCredentials' } + & Pick + & CommonAndroidAppCredentialsFragment + )> } + ) } +); + +export type CreateAndroidFcmMutationVariables = Exact<{ + androidFcmInput: AndroidFcmInput; + accountId: Scalars['ID']; +}>; + + +export type CreateAndroidFcmMutation = ( + { __typename?: 'RootMutation' } + & { androidFcm: ( + { __typename?: 'AndroidFcmMutation' } + & { createAndroidFcm: ( + { __typename?: 'AndroidFcm' } + & Pick + & AndroidFcmFragment + ) } + ) } +); + +export type DeleteAndroidFcmMutationVariables = Exact<{ + androidFcmId: Scalars['ID']; +}>; + + +export type DeleteAndroidFcmMutation = ( + { __typename?: 'RootMutation' } + & { androidFcm: ( + { __typename?: 'AndroidFcmMutation' } + & { deleteAndroidFcm: ( + { __typename?: 'deleteAndroidFcmResult' } + & Pick + ) } + ) } +); + +export type CreateAndroidKeystoreMutationVariables = Exact<{ + androidKeystoreInput: AndroidKeystoreInput; + accountId: Scalars['ID']; +}>; + + +export type CreateAndroidKeystoreMutation = ( + { __typename?: 'RootMutation' } + & { androidKeystore: ( + { __typename?: 'AndroidKeystoreMutation' } + & { createAndroidKeystore?: Maybe<( + { __typename?: 'AndroidKeystore' } + & Pick + & AndroidKeystoreFragment + )> } + ) } +); + +export type DeleteAndroidKeystoreMutationVariables = Exact<{ + androidKeystoreId: Scalars['ID']; +}>; + + +export type DeleteAndroidKeystoreMutation = ( + { __typename?: 'RootMutation' } + & { androidKeystore: ( + { __typename?: 'AndroidKeystoreMutation' } + & { deleteAndroidKeystore: ( + { __typename?: 'DeleteAndroidKeystoreResult' } + & Pick + ) } + ) } +); + +export type CreateGoogleServiceAccountKeyMutationVariables = Exact<{ + googleServiceAccountKeyInput: GoogleServiceAccountKeyInput; + accountId: Scalars['ID']; +}>; + + +export type CreateGoogleServiceAccountKeyMutation = ( + { __typename?: 'RootMutation' } + & { googleServiceAccountKey: ( + { __typename?: 'GoogleServiceAccountKeyMutation' } + & { createGoogleServiceAccountKey: ( + { __typename?: 'GoogleServiceAccountKey' } + & Pick + & GoogleServiceAccountKeyFragment + ) } + ) } +); + +export type DeleteGoogleServiceAccountKeyMutationVariables = Exact<{ + googleServiceAccountKeyId: Scalars['ID']; +}>; + + +export type DeleteGoogleServiceAccountKeyMutation = ( + { __typename?: 'RootMutation' } + & { googleServiceAccountKey: ( + { __typename?: 'GoogleServiceAccountKeyMutation' } + & { deleteGoogleServiceAccountKey: ( + { __typename?: 'DeleteGoogleServiceAccountKeyResult' } + & Pick + ) } + ) } +); + +export type CommonAndroidAppCredentialsWithBuildCredentialsByApplicationIdentifierQueryVariables = Exact<{ + projectFullName: Scalars['String']; + applicationIdentifier?: Maybe; + legacyOnly?: Maybe; +}>; + + +export type CommonAndroidAppCredentialsWithBuildCredentialsByApplicationIdentifierQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byFullName: ( + { __typename?: 'App' } + & Pick + & { androidAppCredentials: Array<( + { __typename?: 'AndroidAppCredentials' } + & Pick + & CommonAndroidAppCredentialsFragment + )> } + ) } + ) } +); + +export type GoogleServiceAccountKeyByAccountQueryVariables = Exact<{ + accountName: Scalars['String']; +}>; + + +export type GoogleServiceAccountKeyByAccountQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { googleServiceAccountKeys: Array<( + { __typename?: 'GoogleServiceAccountKey' } + & Pick + & GoogleServiceAccountKeyFragment + )> } + ) } + ) } +); + +export type CreateAppStoreConnectApiKeyMutationVariables = Exact<{ + appStoreConnectApiKeyInput: AppStoreConnectApiKeyInput; + accountId: Scalars['ID']; +}>; + + +export type CreateAppStoreConnectApiKeyMutation = ( + { __typename?: 'RootMutation' } + & { appStoreConnectApiKey: ( + { __typename?: 'AppStoreConnectApiKeyMutation' } + & { createAppStoreConnectApiKey: ( + { __typename?: 'AppStoreConnectApiKey' } + & Pick + & AppStoreConnectApiKeyFragment + ) } + ) } +); + +export type DeleteAppStoreConnectApiKeyMutationVariables = Exact<{ + appStoreConnectApiKeyId: Scalars['ID']; +}>; + + +export type DeleteAppStoreConnectApiKeyMutation = ( + { __typename?: 'RootMutation' } + & { appStoreConnectApiKey: ( + { __typename?: 'AppStoreConnectApiKeyMutation' } + & { deleteAppStoreConnectApiKey: ( + { __typename?: 'deleteAppStoreConnectApiKeyResult' } + & Pick + ) } + ) } +); + +export type CreateAppleAppIdentifierMutationVariables = Exact<{ + appleAppIdentifierInput: AppleAppIdentifierInput; + accountId: Scalars['ID']; +}>; + + +export type CreateAppleAppIdentifierMutation = ( + { __typename?: 'RootMutation' } + & { appleAppIdentifier: ( + { __typename?: 'AppleAppIdentifierMutation' } + & { createAppleAppIdentifier?: Maybe<( + { __typename?: 'AppleAppIdentifier' } + & Pick + & AppleAppIdentifierFragment + )> } + ) } +); + +export type CreateAppleDeviceMutationVariables = Exact<{ + appleDeviceInput: AppleDeviceInput; + accountId: Scalars['ID']; +}>; + + +export type CreateAppleDeviceMutation = ( + { __typename?: 'RootMutation' } + & { appleDevice: ( + { __typename?: 'AppleDeviceMutation' } + & { createAppleDevice: ( + { __typename?: 'AppleDevice' } + & Pick + & AppleDeviceFragment + ) } + ) } +); + +export type CreateAppleDeviceRegistrationRequestMutationVariables = Exact<{ + appleTeamId: Scalars['ID']; + accountId: Scalars['ID']; +}>; + + +export type CreateAppleDeviceRegistrationRequestMutation = ( + { __typename?: 'RootMutation' } + & { appleDeviceRegistrationRequest: ( + { __typename?: 'AppleDeviceRegistrationRequestMutation' } + & { createAppleDeviceRegistrationRequest: ( + { __typename?: 'AppleDeviceRegistrationRequest' } + & Pick + & AppleDeviceRegistrationRequestFragment + ) } + ) } +); + +export type CreateAppleDistributionCertificateMutationVariables = Exact<{ + appleDistributionCertificateInput: AppleDistributionCertificateInput; + accountId: Scalars['ID']; +}>; + + +export type CreateAppleDistributionCertificateMutation = ( + { __typename?: 'RootMutation' } + & { appleDistributionCertificate: ( + { __typename?: 'AppleDistributionCertificateMutation' } + & { createAppleDistributionCertificate?: Maybe<( + { __typename?: 'AppleDistributionCertificate' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )> } + & AppleDistributionCertificateFragment + )> } + ) } +); + +export type DeleteAppleDistributionCertificateMutationVariables = Exact<{ + appleDistributionCertificateId: Scalars['ID']; +}>; + + +export type DeleteAppleDistributionCertificateMutation = ( + { __typename?: 'RootMutation' } + & { appleDistributionCertificate: ( + { __typename?: 'AppleDistributionCertificateMutation' } + & { deleteAppleDistributionCertificate: ( + { __typename?: 'DeleteAppleDistributionCertificateResult' } + & Pick + ) } + ) } +); + +export type CreateAppleProvisioningProfileMutationVariables = Exact<{ + appleProvisioningProfileInput: AppleProvisioningProfileInput; + accountId: Scalars['ID']; + appleAppIdentifierId: Scalars['ID']; +}>; + + +export type CreateAppleProvisioningProfileMutation = ( + { __typename?: 'RootMutation' } + & { appleProvisioningProfile: ( + { __typename?: 'AppleProvisioningProfileMutation' } + & { createAppleProvisioningProfile: ( + { __typename?: 'AppleProvisioningProfile' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )> } + & AppleProvisioningProfileFragment + ) } + ) } +); + +export type UpdateAppleProvisioningProfileMutationVariables = Exact<{ + appleProvisioningProfileId: Scalars['ID']; + appleProvisioningProfileInput: AppleProvisioningProfileInput; +}>; + + +export type UpdateAppleProvisioningProfileMutation = ( + { __typename?: 'RootMutation' } + & { appleProvisioningProfile: ( + { __typename?: 'AppleProvisioningProfileMutation' } + & { updateAppleProvisioningProfile: ( + { __typename?: 'AppleProvisioningProfile' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )> } + & AppleProvisioningProfileFragment + ) } + ) } +); + +export type DeleteAppleProvisioningProfilesMutationVariables = Exact<{ + appleProvisioningProfileIds: Array; +}>; + + +export type DeleteAppleProvisioningProfilesMutation = ( + { __typename?: 'RootMutation' } + & { appleProvisioningProfile: ( + { __typename?: 'AppleProvisioningProfileMutation' } + & { deleteAppleProvisioningProfiles: Array<( + { __typename?: 'DeleteAppleProvisioningProfileResult' } + & Pick + )> } + ) } +); + +export type CreateApplePushKeyMutationVariables = Exact<{ + applePushKeyInput: ApplePushKeyInput; + accountId: Scalars['ID']; +}>; + + +export type CreateApplePushKeyMutation = ( + { __typename?: 'RootMutation' } + & { applePushKey: ( + { __typename?: 'ApplePushKeyMutation' } + & { createApplePushKey: ( + { __typename?: 'ApplePushKey' } + & Pick + & ApplePushKeyFragment + ) } + ) } +); + +export type DeleteApplePushKeyMutationVariables = Exact<{ + applePushKeyId: Scalars['ID']; +}>; + + +export type DeleteApplePushKeyMutation = ( + { __typename?: 'RootMutation' } + & { applePushKey: ( + { __typename?: 'ApplePushKeyMutation' } + & { deleteApplePushKey: ( + { __typename?: 'deleteApplePushKeyResult' } + & Pick + ) } + ) } +); + +export type CreateAppleTeamMutationVariables = Exact<{ + appleTeamInput: AppleTeamInput; + accountId: Scalars['ID']; +}>; + + +export type CreateAppleTeamMutation = ( + { __typename?: 'RootMutation' } + & { appleTeam: ( + { __typename?: 'AppleTeamMutation' } + & { createAppleTeam: ( + { __typename?: 'AppleTeam' } + & Pick + & { account: ( + { __typename?: 'Account' } + & Pick + ) } + & AppleTeamFragment + ) } + ) } +); + +export type CreateIosAppBuildCredentialsMutationVariables = Exact<{ + iosAppBuildCredentialsInput: IosAppBuildCredentialsInput; + iosAppCredentialsId: Scalars['ID']; +}>; + + +export type CreateIosAppBuildCredentialsMutation = ( + { __typename?: 'RootMutation' } + & { iosAppBuildCredentials: ( + { __typename?: 'IosAppBuildCredentialsMutation' } + & { createIosAppBuildCredentials: ( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & IosAppBuildCredentialsFragment + ) } + ) } +); + +export type SetDistributionCertificateMutationVariables = Exact<{ + iosAppBuildCredentialsId: Scalars['ID']; + distributionCertificateId: Scalars['ID']; +}>; + + +export type SetDistributionCertificateMutation = ( + { __typename?: 'RootMutation' } + & { iosAppBuildCredentials: ( + { __typename?: 'IosAppBuildCredentialsMutation' } + & { setDistributionCertificate: ( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & IosAppBuildCredentialsFragment + ) } + ) } +); + +export type SetProvisioningProfileMutationVariables = Exact<{ + iosAppBuildCredentialsId: Scalars['ID']; + provisioningProfileId: Scalars['ID']; +}>; + + +export type SetProvisioningProfileMutation = ( + { __typename?: 'RootMutation' } + & { iosAppBuildCredentials: ( + { __typename?: 'IosAppBuildCredentialsMutation' } + & { setProvisioningProfile: ( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & IosAppBuildCredentialsFragment + ) } + ) } +); + +export type CreateIosAppCredentialsMutationVariables = Exact<{ + iosAppCredentialsInput: IosAppCredentialsInput; + appId: Scalars['ID']; + appleAppIdentifierId: Scalars['ID']; +}>; + + +export type CreateIosAppCredentialsMutation = ( + { __typename?: 'RootMutation' } + & { iosAppCredentials: ( + { __typename?: 'IosAppCredentialsMutation' } + & { createIosAppCredentials: ( + { __typename?: 'IosAppCredentials' } + & Pick + & CommonIosAppCredentialsFragment + ) } + ) } +); + +export type SetPushKeyMutationVariables = Exact<{ + iosAppCredentialsId: Scalars['ID']; + pushKeyId: Scalars['ID']; +}>; + + +export type SetPushKeyMutation = ( + { __typename?: 'RootMutation' } + & { iosAppCredentials: ( + { __typename?: 'IosAppCredentialsMutation' } + & { setPushKey: ( + { __typename?: 'IosAppCredentials' } + & Pick + & CommonIosAppCredentialsFragment + ) } + ) } +); + +export type SetAppStoreConnectApiKeyForSubmissionsMutationVariables = Exact<{ + iosAppCredentialsId: Scalars['ID']; + ascApiKeyId: Scalars['ID']; +}>; + + +export type SetAppStoreConnectApiKeyForSubmissionsMutation = ( + { __typename?: 'RootMutation' } + & { iosAppCredentials: ( + { __typename?: 'IosAppCredentialsMutation' } + & { setAppStoreConnectApiKeyForSubmissions: ( + { __typename?: 'IosAppCredentials' } + & Pick + & CommonIosAppCredentialsFragment + ) } + ) } +); + +export type AppByFullNameQueryVariables = Exact<{ + fullName: Scalars['String']; +}>; + + +export type AppByFullNameQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byFullName: ( + { __typename?: 'App' } + & Pick + & AppFragment + ) } + ) } +); + +export type AppStoreConnectApiKeyByAccountQueryVariables = Exact<{ + accountName: Scalars['String']; +}>; + + +export type AppStoreConnectApiKeyByAccountQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { appStoreConnectApiKeys: Array<( + { __typename?: 'AppStoreConnectApiKey' } + & Pick + & AppStoreConnectApiKeyFragment + )> } + ) } + ) } +); + +export type AppleAppIdentifierByBundleIdQueryVariables = Exact<{ + accountName: Scalars['String']; + bundleIdentifier: Scalars['String']; +}>; + + +export type AppleAppIdentifierByBundleIdQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { appleAppIdentifiers: Array<( + { __typename?: 'AppleAppIdentifier' } + & Pick + & AppleAppIdentifierFragment + )> } + ) } + ) } +); + +export type AppleDevicesByAppleTeamQueryVariables = Exact<{ + accountId: Scalars['ID']; + appleTeamIdentifier: Scalars['String']; +}>; + + +export type AppleDevicesByAppleTeamQuery = ( + { __typename?: 'RootQuery' } + & { appleTeam: ( + { __typename?: 'AppleTeamQuery' } + & { byAppleTeamIdentifier?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & { appleDevices: Array<( + { __typename?: 'AppleDevice' } + & Pick + & { appleTeam: ( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + ) } + & AppleDeviceFragment + )> } + & AppleTeamFragment + )> } + ) } +); + +export type AppleDevicesByTeamIdentifierQueryVariables = Exact<{ + accountName: Scalars['String']; + appleTeamIdentifier: Scalars['String']; +}>; + + +export type AppleDevicesByTeamIdentifierQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { appleTeams: Array<( + { __typename?: 'AppleTeam' } + & Pick + & { appleDevices: Array<( + { __typename?: 'AppleDevice' } + & Pick + )> } + )> } + ) } + ) } +); + +export type AppleDevicesByIdentifierQueryVariables = Exact<{ + accountName: Scalars['String']; + identifier: Scalars['String']; +}>; + + +export type AppleDevicesByIdentifierQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { appleDevices: Array<( + { __typename?: 'AppleDevice' } + & Pick + & { appleTeam: ( + { __typename?: 'AppleTeam' } + & Pick + ) } + )> } + ) } + ) } +); + +export type AppleDistributionCertificateByAppQueryVariables = Exact<{ + projectFullName: Scalars['String']; + appleAppIdentifierId: Scalars['String']; + iosDistributionType: IosDistributionType; +}>; + + +export type AppleDistributionCertificateByAppQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byFullName: ( + { __typename?: 'App' } + & Pick + & { iosAppCredentials: Array<( + { __typename?: 'IosAppCredentials' } + & Pick + & { iosAppBuildCredentialsList: Array<( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & { distributionCertificate?: Maybe<( + { __typename?: 'AppleDistributionCertificate' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )> } + & AppleDistributionCertificateFragment + )> } + )> } + )> } + ) } + ) } +); + +export type AppleDistributionCertificateByAccountQueryVariables = Exact<{ + accountName: Scalars['String']; +}>; + + +export type AppleDistributionCertificateByAccountQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { appleDistributionCertificates: Array<( + { __typename?: 'AppleDistributionCertificate' } + & Pick + & AppleDistributionCertificateFragment + )> } + ) } + ) } +); + +export type AppleProvisioningProfilesByAppQueryVariables = Exact<{ + projectFullName: Scalars['String']; + appleAppIdentifierId: Scalars['String']; + iosDistributionType: IosDistributionType; +}>; + + +export type AppleProvisioningProfilesByAppQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byFullName: ( + { __typename?: 'App' } + & Pick + & { iosAppCredentials: Array<( + { __typename?: 'IosAppCredentials' } + & Pick + & { iosAppBuildCredentialsList: Array<( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & { provisioningProfile?: Maybe<( + { __typename?: 'AppleProvisioningProfile' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )>, appleDevices: Array<( + { __typename?: 'AppleDevice' } + & Pick + & AppleDeviceFragment + )>, appleAppIdentifier: ( + { __typename?: 'AppleAppIdentifier' } + & Pick + & AppleAppIdentifierFragment + ) } + & AppleProvisioningProfileFragment + )> } + )> } + )> } + ) } + ) } +); + +export type ApplePushKeyByAccountQueryVariables = Exact<{ + accountName: Scalars['String']; +}>; + + +export type ApplePushKeyByAccountQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { applePushKeys: Array<( + { __typename?: 'ApplePushKey' } + & Pick + & ApplePushKeyFragment + )> } + ) } + ) } +); + +export type AppleTeamsByAccountNameQueryVariables = Exact<{ + accountName: Scalars['String']; +}>; + + +export type AppleTeamsByAccountNameQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { appleTeams: Array<( + { __typename?: 'AppleTeam' } + & Pick + )> } + ) } + ) } +); + +export type AppleTeamByIdentifierQueryVariables = Exact<{ + accountId: Scalars['ID']; + appleTeamIdentifier: Scalars['String']; +}>; + + +export type AppleTeamByIdentifierQuery = ( + { __typename?: 'RootQuery' } + & { appleTeam: ( + { __typename?: 'AppleTeamQuery' } + & { byAppleTeamIdentifier?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )> } + ) } +); + +export type IosAppBuildCredentialsByAppleAppIdentiferAndDistributionQueryVariables = Exact<{ + projectFullName: Scalars['String']; + appleAppIdentifierId: Scalars['String']; + iosDistributionType: IosDistributionType; +}>; + + +export type IosAppBuildCredentialsByAppleAppIdentiferAndDistributionQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byFullName: ( + { __typename?: 'App' } + & Pick + & { iosAppCredentials: Array<( + { __typename?: 'IosAppCredentials' } + & Pick + & { iosAppBuildCredentialsList: Array<( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & IosAppBuildCredentialsFragment + )> } + )> } + ) } + ) } +); + +export type IosAppCredentialsWithBuildCredentialsByAppIdentifierIdQueryVariables = Exact<{ + projectFullName: Scalars['String']; + appleAppIdentifierId: Scalars['String']; + iosDistributionType?: Maybe; +}>; + + +export type IosAppCredentialsWithBuildCredentialsByAppIdentifierIdQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byFullName: ( + { __typename?: 'App' } + & Pick + & { iosAppCredentials: Array<( + { __typename?: 'IosAppCredentials' } + & Pick + & { iosAppBuildCredentialsList: Array<( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & IosAppBuildCredentialsFragment + )> } + & CommonIosAppCredentialsWithoutBuildCredentialsFragment + )> } + ) } + ) } +); + +export type CommonIosAppCredentialsWithBuildCredentialsByAppIdentifierIdQueryVariables = Exact<{ + projectFullName: Scalars['String']; + appleAppIdentifierId: Scalars['String']; +}>; + + +export type CommonIosAppCredentialsWithBuildCredentialsByAppIdentifierIdQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byFullName: ( + { __typename?: 'App' } + & Pick + & { iosAppCredentials: Array<( + { __typename?: 'IosAppCredentials' } + & Pick + & CommonIosAppCredentialsFragment + )> } + ) } + ) } +); + +export type CreateAppMutationVariables = Exact<{ + appInput: AppInput; +}>; + + +export type CreateAppMutation = ( + { __typename?: 'RootMutation' } + & { app?: Maybe<( + { __typename?: 'AppMutation' } + & { createApp: ( + { __typename?: 'App' } + & Pick + ) } + )> } +); + +export type CreateAndroidBuildMutationVariables = Exact<{ + appId: Scalars['ID']; + job: AndroidJobInput; + metadata?: Maybe; +}>; + + +export type CreateAndroidBuildMutation = ( + { __typename?: 'RootMutation' } + & { build?: Maybe<( + { __typename?: 'BuildMutation' } + & { createAndroidBuild: ( + { __typename?: 'CreateBuildResult' } + & { build: ( + { __typename?: 'Build' } + & Pick + & BuildFragment + ), deprecationInfo?: Maybe<( + { __typename?: 'EASBuildDeprecationInfo' } + & Pick + )> } + ) } + )> } +); + +export type CreateIosBuildMutationVariables = Exact<{ + appId: Scalars['ID']; + job: IosJobInput; + metadata?: Maybe; +}>; + + +export type CreateIosBuildMutation = ( + { __typename?: 'RootMutation' } + & { build?: Maybe<( + { __typename?: 'BuildMutation' } + & { createIosBuild: ( + { __typename?: 'CreateBuildResult' } + & { build: ( + { __typename?: 'Build' } + & Pick + & BuildFragment + ), deprecationInfo?: Maybe<( + { __typename?: 'EASBuildDeprecationInfo' } + & Pick + )> } + ) } + )> } +); + +export type CreateEnvironmentSecretForAccountMutationVariables = Exact<{ + input: CreateEnvironmentSecretInput; + accountId: Scalars['String']; +}>; + + +export type CreateEnvironmentSecretForAccountMutation = ( + { __typename?: 'RootMutation' } + & { environmentSecret: ( + { __typename?: 'EnvironmentSecretMutation' } + & { createEnvironmentSecretForAccount: ( + { __typename?: 'EnvironmentSecret' } + & Pick + & EnvironmentSecretFragment + ) } + ) } +); + +export type CreateEnvironmentSecretForAppMutationVariables = Exact<{ + input: CreateEnvironmentSecretInput; + appId: Scalars['String']; +}>; + + +export type CreateEnvironmentSecretForAppMutation = ( + { __typename?: 'RootMutation' } + & { environmentSecret: ( + { __typename?: 'EnvironmentSecretMutation' } + & { createEnvironmentSecretForApp: ( + { __typename?: 'EnvironmentSecret' } + & Pick + & EnvironmentSecretFragment + ) } + ) } +); + +export type DeleteEnvironmentSecretMutationVariables = Exact<{ + id: Scalars['String']; +}>; + + +export type DeleteEnvironmentSecretMutation = ( + { __typename?: 'RootMutation' } + & { environmentSecret: ( + { __typename?: 'EnvironmentSecretMutation' } + & { deleteEnvironmentSecret: ( + { __typename?: 'DeleteEnvironmentSecretResult' } + & Pick + ) } + ) } +); + +export type CreateKeystoreGenerationUrlMutationVariables = Exact<{ [key: string]: never; }>; + + +export type CreateKeystoreGenerationUrlMutation = ( + { __typename?: 'RootMutation' } + & { keystoreGenerationUrl: ( + { __typename?: 'KeystoreGenerationUrlMutation' } + & { createKeystoreGenerationUrl: ( + { __typename?: 'KeystoreGenerationUrl' } + & Pick + ) } + ) } +); + +export type GetSignedUploadMutationVariables = Exact<{ + contentTypes: Array; +}>; + + +export type GetSignedUploadMutation = ( + { __typename?: 'RootMutation' } + & { asset: ( + { __typename?: 'AssetMutation' } + & { getSignedAssetUploadSpecifications?: Maybe<( + { __typename?: 'GetSignedAssetUploadSpecificationsResult' } + & Pick + )> } + ) } +); + +export type UpdatePublishMutationVariables = Exact<{ + publishUpdateGroupsInput: Array; +}>; + + +export type UpdatePublishMutation = ( + { __typename?: 'RootMutation' } + & { updateBranch: ( + { __typename?: 'UpdateBranchMutation' } + & { publishUpdateGroups: Array<( + { __typename?: 'Update' } + & Pick + )> } + ) } +); + +export type CreateAndroidSubmissionMutationVariables = Exact<{ + appId: Scalars['ID']; + config: AndroidSubmissionConfigInput; + submittedBuildId?: Maybe; +}>; + + +export type CreateAndroidSubmissionMutation = ( + { __typename?: 'RootMutation' } + & { submission: ( + { __typename?: 'SubmissionMutation' } + & { createAndroidSubmission: ( + { __typename?: 'CreateSubmissionResult' } + & { submission: ( + { __typename?: 'Submission' } + & Pick + & SubmissionFragment + ) } + ) } + ) } +); + +export type CreateIosSubmissionMutationVariables = Exact<{ + appId: Scalars['ID']; + config: IosSubmissionConfigInput; + submittedBuildId?: Maybe; +}>; + + +export type CreateIosSubmissionMutation = ( + { __typename?: 'RootMutation' } + & { submission: ( + { __typename?: 'SubmissionMutation' } + & { createIosSubmission: ( + { __typename?: 'CreateSubmissionResult' } + & { submission: ( + { __typename?: 'Submission' } + & Pick + & SubmissionFragment + ) } + ) } + ) } +); + +export type CreateUploadSessionMutationVariables = Exact<{ + type: UploadSessionType; +}>; + + +export type CreateUploadSessionMutation = ( + { __typename?: 'RootMutation' } + & { uploadSession: ( + { __typename?: 'UploadSession' } + & Pick + ) } +); + +export type CreateWebhookMutationVariables = Exact<{ + appId: Scalars['String']; + webhookInput: WebhookInput; +}>; + + +export type CreateWebhookMutation = ( + { __typename?: 'RootMutation' } + & { webhook: ( + { __typename?: 'WebhookMutation' } + & { createWebhook: ( + { __typename?: 'Webhook' } + & Pick + & WebhookFragment + ) } + ) } +); + +export type UpdateWebhookMutationVariables = Exact<{ + webhookId: Scalars['ID']; + webhookInput: WebhookInput; +}>; + + +export type UpdateWebhookMutation = ( + { __typename?: 'RootMutation' } + & { webhook: ( + { __typename?: 'WebhookMutation' } + & { updateWebhook: ( + { __typename?: 'Webhook' } + & Pick + & WebhookFragment + ) } + ) } +); + +export type DeleteWebhookMutationVariables = Exact<{ + webhookId: Scalars['ID']; +}>; + + +export type DeleteWebhookMutation = ( + { __typename?: 'RootMutation' } + & { webhook: ( + { __typename?: 'WebhookMutation' } + & { deleteWebhook: ( + { __typename?: 'DeleteWebhookResult' } + & Pick + ) } + ) } +); + +export type BuildsByIdQueryVariables = Exact<{ + buildId: Scalars['ID']; +}>; + + +export type BuildsByIdQuery = ( + { __typename?: 'RootQuery' } + & { builds: ( + { __typename?: 'BuildQuery' } + & { byId: ( + { __typename?: 'Build' } + & Pick + & BuildFragment + ) } + ) } +); + +export type GetAllBuildsForAppQueryVariables = Exact<{ + appId: Scalars['String']; + offset: Scalars['Int']; + limit: Scalars['Int']; + filter?: Maybe; +}>; + + +export type GetAllBuildsForAppQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { builds: Array<( + { __typename?: 'Build' } + & Pick + & BuildFragment + )> } + ) } + ) } +); + +export type EnvironmentSecretsByAccountNameQueryVariables = Exact<{ + accountName: Scalars['String']; +}>; + + +export type EnvironmentSecretsByAccountNameQuery = ( + { __typename?: 'RootQuery' } + & { account: ( + { __typename?: 'AccountQuery' } + & { byName: ( + { __typename?: 'Account' } + & Pick + & { environmentSecrets: Array<( + { __typename?: 'EnvironmentSecret' } + & Pick + & EnvironmentSecretFragment + )> } + ) } + ) } +); + +export type EnvironmentSecretsByAppIdQueryVariables = Exact<{ + appId: Scalars['String']; +}>; + + +export type EnvironmentSecretsByAppIdQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { environmentSecrets: Array<( + { __typename?: 'EnvironmentSecret' } + & Pick + & EnvironmentSecretFragment + )> } + ) } + ) } +); + +export type ProjectByUsernameAndSlugQueryVariables = Exact<{ + username: Scalars['String']; + slug: Scalars['String']; +}>; + + +export type ProjectByUsernameAndSlugQuery = ( + { __typename?: 'RootQuery' } + & { project: ( + { __typename?: 'ProjectQuery' } + & { byUsernameAndSlug: ( + { __typename?: 'Snack' } + & Pick + ) | ( + { __typename?: 'App' } + & Pick + ) } + ) } +); + +export type GetAssetMetadataQueryVariables = Exact<{ + storageKeys: Array; +}>; + + +export type GetAssetMetadataQuery = ( + { __typename?: 'RootQuery' } + & { asset: ( + { __typename?: 'AssetQuery' } + & { metadata: Array<( + { __typename?: 'AssetMetadataResult' } + & Pick + )> } + ) } +); + +export type SubmissionsByIdQueryVariables = Exact<{ + submissionId: Scalars['ID']; +}>; + + +export type SubmissionsByIdQuery = ( + { __typename?: 'RootQuery' } + & { submissions: ( + { __typename?: 'SubmissionQuery' } + & { byId: ( + { __typename?: 'Submission' } + & Pick + & SubmissionFragment + ) } + ) } +); + +export type GetAllSubmissionsForAppQueryVariables = Exact<{ + appId: Scalars['String']; + offset: Scalars['Int']; + limit: Scalars['Int']; + status?: Maybe; + platform?: Maybe; +}>; + + +export type GetAllSubmissionsForAppQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { submissions: Array<( + { __typename?: 'Submission' } + & Pick + & SubmissionFragment + )> } + ) } + ) } +); + +export type CurrentUserQueryVariables = Exact<{ [key: string]: never; }>; + + +export type CurrentUserQuery = ( + { __typename?: 'RootQuery' } + & { meActor?: Maybe<( + { __typename: 'User' } + & Pick + & { accounts: Array<( + { __typename?: 'Account' } + & Pick + )> } + ) | ( + { __typename: 'Robot' } + & Pick + & { accounts: Array<( + { __typename?: 'Account' } + & Pick + )> } + )> } +); + +export type WebhooksByAppIdQueryVariables = Exact<{ + appId: Scalars['String']; + webhookFilter?: Maybe; +}>; + + +export type WebhooksByAppIdQuery = ( + { __typename?: 'RootQuery' } + & { app: ( + { __typename?: 'AppQuery' } + & { byId: ( + { __typename?: 'App' } + & Pick + & { webhooks: Array<( + { __typename?: 'Webhook' } + & Pick + & WebhookFragment + )> } + ) } + ) } +); + +export type WebhookByIdQueryVariables = Exact<{ + webhookId: Scalars['ID']; +}>; + + +export type WebhookByIdQuery = ( + { __typename?: 'RootQuery' } + & { webhook: ( + { __typename?: 'WebhookQuery' } + & { byId: ( + { __typename?: 'Webhook' } + & Pick + & WebhookFragment + ) } + ) } +); + +export type AppFragment = ( + { __typename?: 'App' } + & Pick +); + +export type BuildFragment = ( + { __typename?: 'Build' } + & Pick + & { error?: Maybe<( + { __typename?: 'BuildError' } + & Pick + )>, artifacts?: Maybe<( + { __typename?: 'BuildArtifacts' } + & Pick + )>, initiatingActor?: Maybe<( + { __typename: 'User' } + & Pick + ) | ( + { __typename: 'Robot' } + & Pick + )>, project: ( + { __typename: 'Snack' } + & Pick + ) | ( + { __typename: 'App' } + & Pick + & { ownerAccount: ( + { __typename?: 'Account' } + & Pick + ) } + ) } +); + +export type EnvironmentSecretFragment = ( + { __typename?: 'EnvironmentSecret' } + & Pick +); + +export type SubmissionFragment = ( + { __typename?: 'Submission' } + & Pick + & { app: ( + { __typename?: 'App' } + & Pick + & { ownerAccount: ( + { __typename?: 'Account' } + & Pick + ) } + ), androidConfig?: Maybe<( + { __typename?: 'AndroidSubmissionConfig' } + & Pick + )>, iosConfig?: Maybe<( + { __typename?: 'IosSubmissionConfig' } + & Pick + )>, error?: Maybe<( + { __typename?: 'SubmissionError' } + & Pick + )> } +); + +export type UpdateBranchFragment = ( + { __typename?: 'UpdateBranch' } + & Pick + & { updates: Array<( + { __typename?: 'Update' } + & Pick + & { actor?: Maybe<( + { __typename: 'User' } + & Pick + ) | ( + { __typename: 'Robot' } + & Pick + )> } + )> } +); + +export type WebhookFragment = ( + { __typename?: 'Webhook' } + & Pick +); + +export type AndroidAppBuildCredentialsFragment = ( + { __typename?: 'AndroidAppBuildCredentials' } + & Pick + & { androidKeystore?: Maybe<( + { __typename?: 'AndroidKeystore' } + & Pick + & AndroidKeystoreFragment + )> } +); + +export type CommonAndroidAppCredentialsFragment = ( + { __typename?: 'AndroidAppCredentials' } + & Pick + & { app: ( + { __typename?: 'App' } + & Pick + & AppFragment + ), androidFcm?: Maybe<( + { __typename?: 'AndroidFcm' } + & Pick + & AndroidFcmFragment + )>, googleServiceAccountKeyForSubmissions?: Maybe<( + { __typename?: 'GoogleServiceAccountKey' } + & Pick + & GoogleServiceAccountKeyFragment + )>, androidAppBuildCredentialsList: Array<( + { __typename?: 'AndroidAppBuildCredentials' } + & Pick + & AndroidAppBuildCredentialsFragment + )> } +); + +export type AndroidFcmFragment = ( + { __typename?: 'AndroidFcm' } + & Pick + & { snippet: ( + { __typename?: 'FcmSnippetLegacy' } + & Pick + ) | ( + { __typename?: 'FcmSnippetV1' } + & Pick + ) } +); + +export type AndroidKeystoreFragment = ( + { __typename?: 'AndroidKeystore' } + & Pick +); + +export type AppStoreConnectApiKeyFragment = ( + { __typename?: 'AppStoreConnectApiKey' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )> } +); + +export type AppleAppIdentifierFragment = ( + { __typename?: 'AppleAppIdentifier' } + & Pick +); + +export type AppleDeviceFragment = ( + { __typename?: 'AppleDevice' } + & Pick +); + +export type AppleDeviceRegistrationRequestFragment = ( + { __typename?: 'AppleDeviceRegistrationRequest' } + & Pick +); + +export type AppleDistributionCertificateFragment = ( + { __typename?: 'AppleDistributionCertificate' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )>, iosAppBuildCredentialsList: Array<( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & { iosAppCredentials: ( + { __typename?: 'IosAppCredentials' } + & Pick + & { app: ( + { __typename?: 'App' } + & Pick + & AppFragment + ), appleAppIdentifier: ( + { __typename?: 'AppleAppIdentifier' } + & Pick + & AppleAppIdentifierFragment + ) } + ), provisioningProfile?: Maybe<( + { __typename?: 'AppleProvisioningProfile' } + & Pick + & AppleProvisioningProfileIdentifiersFragment + )> } + )> } +); + +export type AppleProvisioningProfileFragment = ( + { __typename?: 'AppleProvisioningProfile' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )>, appleDevices: Array<( + { __typename?: 'AppleDevice' } + & Pick + & AppleDeviceFragment + )> } +); + +export type AppleProvisioningProfileIdentifiersFragment = ( + { __typename?: 'AppleProvisioningProfile' } + & Pick +); + +export type ApplePushKeyFragment = ( + { __typename?: 'ApplePushKey' } + & Pick + & { appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )>, iosAppCredentialsList: Array<( + { __typename?: 'IosAppCredentials' } + & Pick + & { app: ( + { __typename?: 'App' } + & Pick + & AppFragment + ), appleAppIdentifier: ( + { __typename?: 'AppleAppIdentifier' } + & Pick + & AppleAppIdentifierFragment + ) } + )> } +); + +export type AppleTeamFragment = ( + { __typename?: 'AppleTeam' } + & Pick +); + +export type GoogleServiceAccountKeyFragment = ( + { __typename?: 'GoogleServiceAccountKey' } + & Pick +); + +export type IosAppBuildCredentialsFragment = ( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & { distributionCertificate?: Maybe<( + { __typename?: 'AppleDistributionCertificate' } + & Pick + & AppleDistributionCertificateFragment + )>, provisioningProfile?: Maybe<( + { __typename?: 'AppleProvisioningProfile' } + & Pick + & AppleProvisioningProfileFragment + )> } +); + +export type CommonIosAppCredentialsWithoutBuildCredentialsFragment = ( + { __typename?: 'IosAppCredentials' } + & Pick + & { app: ( + { __typename?: 'App' } + & Pick + & AppFragment + ), appleTeam?: Maybe<( + { __typename?: 'AppleTeam' } + & Pick + & AppleTeamFragment + )>, appleAppIdentifier: ( + { __typename?: 'AppleAppIdentifier' } + & Pick + & AppleAppIdentifierFragment + ), pushKey?: Maybe<( + { __typename?: 'ApplePushKey' } + & Pick + & ApplePushKeyFragment + )>, appStoreConnectApiKeyForSubmissions?: Maybe<( + { __typename?: 'AppStoreConnectApiKey' } + & Pick + & AppStoreConnectApiKeyFragment + )> } +); + +export type CommonIosAppCredentialsFragment = ( + { __typename?: 'IosAppCredentials' } + & Pick + & { iosAppBuildCredentialsList: Array<( + { __typename?: 'IosAppBuildCredentials' } + & Pick + & IosAppBuildCredentialsFragment + )> } + & CommonIosAppCredentialsWithoutBuildCredentialsFragment +); diff --git a/packages/expo/cli/utils/graphql/queries/UserQuery.ts b/packages/expo/cli/utils/graphql/queries/UserQuery.ts new file mode 100644 index 0000000000000..cc9bb98e61011 --- /dev/null +++ b/packages/expo/cli/utils/graphql/queries/UserQuery.ts @@ -0,0 +1,40 @@ +import gql from 'graphql-tag'; + +import { graphqlClient, withErrorHandlingAsync } from '../client'; +import { CurrentUserQuery } from '../generated'; + +export const UserQuery = { + async currentUserAsync(): Promise { + const data = await withErrorHandlingAsync( + graphqlClient + .query( + gql` + query CurrentUser { + meActor { + __typename + id + ... on User { + username + } + ... on Robot { + firstName + } + accounts { + id + name + } + isExpoAdmin + } + } + `, + /* variables */ undefined, + { + additionalTypenames: ['User'], + } + ) + .toPromise() + ); + + return data.meActor; + }, +}; diff --git a/packages/expo/cli/utils/link.ts b/packages/expo/cli/utils/link.ts index 998efebad17a5..68fe723a96bf8 100644 --- a/packages/expo/cli/utils/link.ts +++ b/packages/expo/cli/utils/link.ts @@ -2,14 +2,38 @@ import chalk from 'chalk'; import terminalLink from 'terminal-link'; /** - * When linking isn't available, format the learn more link better. + * Prints a link for given URL, using text if provided, otherwise text is just the URL. + * Format links as dim (unless disabled) and with an underline. + * + * @example https://expo.dev + */ +export function link( + url: string, + { text = url, dim = true }: { text?: string; dim?: boolean } = {} +): string { + let output: string; + // Links can be disabled via env variables https://github.com/jamestalmage/supports-hyperlinks/blob/master/index.js + if (terminalLink.isSupported) { + output = terminalLink(text, url); + } else { + output = `${text === url ? '' : text + ': '}${chalk.underline(url)}`; + } + return dim ? chalk.dim(output) : output; +} + +/** + * Provide a consistent "Learn more" link experience. + * Format links as dim (unless disabled) with an underline. * * @example [Learn more](https://expo.dev) * @example Learn more: https://expo.dev - * @param url */ -export function learnMore(url: string): string { - return terminalLink(chalk.underline('Learn more.'), url, { - fallback: (text, url) => `Learn more: ${chalk.underline(url)}`, - }); +export function learnMore( + url: string, + { + learnMoreMessage: maybeLearnMoreMessage, + dim = true, + }: { learnMoreMessage?: string; dim?: boolean } = {} +): string { + return link(url, { text: maybeLearnMoreMessage ?? 'Learn more', dim }); } diff --git a/packages/expo/cli/utils/prompts.ts b/packages/expo/cli/utils/prompts.ts index 6a69587e09c68..53f4997ae7f18 100644 --- a/packages/expo/cli/utils/prompts.ts +++ b/packages/expo/cli/utils/prompts.ts @@ -1,4 +1,4 @@ -import prompts, { Options, PromptObject, PromptType } from 'prompts'; +import prompts, { Options, Choice, PromptObject, PromptType } from 'prompts'; import { CI } from './env'; import { AbortCommandError, CommandError } from './errors'; @@ -7,17 +7,22 @@ export type Question = PromptObject & { optionsPerPage?: number; }; +export interface ExpoChoice extends Choice { + value: T; +} + export { PromptType }; type PromptOptions = { nonInteractiveHelp?: string } & Options; +// TODO: rename to `promptAsync` export default function prompt( questions: Question | Question[], { nonInteractiveHelp, ...options }: PromptOptions = {} ) { questions = Array.isArray(questions) ? questions : [questions]; if (CI && questions.length !== 0) { - let message = `Input is required, but Expo CLI is in non-interactive mode.\n`; + let message = `Input is required, but 'npx expo' is in non-interactive mode.\n`; if (nonInteractiveHelp) { message += nonInteractiveHelp; } else { @@ -62,3 +67,22 @@ export async function confirmAsync( ); return value ?? null; } + +export async function selectAsync( + message: string, + choices: ExpoChoice[], + options?: PromptOptions +): Promise { + const { value } = await prompt( + { + message, + choices, + name: 'value', + type: 'select', + }, + options + ); + return value ?? null; +} + +export const promptAsync = prompt; diff --git a/packages/expo/cli/utils/user/UserSettings.ts b/packages/expo/cli/utils/user/UserSettings.ts new file mode 100644 index 0000000000000..7c00cc56a8b13 --- /dev/null +++ b/packages/expo/cli/utils/user/UserSettings.ts @@ -0,0 +1,42 @@ +import JsonFile from '@expo/json-file'; +import os from 'os'; +import path from 'path'; +import process from 'process'; + +// Extracted from https://github.com/sindresorhus/env-paths/blob/main/index.js +function getConfigDirectory() { + // Share data between eas-cli and expo. + const name = 'eas-cli'; + const homedir = os.homedir(); + + if (process.platform === 'darwin') { + const library = path.join(homedir, 'Library'); + return path.join(library, 'Preferences', name); + } + + if (process.platform === 'win32') { + const appData = process.env.APPDATA || path.join(homedir, 'AppData', 'Roaming'); + return path.join(appData, name, 'Config'); + } + + // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + return path.join(process.env.XDG_CONFIG_HOME || path.join(homedir, '.config'), name); +} + +const SETTINGS_FILE_PATH = path.join(getConfigDirectory(), 'user-settings.json'); + +export type UserSettingsData = { + appleId?: string; + amplitudeDeviceId?: string; + amplitudeEnabled?: boolean; + analyticsDeviceId?: string; + analyticsEnabled?: boolean; +}; + +const UserSettings = new JsonFile(SETTINGS_FILE_PATH, { + jsonParseErrorDefault: {}, + cantReadFileDefault: {}, + ensureDir: true, +}); + +export default UserSettings; diff --git a/packages/expo/cli/utils/user/__mocks__/user.ts b/packages/expo/cli/utils/user/__mocks__/user.ts new file mode 100644 index 0000000000000..c9187b138b0ef --- /dev/null +++ b/packages/expo/cli/utils/user/__mocks__/user.ts @@ -0,0 +1,2 @@ +export const getUserAsync = jest.fn(); +export const loginAsync = jest.fn(); diff --git a/packages/expo/cli/utils/user/__tests__/actions-test.ts b/packages/expo/cli/utils/user/__tests__/actions-test.ts new file mode 100644 index 0000000000000..877487f9c0dba --- /dev/null +++ b/packages/expo/cli/utils/user/__tests__/actions-test.ts @@ -0,0 +1,100 @@ +import { ApiV2Error } from '../../errors'; +import { promptAsync } from '../../prompts'; +import { ensureActorHasUsername, showLoginPromptAsync } from '../actions'; +import { retryUsernamePasswordAuthWithOTPAsync, UserSecondFactorDeviceMethod } from '../otp'; +import { Actor, loginAsync } from '../user'; + +jest.mock('../../prompts'); +jest.mock('../../api'); +jest.mock('../otp'); +jest.mock('../user'); + +const asMock = (fn: any): jest.Mock => fn as jest.Mock; + +beforeEach(() => { + asMock(promptAsync).mockReset(); + asMock(promptAsync).mockImplementation(() => { + throw new Error('Should not be called'); + }); + + asMock(loginAsync).mockReset(); +}); + +const userStub: Actor = { + __typename: 'User', + id: 'userId', + username: 'username', + accounts: [], + isExpoAdmin: false, +}; + +const robotStub: Actor = { + __typename: 'Robot', + id: 'userId', + firstName: 'GLaDOS', + accounts: [], + isExpoAdmin: false, +}; + +describe('ensureActorHasUsername', () => { + it('returns username for user actors', () => { + expect(ensureActorHasUsername(userStub)).toBe(userStub.username); + }); + + it('throws for robot actors', () => { + expect(() => ensureActorHasUsername(robotStub)).toThrow('not supported for robot'); + }); +}); + +describe(showLoginPromptAsync, () => { + it('prompts for OTP when 2FA is enabled', async () => { + asMock(promptAsync) + .mockImplementationOnce(() => ({ username: 'hello', password: 'world' })) + .mockImplementationOnce(() => ({ otp: '123456' })) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + asMock(loginAsync) + .mockImplementationOnce(async () => { + throw new ApiV2Error({ code: 'testcode', request: {} } as any, { + message: 'An OTP is required', + code: 'ONE_TIME_PASSWORD_REQUIRED', + metadata: { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.SMS, + sms_phone_number: 'testphone', + }, + ], + smsAutomaticallySent: true, + }, + }); + }) + .mockImplementation(() => {}); + + await showLoginPromptAsync(); + + expect(retryUsernamePasswordAuthWithOTPAsync).toHaveBeenCalledWith('hello', 'world', { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.SMS, + sms_phone_number: 'testphone', + }, + ], + smsAutomaticallySent: true, + }); + }); + + it('does not prompt if all required credentials are provided', async () => { + asMock(promptAsync).mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + asMock(loginAsync).mockImplementation(() => {}); + + await showLoginPromptAsync({ username: 'hello', password: 'world' }); + }); +}); diff --git a/packages/expo/cli/utils/user/__tests__/otp-test.ts b/packages/expo/cli/utils/user/__tests__/otp-test.ts new file mode 100644 index 0000000000000..16ee616c4ffc9 --- /dev/null +++ b/packages/expo/cli/utils/user/__tests__/otp-test.ts @@ -0,0 +1,256 @@ +import * as Log from '../../../log'; +import { apiClient } from '../../api'; +import { promptAsync, selectAsync } from '../../prompts'; +import { retryUsernamePasswordAuthWithOTPAsync, UserSecondFactorDeviceMethod } from '../otp'; +import { loginAsync } from '../user'; + +jest.mock('../../prompts'); +jest.mock('../../api'); +jest.mock('../user'); +jest.mock('../../../log'); + +const asMock = (fn: any): jest.Mock => fn as jest.Mock; + +beforeEach(() => { + asMock(promptAsync).mockReset(); + asMock(promptAsync).mockImplementation(() => { + throw new Error('Should not be called'); + }); + + asMock(selectAsync).mockReset(); + asMock(selectAsync).mockImplementation(() => { + throw new Error('Should not be called'); + }); + + asMock(loginAsync).mockReset(); + asMock(Log.log).mockReset(); +}); + +describe(retryUsernamePasswordAuthWithOTPAsync, () => { + it('shows SMS OTP prompt when SMS is primary and code was automatically sent', async () => { + asMock(promptAsync) + .mockImplementationOnce(() => ({ otp: 'hello' })) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + await retryUsernamePasswordAuthWithOTPAsync('blah', 'blah', { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.SMS, + sms_phone_number: 'testphone', + }, + ], + smsAutomaticallySent: true, + }); + + expect(Log.log).toHaveBeenCalledWith( + 'One-time password was sent to the phone number ending in testphone.' + ); + + expect(asMock(loginAsync)).toHaveBeenCalledTimes(1); + }); + + it('shows authenticator OTP prompt when authenticator is primary', async () => { + asMock(promptAsync) + .mockImplementationOnce(() => ({ otp: 'hello' })) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + await retryUsernamePasswordAuthWithOTPAsync('blah', 'blah', { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + ], + smsAutomaticallySent: false, + }); + + expect(Log.log).toHaveBeenCalledWith('One-time password from authenticator required.'); + expect(asMock(loginAsync)).toHaveBeenCalledTimes(1); + }); + + it('shows menu when user bails on primary', async () => { + asMock(promptAsync) + .mockImplementationOnce(() => ({ otp: null })) + .mockImplementationOnce(() => ({ otp: 'hello' })) // second time it is prompted after selecting backup code + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + asMock(selectAsync) + .mockImplementationOnce(() => -1) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + await retryUsernamePasswordAuthWithOTPAsync('blah', 'blah', { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + { + id: 'p2', + is_primary: false, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + ], + smsAutomaticallySent: false, + }); + + expect(asMock(selectAsync).mock.calls.length).toEqual(1); + expect(asMock(loginAsync)).toHaveBeenCalledTimes(1); + }); + + it('shows a warning when when user bails on primary and does not have any secondary set up', async () => { + asMock(promptAsync) + .mockImplementationOnce(() => ({ otp: null })) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + await expect( + retryUsernamePasswordAuthWithOTPAsync('blah', 'blah', { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + ], + smsAutomaticallySent: false, + }) + ).rejects.toThrowError( + 'No other second-factor devices set up. Ensure you have set up and certified a backup device.' + ); + }); + + it('prompts for authenticator OTP when user selects authenticator secondary', async () => { + asMock(promptAsync) + .mockImplementationOnce(() => ({ otp: null })) + .mockImplementationOnce(() => ({ otp: 'hello' })) // second time it is prompted after selecting backup code + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + asMock(selectAsync) + .mockImplementationOnce(() => -1) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + await retryUsernamePasswordAuthWithOTPAsync('blah', 'blah', { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + { + id: 'p2', + is_primary: false, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + ], + smsAutomaticallySent: false, + }); + + expect(asMock(promptAsync).mock.calls.length).toBe(2); // first OTP, second OTP + }); + + it('requests SMS OTP and prompts for SMS OTP when user selects SMS secondary', async () => { + asMock(promptAsync) + .mockImplementationOnce(() => ({ otp: null })) + .mockImplementationOnce(() => ({ otp: 'hello' })) // second time it is prompted after selecting backup code + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + asMock(selectAsync) + .mockImplementationOnce(() => 0) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + asMock(apiClient.post).mockReturnValueOnce({ + json: () => Promise.resolve({ data: { sessionSecret: 'SESSION_SECRET' } }), + }); + + await retryUsernamePasswordAuthWithOTPAsync('blah', 'blah', { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + { + id: 'p2', + is_primary: false, + method: UserSecondFactorDeviceMethod.SMS, + sms_phone_number: 'wat', + }, + ], + smsAutomaticallySent: false, + }); + + expect(asMock(promptAsync).mock.calls.length).toBe(2); // first OTP, second OTP + expect(asMock(apiClient.post).mock.calls[0]).toEqual([ + 'auth/send-sms-otp', + { + json: { + username: 'blah', + password: 'blah', + secondFactorDeviceID: 'p2', + }, + }, + ]); + }); + + it('exits when user bails on primary and backup', async () => { + asMock(promptAsync) + .mockImplementationOnce(() => ({ otp: null })) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + asMock(selectAsync) + .mockImplementationOnce(() => -2) + .mockImplementation(() => { + throw new Error("shouldn't happen"); + }); + + await expect( + retryUsernamePasswordAuthWithOTPAsync('blah', 'blah', { + secondFactorDevices: [ + { + id: 'p0', + is_primary: true, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + { + id: 'p2', + is_primary: false, + method: UserSecondFactorDeviceMethod.AUTHENTICATOR, + sms_phone_number: null, + }, + ], + smsAutomaticallySent: false, + }) + ).rejects.toThrowError('Interactive prompt was cancelled.'); + }); +}); diff --git a/packages/expo/cli/utils/user/__tests__/sessionStorage-test.ts b/packages/expo/cli/utils/user/__tests__/sessionStorage-test.ts new file mode 100644 index 0000000000000..e277113008318 --- /dev/null +++ b/packages/expo/cli/utils/user/__tests__/sessionStorage-test.ts @@ -0,0 +1,66 @@ +import { getUserStatePath } from '@expo/config/build/getUserState'; +import fs from 'fs-extra'; +import { vol } from 'memfs'; +import path from 'path'; + +import { getAccessToken, getSession, getSessionSecret, setSessionAsync } from '../sessionStorage'; + +jest.mock('fs'); + +const authStub: any = { + sessionSecret: 'SESSION_SECRET', + userId: 'USER_ID', + username: 'USERNAME', + currentConnection: 'Username-Password-Authentication', +}; + +beforeEach(() => { + vol.reset(); +}); + +describe(getSession, () => { + it('returns null when session is not stored', () => { + expect(getSession()).toBeNull(); + }); + + it('returns stored session data', async () => { + await fs.mkdirp(path.dirname(getUserStatePath())); + await fs.writeJSON(getUserStatePath(), { auth: authStub }); + expect(getSession()).toMatchObject(authStub); + }); +}); + +describe(setSessionAsync, () => { + it('stores empty session data', async () => { + await setSessionAsync(); + expect(await fs.pathExists(getUserStatePath())).toBeTruthy(); + }); + + it('stores actual session data', async () => { + await setSessionAsync(authStub); + expect(await fs.readJSON(getUserStatePath())).toMatchObject({ auth: authStub }); + }); +}); + +describe(getAccessToken, () => { + it('returns null when envvar is undefined', () => { + expect(getAccessToken()).toBeNull(); + }); + + it('returns token when envar is defined', () => { + process.env.EXPO_TOKEN = 'mytesttoken'; + expect(getAccessToken()).toBe('mytesttoken'); + process.env.EXPO_TOKEN = undefined; + }); +}); + +describe(getSessionSecret, () => { + it('returns null when session is not stored', () => { + expect(getSessionSecret()).toBeNull(); + }); + + it('returns secret when session is stored', async () => { + await setSessionAsync(authStub); + expect(getSessionSecret()).toBe(authStub.sessionSecret); + }); +}); diff --git a/packages/expo/cli/utils/user/__tests__/user-test.ts b/packages/expo/cli/utils/user/__tests__/user-test.ts new file mode 100644 index 0000000000000..d81a19f2eb1cf --- /dev/null +++ b/packages/expo/cli/utils/user/__tests__/user-test.ts @@ -0,0 +1,120 @@ +import { getUserStatePath } from '@expo/config/build/getUserState'; +import fs from 'fs-extra'; +import { vol } from 'memfs'; + +import { + Actor, + getActorDisplayName, + getSessionSecret, + getUserAsync, + loginAsync, + logoutAsync, +} from '../user'; + +jest.mock('fs'); +jest.mock('../../api', () => ({ + apiClient: { + post: jest.fn(() => { + return { + json: () => Promise.resolve({ data: { sessionSecret: 'SESSION_SECRET' } }), + }; + }), + }, +})); +jest.mock('../../graphql/client', () => ({ + graphqlClient: { + query: () => { + return { + toPromise: () => + Promise.resolve({ data: { viewer: { id: 'USER_ID', username: 'USERNAME' } } }), + }; + }, + }, +})); +jest.mock('../../graphql/queries/UserQuery', () => ({ + UserQuery: { + currentUserAsync: async () => ({ __typename: 'User', username: 'USERNAME', id: 'USER_ID' }), + }, +})); + +beforeEach(() => { + vol.reset(); +}); + +const userStub: Actor = { + __typename: 'User', + id: 'userId', + username: 'username', + accounts: [], + isExpoAdmin: false, +}; + +const robotStub: Actor = { + __typename: 'Robot', + id: 'userId', + firstName: 'GLaDOS', + accounts: [], + isExpoAdmin: false, +}; + +describe(getUserAsync, () => { + it('skips fetching user without access token or session secret', async () => { + expect(await getUserAsync()).toBeUndefined(); + }); + + it('fetches user when access token is defined', async () => { + process.env.EXPO_TOKEN = 'accesstoken'; + expect(await getUserAsync()).toMatchObject({ __typename: 'User' }); + }); + + it('fetches user when session secret is defined', async () => { + await loginAsync({ username: 'USERNAME', password: 'PASSWORD' }); + expect(await getUserAsync()).toMatchObject({ __typename: 'User' }); + }); +}); + +describe(loginAsync, () => { + it('saves user data to ~/.expo/state.json', async () => { + await loginAsync({ username: 'USERNAME', password: 'PASSWORD' }); + + expect(await fs.readFile(getUserStatePath(), 'utf8')).toMatchInlineSnapshot(` + "{ + \\"auth\\": { + \\"sessionSecret\\": \\"SESSION_SECRET\\", + \\"userId\\": \\"USER_ID\\", + \\"username\\": \\"USERNAME\\", + \\"currentConnection\\": \\"Username-Password-Authentication\\" + } + } + " + `); + }); +}); + +describe(logoutAsync, () => { + it('removes the session secret', async () => { + await loginAsync({ username: 'USERNAME', password: 'PASSWORD' }); + expect(getSessionSecret()).toBe('SESSION_SECRET'); + + await logoutAsync(); + expect(getSessionSecret()).toBe(null); + }); +}); + +describe(getActorDisplayName, () => { + it('returns anonymous for unauthenticated users', () => { + expect(getActorDisplayName()).toBe('anonymous'); + }); + + it('returns username for user actors', () => { + expect(getActorDisplayName(userStub)).toBe(userStub.username); + }); + + it('returns firstName with robot prefix for robot actors', () => { + expect(getActorDisplayName(robotStub)).toBe(`${robotStub.firstName} (robot)`); + }); + + it('returns robot prefix only for robot actors without firstName', () => { + expect(getActorDisplayName({ ...robotStub, firstName: undefined })).toBe('robot'); + }); +}); diff --git a/packages/expo/cli/utils/user/actions.ts b/packages/expo/cli/utils/user/actions.ts new file mode 100644 index 0000000000000..c86aa25a38ea4 --- /dev/null +++ b/packages/expo/cli/utils/user/actions.ts @@ -0,0 +1,98 @@ +import assert from 'assert'; +import chalk from 'chalk'; + +import * as Log from '../../Log'; +import { ApiV2Error, CommandError } from '../errors'; +import { learnMore } from '../link'; +import promptAsync, { Question } from '../prompts'; +import { retryUsernamePasswordAuthWithOTPAsync } from './otp'; +import { Actor, getUserAsync, loginAsync } from './user'; + +/** Show login prompt while prompting for missing credentials. */ +export async function showLoginPromptAsync({ + printNewLine = false, + otp, + ...options +}: { + printNewLine?: boolean; + username?: string; + password?: string; + otp?: string; +} = {}): Promise { + const hasCredentials = options.username && options.password; + + if (printNewLine) { + Log.log(); + } + + Log.log(hasCredentials ? 'Logging in to EAS' : 'Log in to EAS'); + + let username = options.username; + let password = options.password; + + if (!hasCredentials) { + const resolved = await promptAsync( + [ + !options.username && { + type: 'text', + name: 'username', + message: 'Email or username', + }, + !options.password && { + type: 'password', + name: 'password', + message: 'Password', + }, + ].filter(Boolean) as Question[], + { + nonInteractiveHelp: `Use the EXPO_TOKEN environment variable to authenticate in CI (${learnMore( + 'https://docs.expo.dev/accounts/programmatic-access/' + )})`, + } + ); + username = resolved.username ?? options.username; + password = resolved.password ?? options.password; + } + + try { + await loginAsync({ + username, + password, + otp, + }); + } catch (e) { + if (e instanceof ApiV2Error && e.expoApiV2ErrorCode === 'ONE_TIME_PASSWORD_REQUIRED') { + await retryUsernamePasswordAuthWithOTPAsync( + username, + password, + e.expoApiV2ErrorMetadata as any + ); + } else { + throw e; + } + } +} + +/** Ensure the user is logged in, if not, prompt to login. */ +export async function ensureLoggedInAsync(): Promise { + let user: Actor | undefined; + try { + user = await getUserAsync(); + } catch {} + + if (!user) { + Log.warn(chalk.yellow`An Expo user account is required to proceed.`); + await showLoginPromptAsync({ printNewLine: true }); + user = await getUserAsync(); + } + + assert(user, 'User should be logged in'); + return user; +} + +export function ensureActorHasUsername(user: Actor): string { + if (user.__typename === 'User') { + return user.username; + } + throw new CommandError('This action is not supported for robot users.'); +} diff --git a/packages/expo/cli/utils/user/otp.ts b/packages/expo/cli/utils/user/otp.ts new file mode 100644 index 0000000000000..7a2aa62357254 --- /dev/null +++ b/packages/expo/cli/utils/user/otp.ts @@ -0,0 +1,178 @@ +import assert from 'assert'; +import chalk from 'chalk'; + +import * as Log from '../../log'; +import { apiClient } from '../api'; +import { AbortCommandError } from '../errors'; +import { learnMore } from '../link'; +import { promptAsync, selectAsync } from '../prompts'; +import { loginAsync } from './user'; + +export enum UserSecondFactorDeviceMethod { + AUTHENTICATOR = 'authenticator', + SMS = 'sms', +} + +export type SecondFactorDevice = { + id: string; + method: UserSecondFactorDeviceMethod; + sms_phone_number: string | null; + is_primary: boolean; +}; + +const nonInteractiveHelp = `Use the EXPO_TOKEN environment variable to authenticate in CI (${learnMore( + 'https://docs.expo.dev/accounts/programmatic-access/' +)})`; + +/** + * Prompt for an OTP with the option to cancel the question by answering empty (pressing return key). + */ +async function promptForOTPAsync(cancelBehavior: 'cancel' | 'menu'): Promise { + const enterMessage = + cancelBehavior === 'cancel' + ? `press ${chalk.bold('Enter')} to cancel` + : `press ${chalk.bold('Enter')} for more options`; + const { otp } = await promptAsync( + { + type: 'text', + name: 'otp', + message: `One-time password or backup code (${enterMessage}):`, + }, + { nonInteractiveHelp } + ); + if (!otp) { + return null; + } + + return otp; +} + +/** + * Prompt for user to choose a backup OTP method. If selected method is SMS, a request + * for a new OTP will be sent to that method. Then, prompt for the OTP, and retry the user login. + */ +async function promptForBackupOTPAsync( + username: string, + password: string, + secondFactorDevices: SecondFactorDevice[] +): Promise { + const nonPrimarySecondFactorDevices = secondFactorDevices.filter((device) => !device.is_primary); + + if (nonPrimarySecondFactorDevices.length === 0) { + throw new Error( + 'No other second-factor devices set up. Ensure you have set up and certified a backup device.' + ); + } + + const hasAuthenticatorSecondFactorDevice = nonPrimarySecondFactorDevices.find( + (device) => device.method === UserSecondFactorDeviceMethod.AUTHENTICATOR + ); + + const smsNonPrimarySecondFactorDevices = nonPrimarySecondFactorDevices.filter( + (device) => device.method === UserSecondFactorDeviceMethod.SMS + ); + + const authenticatorChoiceSentinel = -1; + const cancelChoiceSentinel = -2; + + const deviceChoices = smsNonPrimarySecondFactorDevices.map((device, idx) => ({ + title: device.sms_phone_number!, + value: idx, + })); + + if (hasAuthenticatorSecondFactorDevice) { + deviceChoices.push({ + title: 'Authenticator', + value: authenticatorChoiceSentinel, + }); + } + + deviceChoices.push({ + title: 'Cancel', + value: cancelChoiceSentinel, + }); + + const selectedValue = await selectAsync('Select a second-factor device:', deviceChoices, { + nonInteractiveHelp, + }); + if (selectedValue === cancelChoiceSentinel) { + return null; + } else if (selectedValue === authenticatorChoiceSentinel) { + return await promptForOTPAsync('cancel'); + } + + const device = smsNonPrimarySecondFactorDevices[selectedValue]; + + await apiClient + .post('auth/send-sms-otp', { + json: { + username, + password, + secondFactorDeviceID: device.id, + }, + }) + .json(); + + return await promptForOTPAsync('cancel'); +} + +/** + * Handle the special case error indicating that a second-factor is required for + * authentication. + * + * There are three cases we need to handle: + * 1. User's primary second-factor device was SMS, OTP was automatically sent by the server to that + * device already. In this case we should just prompt for the SMS OTP (or backup code), which the + * user should be receiving shortly. We should give the user a way to cancel and the prompt and move + * to case 3 below. + * 2. User's primary second-factor device is authenticator. In this case we should prompt for authenticator + * OTP (or backup code) and also give the user a way to cancel and move to case 3 below. + * 3. User doesn't have a primary device or doesn't have access to their primary device. In this case + * we should show a picker of the SMS devices that they can have an OTP code sent to, and when + * the user picks one we show a prompt() for the sent OTP. + */ +export async function retryUsernamePasswordAuthWithOTPAsync( + username: string, + password: string, + metadata: { + secondFactorDevices?: SecondFactorDevice[]; + smsAutomaticallySent?: boolean; + } +): Promise { + const { secondFactorDevices, smsAutomaticallySent } = metadata; + assert( + secondFactorDevices !== undefined && smsAutomaticallySent !== undefined, + `Malformed OTP error metadata: ${metadata}` + ); + + const primaryDevice = secondFactorDevices.find((device) => device.is_primary); + let otp: string | null = null; + + if (smsAutomaticallySent) { + assert(primaryDevice, 'OTP should only automatically be sent when there is a primary device'); + Log.log( + `One-time password was sent to the phone number ending in ${primaryDevice.sms_phone_number}.` + ); + otp = await promptForOTPAsync('menu'); + } + + if (primaryDevice?.method === UserSecondFactorDeviceMethod.AUTHENTICATOR) { + Log.log('One-time password from authenticator required.'); + otp = await promptForOTPAsync('menu'); + } + + // user bailed on case 1 or 2, wants to move to case 3 + if (!otp) { + otp = await promptForBackupOTPAsync(username, password, secondFactorDevices); + } + + if (!otp) { + throw new AbortCommandError(); + } + + await loginAsync({ + username, + password, + otp, + }); +} diff --git a/packages/expo/cli/utils/user/sessionStorage.ts b/packages/expo/cli/utils/user/sessionStorage.ts new file mode 100644 index 0000000000000..1510a0e4f648d --- /dev/null +++ b/packages/expo/cli/utils/user/sessionStorage.ts @@ -0,0 +1,42 @@ +import { getUserStatePath } from '@expo/config/build/getUserState'; +import JsonFile from '@expo/json-file'; + +type UserSettingsData = { + auth?: SessionData; +}; + +type SessionData = { + sessionSecret: string; + + // These fields are potentially used by Expo CLI. + userId: string; + username: string; + currentConnection: 'Username-Password-Authentication'; +}; + +export function getSession(): SessionData | null { + try { + return JsonFile.read(getUserStatePath())?.auth ?? null; + } catch (error: any) { + if (error.code === 'ENOENT') { + return null; + } + throw error; + } +} + +export async function setSessionAsync(sessionData?: SessionData): Promise { + await JsonFile.setAsync(getUserStatePath(), 'auth', sessionData, { + default: {}, + ensureDir: true, + }); +} + +export function getAccessToken(): string | null { + // TODO: Move to env + return process.env.EXPO_TOKEN ?? null; +} + +export function getSessionSecret(): string | null { + return getSession()?.sessionSecret ?? null; +} diff --git a/packages/expo/cli/utils/user/user.ts b/packages/expo/cli/utils/user/user.ts new file mode 100644 index 0000000000000..4a5779db251a9 --- /dev/null +++ b/packages/expo/cli/utils/user/user.ts @@ -0,0 +1,96 @@ +import gql from 'graphql-tag'; + +import * as Log from '../../log'; +import * as Analytics from '../analytics/rudderstackClient'; +import { apiClient } from '../api'; +import { graphqlClient } from '../graphql/client'; +import { CurrentUserQuery } from '../graphql/generated'; +import { UserQuery } from '../graphql/queries/UserQuery'; +import { getAccessToken, getSessionSecret, setSessionAsync } from './sessionStorage'; + +// Re-export, but keep in separate file to avoid dependency cycle +export { getSessionSecret, getAccessToken }; + +export type Actor = NonNullable; + +let currentUser: Actor | undefined; + +/** + * Resolve the name of the actor, either normal user or robot user. + * This should be used whenever the "current user" needs to be displayed. + * The display name CANNOT be used as project owner. + */ +export function getActorDisplayName(user?: Actor): string { + switch (user?.__typename) { + case 'User': + return user.username; + case 'Robot': + return user.firstName ? `${user.firstName} (robot)` : 'robot'; + default: + return 'anonymous'; + } +} + +export async function getUserAsync(): Promise { + if (!currentUser && (getAccessToken() || getSessionSecret())) { + const user = await UserQuery.currentUserAsync(); + currentUser = user ?? undefined; + if (user) { + await Analytics.setUserDataAsync(user.id, { + username: getActorDisplayName(user), + user_id: user.id, + user_type: user.__typename, + }); + } + } + return currentUser; +} + +export async function loginAsync({ + username, + password, + otp, +}: { + username: string; + password: string; + otp?: string; +}): Promise { + const body = await apiClient + .post('auth/loginAsync', { json: { username, password, otp } }) + .json(); + const { sessionSecret } = (body as any).data; + const result = await graphqlClient + .query( + gql` + query UserQuery { + viewer { + id + username + } + } + `, + {}, + { + fetchOptions: { + headers: { + 'expo-session': sessionSecret, + }, + }, + additionalTypenames: [] /* UserQuery has immutable fields */, + } + ) + .toPromise(); + const { data } = result; + await setSessionAsync({ + sessionSecret, + userId: data.viewer.id, + username: data.viewer.username, + currentConnection: 'Username-Password-Authentication', + }); +} + +export async function logoutAsync(): Promise { + currentUser = undefined; + await setSessionAsync(undefined); + Log.log('Logged out'); +} diff --git a/packages/expo/cli/whoami/index.ts b/packages/expo/cli/whoami/index.ts new file mode 100644 index 0000000000000..dad8610bdb63b --- /dev/null +++ b/packages/expo/cli/whoami/index.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env node +import chalk from 'chalk'; + +import { Command } from '../../bin/cli'; +import * as Log from '../log'; +import { assertArgs } from '../utils/args'; +import { logCmdError } from '../utils/errors'; + +export const expoWhoami: Command = async (argv) => { + const args = assertArgs( + { + // Types + '--help': Boolean, + // Aliases + '-h': '--help', + }, + argv + ); + + if (args['--help']) { + Log.exit( + chalk` + {bold Description} + Show the currently authenticated username + + {bold Usage} + $ npx expo whoami + + Options + -h, --help Output usage information + `, + 0 + ); + } + + const { whoamiAsync } = await import('./whoamiAsync'); + return whoamiAsync().catch(logCmdError); +}; diff --git a/packages/expo/cli/whoami/whoamiAsync.ts b/packages/expo/cli/whoami/whoamiAsync.ts new file mode 100644 index 0000000000000..3b534677a3dc3 --- /dev/null +++ b/packages/expo/cli/whoami/whoamiAsync.ts @@ -0,0 +1,13 @@ +import chalk from 'chalk'; + +import * as Log from '../log'; +import { getActorDisplayName, getUserAsync } from '../utils/user/user'; + +export async function whoamiAsync() { + const user = await getUserAsync(); + if (user) { + Log.exit(chalk.green(getActorDisplayName(user)), 0); + } else { + Log.exit('Not logged in', 1); + } +} diff --git a/packages/expo/e2e/__tests__/login-test.ts b/packages/expo/e2e/__tests__/login-test.ts new file mode 100644 index 0000000000000..af04ab3a2e256 --- /dev/null +++ b/packages/expo/e2e/__tests__/login-test.ts @@ -0,0 +1,63 @@ +/* eslint-env jest */ +import fs from 'fs/promises'; + +import { execute, projectRoot } from './utils'; + +const originalForceColor = process.env.FORCE_COLOR; +const originalCI = process.env.CI; +beforeAll(async () => { + await fs.mkdir(projectRoot, { recursive: true }); + process.env.FORCE_COLOR = '1'; + process.env.CI = '1'; +}); +afterAll(() => { + process.env.FORCE_COLOR = originalForceColor; + process.env.CI = originalCI; +}); + +it('runs `npx expo login --help`', async () => { + const results = await execute('login', '--help'); + expect(results.stdout).toMatchInlineSnapshot(` + " + Description + Login to an Expo account + + Usage + $ npx expo login + + Options + -u, --username Username + -p, --password Password + --otp One-time password from your 2FA device + -h, --help Output usage information + " + `); +}); + +it('throws on invalid project root', async () => { + expect.assertions(1); + try { + await execute('very---invalid', 'login'); + } catch (e) { + expect(e.stderr).toMatch(/Invalid project root: \//); + } +}); + +it('runs `npx expo login` and throws due to CI', async () => { + expect.assertions(2); + try { + console.log(await execute('login')); + } catch (e) { + expect(e.stderr).toMatch(/Input is required/); + expect(e.stderr).toMatch(/Use the EXPO_TOKEN environment variable to authenticate in CI/); + } +}); + +it('runs `npx expo login` and throws due to invalid credentials', async () => { + expect.assertions(1); + try { + console.log(await execute('login', '--username', 'bacon', '--password', 'invalid')); + } catch (e) { + expect(e.stderr).toMatch(/Invalid username\/password. Please try again/); + } +}); diff --git a/packages/expo/e2e/__tests__/logout-test.ts b/packages/expo/e2e/__tests__/logout-test.ts new file mode 100644 index 0000000000000..7e914b5cdf683 --- /dev/null +++ b/packages/expo/e2e/__tests__/logout-test.ts @@ -0,0 +1,39 @@ +/* eslint-env jest */ +import fs from 'fs/promises'; + +import { execute, projectRoot } from './utils'; + +const originalForceColor = process.env.FORCE_COLOR; + +beforeAll(async () => { + await fs.mkdir(projectRoot, { recursive: true }); + process.env.FORCE_COLOR = '1'; +}); +afterAll(() => { + process.env.FORCE_COLOR = originalForceColor; +}); + +it('runs `npx expo logout --help`', async () => { + const results = await execute('logout', '--help'); + expect(results.stdout).toMatchInlineSnapshot(` + " + Description + Logout of an Expo account + + Usage + $ npx expo logout + + Options + -h, --help Output usage information + " + `); +}); + +it('throws on invalid project root', async () => { + expect.assertions(1); + try { + await execute('very---invalid', 'logout'); + } catch (e) { + expect(e.stderr).toMatch(/Invalid project root: \//); + } +}); diff --git a/packages/expo/e2e/__tests__/register-test.ts b/packages/expo/e2e/__tests__/register-test.ts new file mode 100644 index 0000000000000..e3192e9cf3593 --- /dev/null +++ b/packages/expo/e2e/__tests__/register-test.ts @@ -0,0 +1,50 @@ +/* eslint-env jest */ +import fs from 'fs/promises'; + +import { execute, projectRoot } from './utils'; + +const originalForceColor = process.env.FORCE_COLOR; +const originalCI = process.env.CI; +beforeAll(async () => { + await fs.mkdir(projectRoot, { recursive: true }); + process.env.FORCE_COLOR = '1'; + process.env.CI = '1'; +}); +afterAll(() => { + process.env.FORCE_COLOR = originalForceColor; + process.env.CI = originalCI; +}); + +it('runs `npx expo register --help`', async () => { + const results = await execute('register', '--help'); + expect(results.stdout).toMatchInlineSnapshot(` + " + Description + Sign up for a new Expo account + + Usage + $ npx expo register + + Options + -h, --help Output usage information + " + `); +}); + +it('throws on invalid project root', async () => { + expect.assertions(1); + try { + await execute('very---invalid', 'register'); + } catch (e) { + expect(e.stderr).toMatch(/Invalid project root: \//); + } +}); + +it('runs `npx expo register` and throws due to CI', async () => { + expect.assertions(1); + try { + console.log(await execute('register')); + } catch (e) { + expect(e.stderr).toMatch(/Cannot register an account in CI/); + } +}); diff --git a/packages/expo/e2e/__tests__/whoami-test.ts b/packages/expo/e2e/__tests__/whoami-test.ts new file mode 100644 index 0000000000000..6ff100b972d1e --- /dev/null +++ b/packages/expo/e2e/__tests__/whoami-test.ts @@ -0,0 +1,63 @@ +/* eslint-env jest */ +import fs from 'fs/promises'; +import os from 'os'; + +import { execute, projectRoot } from './utils'; + +const originalForceColor = process.env.FORCE_COLOR; +beforeAll(async () => { + await fs.mkdir(projectRoot, { recursive: true }); + process.env.FORCE_COLOR = '1'; +}); +afterAll(() => { + process.env.FORCE_COLOR = originalForceColor; +}); + +it('runs `npx expo whoami --help`', async () => { + const results = await execute('whoami', '--help'); + expect(results.stdout).toMatchInlineSnapshot(` + " + Description + Show the currently authenticated username + + Usage + $ npx expo whoami + + Options + -h, --help Output usage information + " + `); +}); + +it('throws on invalid project root', async () => { + expect.assertions(1); + try { + await execute('very---invalid', 'whoami'); + } catch (e) { + expect(e.stderr).toMatch(/Invalid project root: \//); + } +}); + +it('runs `npx expo whoami`', async () => { + const results = await execute('whoami').catch((e) => e); + + // Test logged in or logged out. + if (results.stderr) { + expect(results.stderr.trim()).toBe('Not logged in'); + } else { + expect(results.stdout.trim()).toBe(expect.any(String)); + // Ensure this can always be used as a means of automation. + expect(results.stdout.trim().split(os.EOL)).toBe(1); + } +}); + +if (process.env.CI) { + it('runs `npx expo whoami` and throws logged out error', async () => { + expect.assertions(1); + try { + console.log(await execute('whoami')); + } catch (e) { + expect(e.stderr).toMatch(/Not logged in/); + } + }); +} diff --git a/packages/expo/package.json b/packages/expo/package.json index 677ca9251834a..f3a7f0902b59d 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -63,12 +63,16 @@ "@expo/config-plugins": "^4.0.15", "@expo/json-file": "8.2.34", "@expo/metro-config": "~0.3.7", + "@expo/rudder-sdk-node": "1.1.1", "@expo/package-manager": "0.0.49", "@expo/prebuild-config": "^3.0.16", "@expo/spawn-async": "1.5.0", "@expo/vector-icons": "^12.0.4", + "@urql/core": "2.3.1", + "@urql/exchange-retry": "0.3.0", "arg": "4.1.0", "babel-preset-expo": "~9.0.1", + "better-opn": "~3.0.2", "cacache": "^15.3.0", "chalk": "^4.0.0", "cross-spawn": "^6.0.5", @@ -84,6 +88,9 @@ "form-data": "^2.3.2", "fs-extra": "9.0.0", "getenv": "^1.0.0", + "got": "11.8.2", + "graphql": "15.5.1", + "graphql-tag": "2.12.5", "invariant": "^2.2.4", "js-yaml": "^3.13.1", "md5-file": "^3.2.3", diff --git a/yarn.lock b/yarn.lock index bbbe90fae6e21..50174f0c99eae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2340,7 +2340,7 @@ tslib "~2.3.0" value-or-promise "1.0.11" -"@graphql-typed-document-node/core@^3.0.0": +"@graphql-typed-document-node/core@^3.0.0", "@graphql-typed-document-node/core@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052" integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== @@ -3395,6 +3395,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@sindresorhus/is@^4.0.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.4.0.tgz#e277e5bdbdf7cb1e20d320f02f5e2ed113cd3185" + integrity sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ== + "@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -3531,6 +3536,13 @@ dependencies: defer-to-connect "^1.0.1" +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + "@taskr/clear@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@taskr/clear/-/clear-1.1.0.tgz#0a88d180bed2f91f310136375a72c00b50834fd1" @@ -3666,6 +3678,16 @@ dependencies: "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" + integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/cheerio@*", "@types/cheerio@^0.22.22": version "0.22.30" resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.30.tgz#6c1ded70d20d890337f0f5144be2c5e9ce0936e6" @@ -3769,6 +3791,11 @@ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" integrity sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w== +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + "@types/i18n-js@^3.0.1": version "3.8.2" resolved "https://registry.yarnpkg.com/@types/i18n-js/-/i18n-js-3.8.2.tgz#957a3fa268124d09e3b3b34695f0184118f4bc4f" @@ -3846,6 +3873,13 @@ dependencies: "@types/node" "*" +"@types/keyv@*": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.3.tgz#1c9aae32872ec1f20dcdaee89a9f3ba88f465e41" + integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== + dependencies: + "@types/node" "*" + "@types/lodash@^4.14.161": version "4.14.178" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8" @@ -3971,6 +4005,13 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/retry@^0.12.0": version "0.12.1" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" @@ -4188,6 +4229,30 @@ "@typescript-eslint/types" "5.9.0" eslint-visitor-keys "^3.0.0" +"@urql/core@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.3.1.tgz#1aa558b5aa6eca785062a3a768e6fec708aab6d2" + integrity sha512-M9gGz02+TH7buqkelVg+m0eTkLCKly9ZjuTp6NHMP7hXD/zpImPu/m3YluA+D4HbFqw3kofBtLCuVk67X9dxRw== + dependencies: + "@graphql-typed-document-node/core" "^3.1.0" + wonka "^4.0.14" + +"@urql/core@>=2.3.1": + version "2.3.6" + resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.3.6.tgz#ee0a6f8fde02251e9560c5f17dce5cd90f948552" + integrity sha512-PUxhtBh7/8167HJK6WqBv6Z0piuiaZHQGYbhwpNL9aIQmLROPEdaUYkY4wh45wPQXcTpnd11l0q3Pw+TI11pdw== + dependencies: + "@graphql-typed-document-node/core" "^3.1.0" + wonka "^4.0.14" + +"@urql/exchange-retry@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@urql/exchange-retry/-/exchange-retry-0.3.0.tgz#13252108b5a111aab45f9982f4db18d1a286e423" + integrity sha512-hHqer2mcdVC0eYnVNbWyi28AlGOPb2vjH3lP3/Bc8Lc8BjhMsDwFMm7WhoP5C1+cfbr/QJ6Er3H/L08wznXxfg== + dependencies: + "@urql/core" ">=2.3.1" + wonka "^4.0.14" + "@use-expo/permissions@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@use-expo/permissions/-/permissions-2.0.0.tgz#1befc01397eed277f24beda137b110721fbe4868" @@ -5319,10 +5384,10 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -better-opn@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-3.0.1.tgz#01d4dd3bde34d959a0645281fb6b2e35ef91b6f7" - integrity sha512-u7pU4QnwLQ+wCDLHdvtWbI/41pSRayJ+UHyAqpb5sr42FGnqzBlEyWdCklfaSzXqbmnXDBzCvWcaZmL3qp0xGA== +better-opn@^3.0.1, better-opn@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-3.0.2.tgz#f96f35deaaf8f34144a4102651babcf00d1d8817" + integrity sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ== dependencies: open "^8.0.4" @@ -5829,6 +5894,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" @@ -5855,6 +5925,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cacheable-request@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -7298,6 +7381,13 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + dedent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.6.0.tgz#0e6da8f0ce52838ef5cec5c8f9396b0c1b64a3cb" @@ -7372,6 +7462,11 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -8548,6 +8643,17 @@ expo-asset-utils@~3.0.0: resolved "https://registry.yarnpkg.com/expo-asset-utils/-/expo-asset-utils-3.0.0.tgz#2c7ddf71ba9efacf7b46c159c1c650114a2f14dc" integrity sha512-CgIbNvTqKqQi1lrlptmwoaCMu4ZVOZf8tghmytlor23CjIOuorw6cfuOqiqWkJLz23arTt91maYEU9XLMPB23A== +expo-modules-autolinking@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-0.5.5.tgz#6bcc42072dcbdfca79d207b7f549f1fdb54a2b74" + integrity sha512-bILEG0Fg+ZhIhdEaShHzsEN1WC0hUmXJ5Kcd4cd+8rVk1Ead9vRZxA/yLx1cNBDCOwMe0GAMrhF7TKT+A1P+YA== + dependencies: + chalk "^4.1.0" + commander "^7.2.0" + fast-glob "^3.2.5" + find-up "^5.0.0" + fs-extra "^9.1.0" + expo-progress@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/expo-progress/-/expo-progress-0.0.2.tgz#0d42470f8f1f019d3ae0f1da377c9bca891f3dd8" @@ -9705,6 +9811,23 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +got@11.8.2: + version "11.8.2" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" + integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.1" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" @@ -9781,6 +9904,13 @@ graphql-sse@^1.0.1: resolved "https://registry.yarnpkg.com/graphql-sse/-/graphql-sse-1.0.6.tgz#4f98e0a06f2020542ed054399116108491263224" integrity sha512-y2mVBN2KwNrzxX2KBncQ6kzc6JWvecxuBernrl0j65hsr6MAS3+Yn8PTFSOgRmtolxugepxveyZVQEuaNEbw3w== +graphql-tag@2.12.5: + version "2.12.5" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.5.tgz#5cff974a67b417747d05c8d9f5f3cb4495d0db8f" + integrity sha512-5xNhP4063d16Pz3HBtKprutsPrmHZi5IdUGOWRxA2B6VF7BIRGOHZ5WQvDmJXZuPcBg7rYwaFxvQYjqkSdR3TQ== + dependencies: + tslib "^2.1.0" + graphql-tag@^2.10.1, graphql-tag@^2.11.0, graphql-tag@^2.12.3: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" @@ -9793,6 +9923,11 @@ graphql-ws@^5.4.1: resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.5.5.tgz#f375486d3f196e2a2527b503644693ae3a8670a9" integrity sha512-hvyIS71vs4Tu/yUYHPvGXsTgo0t3arU820+lT5VjZS2go0ewp2LqyCgxEN56CzOG7Iys52eRhHBiD1gGRdiQtw== +graphql@15.5.1: + version "15.5.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad" + integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw== + graphql@^15.3.0: version "15.8.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" @@ -10258,6 +10393,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -12386,6 +12529,11 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -12587,6 +12735,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.0.5" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.5.tgz#bb12b467aba372fab2a44d4420c00d3c4ebd484c" + integrity sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -13687,6 +13842,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + mini-css-extract-plugin@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" @@ -14214,6 +14374,11 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + npm-package-arg@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-7.0.0.tgz#52cdf08b491c0c59df687c4c925a89102ef794a5" @@ -15889,6 +16054,11 @@ queue@6.0.2: dependencies: inherits "~2.0.3" +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" @@ -16817,6 +16987,11 @@ reselect@^4.0.0: resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6" integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ== +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -16903,6 +17078,13 @@ responselike@1.0.2, responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -19968,6 +20150,11 @@ with-open-file@^0.1.6: p-try "^2.1.0" pify "^4.0.1" +wonka@^4.0.14: + version "4.0.15" + resolved "https://registry.yarnpkg.com/wonka/-/wonka-4.0.15.tgz#9aa42046efa424565ab8f8f451fcca955bf80b89" + integrity sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg== + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" From d6e17c4a35f47508aaa6e50fb5c42da4d2d80650 Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Wed, 26 Jan 2022 18:52:51 -0700 Subject: [PATCH 02/14] Added api tests Update otp.ts Update actions.ts --- packages/expo/cli/utils/__tests__/api-test.ts | 51 +++++++++++++++++++ packages/expo/cli/utils/user/actions.ts | 2 +- packages/expo/cli/utils/user/otp.ts | 1 + packages/expo/package.json | 1 + yarn.lock | 22 +++++++- 5 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 packages/expo/cli/utils/__tests__/api-test.ts diff --git a/packages/expo/cli/utils/__tests__/api-test.ts b/packages/expo/cli/utils/__tests__/api-test.ts new file mode 100644 index 0000000000000..5555985774feb --- /dev/null +++ b/packages/expo/cli/utils/__tests__/api-test.ts @@ -0,0 +1,51 @@ +import assert from 'assert'; +import { RequestError } from 'got/dist/source'; +import nock from 'nock'; + +import { apiClient, getExpoApiBaseUrl } from '../api'; +import { ApiV2Error } from '../errors'; + +it('converts Expo APIv2 error to ApiV2Error', async () => { + nock(getExpoApiBaseUrl()) + .post('/v2/test') + .reply(400, { + errors: [ + { + message: 'hellomessage', + code: 'TEST_CODE', + stack: 'line 1: hello', + details: { who: 'world' }, + metadata: { an: 'object' }, + }, + ], + }); + + let error: Error | null = null; + try { + await apiClient.post('test'); + } catch (e: any) { + error = e; + } + + expect(error).toBeInstanceOf(ApiV2Error); + assert(error instanceof ApiV2Error); + + expect(error.message).toEqual('hellomessage'); + expect(error.expoApiV2ErrorCode).toEqual('TEST_CODE'); + expect(error.expoApiV2ErrorDetails).toEqual({ who: 'world' }); + expect(error.expoApiV2ErrorMetadata).toEqual({ an: 'object' }); + expect(error.expoApiV2ErrorServerStack).toEqual('line 1: hello'); +}); + +it('does not convert non-APIv2 error to ApiV2Error', async () => { + nock(getExpoApiBaseUrl()).post('/v2/test').reply(500, 'Something went wrong'); + + let error: Error | null = null; + try { + await apiClient.post('test'); + } catch (e: any) { + error = e; + } + expect(error).toBeInstanceOf(RequestError); + expect(error).not.toBeInstanceOf(ApiV2Error); +}); diff --git a/packages/expo/cli/utils/user/actions.ts b/packages/expo/cli/utils/user/actions.ts index c86aa25a38ea4..a949122ace118 100644 --- a/packages/expo/cli/utils/user/actions.ts +++ b/packages/expo/cli/utils/user/actions.ts @@ -1,7 +1,7 @@ import assert from 'assert'; import chalk from 'chalk'; -import * as Log from '../../Log'; +import * as Log from '../../log'; import { ApiV2Error, CommandError } from '../errors'; import { learnMore } from '../link'; import promptAsync, { Question } from '../prompts'; diff --git a/packages/expo/cli/utils/user/otp.ts b/packages/expo/cli/utils/user/otp.ts index 7a2aa62357254..e2054b0f3a8a1 100644 --- a/packages/expo/cli/utils/user/otp.ts +++ b/packages/expo/cli/utils/user/otp.ts @@ -13,6 +13,7 @@ export enum UserSecondFactorDeviceMethod { SMS = 'sms', } +/** Device properties for 2FA */ export type SecondFactorDevice = { id: string; method: UserSecondFactorDeviceMethod; diff --git a/packages/expo/package.json b/packages/expo/package.json index f3a7f0902b59d..500a296b44f68 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -132,6 +132,7 @@ "@types/uuid": "^3.4.7", "expo-module-scripts": "^2.0.0", "klaw-sync": "^6.0.0", + "nock": "~13.2.2", "react": "17.0.2", "react-dom": "17.0.2", "react-native": "0.66.4", diff --git a/yarn.lock b/yarn.lock index 50174f0c99eae..430a7d156d5d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12590,7 +12590,7 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -13122,6 +13122,11 @@ lodash.pick@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= +lodash.set@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -14192,6 +14197,16 @@ nocache@^2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +nock@~13.2.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.2.tgz#29a6942250278209c2b3e7a38310f703581b21fa" + integrity sha512-PcBHuvl9i6zfaJ50A7LS55oU+nFLv8htXIhffJO+FxyfibdZ4jEvd9kTuvkrJireBFIGMZ+oUIRpMK5gU9h//g== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash.set "^4.3.2" + propagate "^2.0.0" + node-dir@^0.1.17: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" @@ -15829,6 +15844,11 @@ prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, object-assign "^4.1.1" react-is "^16.13.1" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + proper-lockfile@^3.0.2: version "3.2.0" resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-3.2.0.tgz#89ca420eea1d55d38ca552578851460067bcda66" From 9a32611025e2b46d636d8d0a5424286c0e8394af Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Wed, 26 Jan 2022 19:30:19 -0700 Subject: [PATCH 03/14] Update generated.ts --- packages/expo/cli/utils/graphql/generated.ts | 5208 +----------------- 1 file changed, 115 insertions(+), 5093 deletions(-) diff --git a/packages/expo/cli/utils/graphql/generated.ts b/packages/expo/cli/utils/graphql/generated.ts index c777daa604816..f42a8defd0fcc 100644 --- a/packages/expo/cli/utils/graphql/generated.ts +++ b/packages/expo/cli/utils/graphql/generated.ts @@ -5,10 +5,9 @@ * For more info and docs, visit https://graphql-code-generator.com/ */ -export type Maybe = T | null; -export type Exact = { [K in keyof T]: T[K] }; +type Maybe = T | null; /** All built-in and custom scalars, mapped to their actual values */ -export type Scalars = { +type Scalars = { ID: string; String: string; Boolean: boolean; @@ -24,103 +23,7 @@ export type Scalars = { Upload: any; }; -export type RootQuery = { - __typename?: 'RootQuery'; - /** - * This is a placeholder field - * @deprecated Not used. - */ - _doNotUse?: Maybe; - /** fetch all updates in a group */ - updatesByGroup: Array; - /** Top-level query object for querying Accounts. */ - account: AccountQuery; - /** Top-level query object for querying Actors. */ - actor: ActorQuery; - /** Top-level query object for querying Apple Device registration requests. */ - appleDeviceRegistrationRequest: AppleDeviceRegistrationRequestQuery; - /** Top-level query object for querying Apple Teams. */ - appleTeam: AppleTeamQuery; - app: AppQuery; - /** - * Look up app by app id - * @deprecated Use 'byId' field under 'app'. - */ - appByAppId?: Maybe; - /** - * Public apps in the app directory - * @deprecated Use 'all' field under 'app'. - */ - allPublicApps?: Maybe>>; - asset: AssetQuery; - /** Top-level query object for querying BuildPublicData publicly. */ - buildPublicData: BuildPublicDataQuery; - buildJobs: BuildJobQuery; - builds: BuildQuery; - clientBuilds: ClientBuildQuery; - /** Top-level query object for querying Experimentation configuration. */ - experimentation: ExperimentationQuery; - /** Top-level query object for querying Stripe Invoices. */ - invoice: InvoiceQuery; - project: ProjectQuery; - snack: SnackQuery; - submissions: SubmissionQuery; - /** Top-level query object for querying UserInvitationPublicData publicly. */ - userInvitationPublicData: UserInvitationPublicDataQuery; - /** Top-level query object for querying Users. */ - user: UserQuery; - /** @deprecated Use 'byId' field under 'user'. */ - userByUserId?: Maybe; - /** @deprecated Use 'byUsername' field under 'user'. */ - userByUsername?: Maybe; - /** - * If authenticated as a typical end user, this is the appropriate top-level - * query object - */ - me?: Maybe; - /** - * If authenticated as a typical end user, this is the appropriate top-level - * query object - */ - viewer?: Maybe; - /** - * If authenticated as a any type of Actor, this is the appropriate top-level - * query object - */ - meActor?: Maybe; - /** Top-level query object for querying Webhooks. */ - webhook: WebhookQuery; -}; - - -export type RootQueryUpdatesByGroupArgs = { - group: Scalars['ID']; -}; - - -export type RootQueryAppByAppIdArgs = { - appId: Scalars['String']; -}; - - -export type RootQueryAllPublicAppsArgs = { - filter: AppsFilter; - sort: AppSort; - offset?: Maybe; - limit?: Maybe; -}; - - -export type RootQueryUserByUserIdArgs = { - userId: Scalars['String']; -}; - - -export type RootQueryUserByUsernameArgs = { - username: Scalars['String']; -}; - -export type Update = ActivityTimelineProjectActivity & { +type Update = ActivityTimelineProjectActivity & { __typename?: 'Update'; id: Scalars['ID']; actor?: Maybe; @@ -137,14 +40,14 @@ export type Update = ActivityTimelineProjectActivity & { manifestPermalink: Scalars['String']; }; -export type ActivityTimelineProjectActivity = { +type ActivityTimelineProjectActivity = { id: Scalars['ID']; actor?: Maybe; activityTimestamp: Scalars['DateTime']; }; /** A user or robot that can authenticate with Expo services and be a member of accounts. */ -export type Actor = { +type Actor = { id: Scalars['ID']; firstName?: Maybe; created: Scalars['DateTime']; @@ -165,18 +68,11 @@ export type Actor = { featureGates: Scalars['JSONObject']; }; - -/** A user or robot that can authenticate with Expo services and be a member of accounts. */ -export type ActorFeatureGatesArgs = { - filter?: Maybe>; -}; - - /** * An account is a container owning projects, credentials, billing and other organization * data and settings. Actors may own and be members of accounts. */ -export type Account = { +type Account = { __typename?: 'Account'; id: Scalars['ID']; name: Scalars['String']; @@ -186,8 +82,6 @@ export type Account = { updatedAt: Scalars['DateTime']; /** Offers set on this account */ offers?: Maybe>; - /** Snacks associated with this account */ - snacks: Array; /** Apps associated with this account */ apps: Array; appCount: Scalars['Int']; @@ -208,10 +102,6 @@ export type Account = { users: Array; /** Pending user invitations for this account */ userInvitations: Array; - /** Billing information */ - billing?: Maybe; - /** Subscription info visible to members that have VIEWER role */ - subscription?: Maybe; /** iOS credentials for account */ appleTeams: Array; appleAppIdentifiers: Array; @@ -238,117 +128,7 @@ export type Account = { willAutoRenewBuilds?: Maybe; }; - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountSnacksArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountAppsArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; - includeUnpublished?: Maybe; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountBuildJobsArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; - status?: Maybe; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountBuildsArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; - status?: Maybe; - platform?: Maybe; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountBuildOrBuildJobsArgs = { - limit: Scalars['Int']; - createdBefore?: Maybe; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountActivityTimelineProjectActivitiesArgs = { - limit: Scalars['Int']; - createdBefore?: Maybe; - filterTypes?: Maybe>; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountAppleTeamsArgs = { - appleTeamIdentifier?: Maybe; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountAppleAppIdentifiersArgs = { - bundleIdentifier?: Maybe; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountAppleProvisioningProfilesArgs = { - appleAppIdentifierId?: Maybe; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountAppleDevicesArgs = { - identifier?: Maybe; -}; - - -/** - * An account is a container owning projects, credentials, billing and other organization - * data and settings. Actors may own and be members of accounts. - */ -export type AccountEnvironmentSecretsArgs = { - filterNames?: Maybe>; -}; - -export type Offer = { +type Offer = { __typename?: 'Offer'; id: Scalars['ID']; stripeId: Scalars['ID']; @@ -360,16 +140,16 @@ export type Offer = { prerequisite?: Maybe; }; -export enum OfferType { +enum OfferType { /** Term subscription */ Subscription = 'SUBSCRIPTION', /** Advanced Purchase of Paid Resource */ Prepaid = 'PREPAID', /** Addon, or supplementary subscription */ - Addon = 'ADDON' + Addon = 'ADDON', } -export enum Feature { +enum Feature { /** Top Tier Support */ Support = 'SUPPORT', /** Share access to projects */ @@ -377,39 +157,16 @@ export enum Feature { /** Priority Builds */ Builds = 'BUILDS', /** Funds support for open source development */ - OpenSource = 'OPEN_SOURCE' + OpenSource = 'OPEN_SOURCE', } -export type OfferPrerequisite = { +type OfferPrerequisite = { __typename?: 'OfferPrerequisite'; type: Scalars['String']; stripeIds: Array; }; -export type Snack = Project & { - __typename?: 'Snack'; - id: Scalars['ID']; - hashId: Scalars['String']; - /** Name of the Snack, e.g. "My Snack" */ - name: Scalars['String']; - /** Full name of the Snack, e.g. "@john/mysnack", "@snack/245631" */ - fullName: Scalars['String']; - /** Description of the Snack */ - description: Scalars['String']; - /** @deprecated Field no longer supported */ - iconUrl?: Maybe; - /** Slug name, e.g. "mysnack", "245631" */ - slug: Scalars['String']; - /** Name of the user that created the Snack, or "snack" when the Snack was saved anonymously */ - username: Scalars['String']; - /** Date and time the Snack was last updated */ - updated: Scalars['DateTime']; - published: Scalars['Boolean']; - /** Draft status, which is true when the Snack was not saved explicitly, but auto-saved */ - isDraft: Scalars['Boolean']; -}; - -export type Project = { +type Project = { id: Scalars['ID']; name: Scalars['String']; fullName: Scalars['String']; @@ -423,7 +180,7 @@ export type Project = { }; /** Represents an Exponent App (or Experience in legacy terms) */ -export type App = Project & { +type App = Project & { __typename?: 'App'; id: Scalars['ID']; name: Scalars['String']; @@ -455,31 +212,10 @@ export type App = Project & { appStoreUrl?: Maybe; /** Info about the icon specified in the most recent classic update manifest */ icon?: Maybe; - /** (EAS Build) Builds associated with this app */ - builds: Array; - buildJobs: Array; - /** - * Coalesced Build (EAS) or BuildJob (Classic) items for this app. - * @deprecated Use activityTimelineProjectActivities with filterTypes instead - */ - buildOrBuildJobs: Array; - /** EAS Submissions associated with this app */ - submissions: Array; - /** Deployments associated with this app */ - deployments: Array; - deployment?: Maybe; /** iOS app credentials for the project */ iosAppCredentials: Array; /** Android app credentials for the project */ androidAppCredentials: Array; - /** EAS channels owned by an app */ - updateChannels: Array; - /** get an EAS channel owned by the app by name */ - updateChannelByName?: Maybe; - /** EAS branches owned by an app */ - updateBranches: Array; - /** get an EAS branch owned by the app by name */ - updateBranchByName?: Maybe; /** Coalesced project activity for an app */ activityTimelineProjectActivities: Array; /** Environment secrets for an app */ @@ -517,144 +253,13 @@ export type App = Project & { latestReleaseForReleaseChannel?: Maybe; }; - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppBuildsArgs = { - filter?: Maybe; - offset: Scalars['Int']; - limit: Scalars['Int']; - status?: Maybe; - platform?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppBuildJobsArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; - status?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppBuildOrBuildJobsArgs = { - limit: Scalars['Int']; - createdBefore?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppSubmissionsArgs = { - filter: SubmissionFilter; - offset: Scalars['Int']; - limit: Scalars['Int']; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppDeploymentsArgs = { - limit: Scalars['Int']; - mostRecentlyUpdatedAt?: Maybe; - options?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppDeploymentArgs = { - channel: Scalars['String']; - runtimeVersion: Scalars['String']; - options?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppIosAppCredentialsArgs = { - filter?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppAndroidAppCredentialsArgs = { - filter?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppUpdateChannelsArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppUpdateChannelByNameArgs = { - name: Scalars['String']; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppUpdateBranchesArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppUpdateBranchByNameArgs = { - name: Scalars['String']; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppActivityTimelineProjectActivitiesArgs = { - limit: Scalars['Int']; - createdBefore?: Maybe; - filterTypes?: Maybe>; - filterPlatforms?: Maybe>; - filterReleaseChannels?: Maybe>; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppEnvironmentSecretsArgs = { - filterNames?: Maybe>; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppWebhooksArgs = { - filter?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppLikedByArgs = { - offset?: Maybe; - limit?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppReleasesArgs = { - platform: AppPlatform; - offset?: Maybe; - limit?: Maybe; -}; - - -/** Represents an Exponent App (or Experience in legacy terms) */ -export type AppLatestReleaseForReleaseChannelArgs = { - platform: AppPlatform; - releaseChannel: Scalars['String']; -}; - -export enum AppPrivacy { +enum AppPrivacy { Public = 'PUBLIC', Unlisted = 'UNLISTED', - Hidden = 'HIDDEN' + Hidden = 'HIDDEN', } -export type AppIcon = { +type AppIcon = { __typename?: 'AppIcon'; url: Scalars['String']; primaryColor?: Maybe; @@ -663,82 +268,17 @@ export type AppIcon = { colorPalette?: Maybe; }; - -export type BuildFilter = { - platform?: Maybe; - status?: Maybe; - distribution?: Maybe; - channel?: Maybe; - appVersion?: Maybe; - appBuildVersion?: Maybe; - sdkVersion?: Maybe; - runtimeVersion?: Maybe; - appIdentifier?: Maybe; - buildProfile?: Maybe; - gitCommitHash?: Maybe; -}; - -export enum AppPlatform { +enum AppPlatform { Ios = 'IOS', - Android = 'ANDROID' -} - -export enum BuildStatus { - New = 'NEW', - InQueue = 'IN_QUEUE', - InProgress = 'IN_PROGRESS', - Errored = 'ERRORED', - Finished = 'FINISHED', - Canceled = 'CANCELED' -} - -export enum DistributionType { - Store = 'STORE', - Internal = 'INTERNAL', - Simulator = 'SIMULATOR' + Android = 'ANDROID', } -/** Represents an EAS Build */ -export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & { - __typename?: 'Build'; - id: Scalars['ID']; - actor?: Maybe; - activityTimestamp: Scalars['DateTime']; - project: Project; - /** @deprecated User type is deprecated */ - initiatingUser?: Maybe; - initiatingActor?: Maybe; - cancelingActor?: Maybe; - artifacts?: Maybe; - logFiles: Array; - updatedAt?: Maybe; - createdAt?: Maybe; - status: BuildStatus; - expirationDate?: Maybe; - platform: AppPlatform; - appVersion?: Maybe; - appBuildVersion?: Maybe; - sdkVersion?: Maybe; - runtimeVersion?: Maybe; - reactNativeVersion?: Maybe; - releaseChannel?: Maybe; - channel?: Maybe; - metrics?: Maybe; - distribution?: Maybe; - iosEnterpriseProvisioning?: Maybe; - buildProfile?: Maybe; - gitCommitHash?: Maybe; - isGitWorkingTreeDirty?: Maybe; - error?: Maybe; - submissions: Array; -}; - -export type BuildOrBuildJob = { +type BuildOrBuildJob = { id: Scalars['ID']; }; /** Represents a human (not robot) actor. */ -export type User = Actor & { +type User = Actor & { __typename?: 'User'; id: Scalars['ID']; username: Scalars['String']; @@ -765,8 +305,6 @@ export type User = Actor & { accounts: Array; /** Access Tokens belonging to this actor */ accessTokens: Array; - /** Snacks associated with this account */ - snacks: Array; /** Apps this user has published */ apps: Array; /** Whether this user has any pending user invitations. Only resolves for the viewer. */ @@ -796,44 +334,8 @@ export type User = Actor & { likes?: Maybe>>; }; - -/** Represents a human (not robot) actor. */ -export type UserSnacksArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; -}; - - -/** Represents a human (not robot) actor. */ -export type UserAppsArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; - includeUnpublished?: Maybe; -}; - - -/** Represents a human (not robot) actor. */ -export type UserActivityTimelineProjectActivitiesArgs = { - limit: Scalars['Int']; - createdBefore?: Maybe; - filterTypes?: Maybe>; -}; - - -/** Represents a human (not robot) actor. */ -export type UserFeatureGatesArgs = { - filter?: Maybe>; -}; - - -/** Represents a human (not robot) actor. */ -export type UserLikesArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; -}; - /** A second factor device belonging to a User */ -export type UserSecondFactorDevice = { +type UserSecondFactorDevice = { __typename?: 'UserSecondFactorDevice'; id: Scalars['ID']; user: User; @@ -846,15 +348,15 @@ export type UserSecondFactorDevice = { updatedAt: Scalars['DateTime']; }; -export enum SecondFactorMethod { +enum SecondFactorMethod { /** Google Authenticator (TOTP) */ Authenticator = 'AUTHENTICATOR', /** SMS */ - Sms = 'SMS' + Sms = 'SMS', } /** A method of authentication for an Actor */ -export type AccessToken = { +type AccessToken = { __typename?: 'AccessToken'; id: Scalars['ID']; visibleTokenPrefix: Scalars['String']; @@ -867,7 +369,7 @@ export type AccessToken = { }; /** An pending invitation sent to an email granting membership on an Account. */ -export type UserInvitation = { +type UserInvitation = { __typename?: 'UserInvitation'; id: Scalars['ID']; /** Email to which this invitation was sent */ @@ -880,149 +382,47 @@ export type UserInvitation = { role: Role; }; -export enum Permission { +enum Permission { Own = 'OWN', Admin = 'ADMIN', Publish = 'PUBLISH', - View = 'VIEW' + View = 'VIEW', } -export enum Role { +enum Role { Owner = 'OWNER', Admin = 'ADMIN', Developer = 'DEVELOPER', ViewOnly = 'VIEW_ONLY', Custom = 'CUSTOM', HasAdmin = 'HAS_ADMIN', - NotAdmin = 'NOT_ADMIN' -} - -export enum ActivityTimelineProjectActivityType { - BuildJob = 'BUILD_JOB', - Build = 'BUILD', - Update = 'UPDATE', - Submission = 'SUBMISSION' -} - - -export type BuildArtifacts = { - __typename?: 'BuildArtifacts'; - buildUrl?: Maybe; - xcodeBuildLogsUrl?: Maybe; -}; - -export type BuildMetrics = { - __typename?: 'BuildMetrics'; - buildWaitTime?: Maybe; - buildQueueTime?: Maybe; - buildDuration?: Maybe; -}; - -export enum BuildIosEnterpriseProvisioning { - Adhoc = 'ADHOC', - Universal = 'UNIVERSAL' + NotAdmin = 'NOT_ADMIN', } -export type BuildError = { - __typename?: 'BuildError'; - errorCode: Scalars['String']; - message: Scalars['String']; - docsUrl?: Maybe; -}; - -/** Represents an EAS Submission */ -export type Submission = ActivityTimelineProjectActivity & { - __typename?: 'Submission'; - id: Scalars['ID']; - actor?: Maybe; - activityTimestamp: Scalars['DateTime']; - app: App; - initiatingActor?: Maybe; - submittedBuild?: Maybe; - platform: AppPlatform; - status: SubmissionStatus; - androidConfig?: Maybe; - iosConfig?: Maybe; - logsUrl?: Maybe; - error?: Maybe; - createdAt: Scalars['DateTime']; - updatedAt: Scalars['DateTime']; -}; - -export enum SubmissionStatus { - AwaitingBuild = 'AWAITING_BUILD', - InQueue = 'IN_QUEUE', - InProgress = 'IN_PROGRESS', - Finished = 'FINISHED', - Errored = 'ERRORED', - Canceled = 'CANCELED' -} - -export type AndroidSubmissionConfig = { - __typename?: 'AndroidSubmissionConfig'; - /** @deprecated applicationIdentifier is deprecated and will be auto-detected on submit */ - applicationIdentifier?: Maybe; - /** @deprecated archiveType is deprecated and will be null */ - archiveType?: Maybe; - track: SubmissionAndroidTrack; - releaseStatus?: Maybe; -}; - -export enum SubmissionAndroidArchiveType { - Apk = 'APK', - Aab = 'AAB' -} - -export enum SubmissionAndroidTrack { - Production = 'PRODUCTION', - Internal = 'INTERNAL', - Alpha = 'ALPHA', - Beta = 'BETA' -} - -export enum SubmissionAndroidReleaseStatus { - Draft = 'DRAFT', - InProgress = 'IN_PROGRESS', - Halted = 'HALTED', - Completed = 'COMPLETED' -} - -export type IosSubmissionConfig = { - __typename?: 'IosSubmissionConfig'; - ascAppIdentifier: Scalars['String']; - appleIdUsername?: Maybe; - ascApiKeyId?: Maybe; -}; - -export type SubmissionError = { - __typename?: 'SubmissionError'; - errorCode?: Maybe; - message?: Maybe; -}; - /** Represents an Standalone App build job */ -export type BuildJob = ActivityTimelineProjectActivity & BuildOrBuildJob & { - __typename?: 'BuildJob'; - id: Scalars['ID']; - actor?: Maybe; - activityTimestamp: Scalars['DateTime']; - app?: Maybe; - user?: Maybe; - release?: Maybe; - config?: Maybe; - artifacts?: Maybe; - logs?: Maybe; - created?: Maybe; - updated?: Maybe; - fullExperienceName?: Maybe; - status?: Maybe; - expirationDate?: Maybe; - platform: AppPlatform; - sdkVersion?: Maybe; - releaseChannel?: Maybe; -}; - -export type AppRelease = { +type BuildJob = ActivityTimelineProjectActivity & + BuildOrBuildJob & { + __typename?: 'BuildJob'; + id: Scalars['ID']; + actor?: Maybe; + activityTimestamp: Scalars['DateTime']; + app?: Maybe; + user?: Maybe; + release?: Maybe; + config?: Maybe; + artifacts?: Maybe; + logs?: Maybe; + created?: Maybe; + updated?: Maybe; + fullExperienceName?: Maybe; + status?: Maybe; + expirationDate?: Maybe; + platform: AppPlatform; + sdkVersion?: Maybe; + releaseChannel?: Maybe; + }; + +type AppRelease = { __typename?: 'AppRelease'; id: Scalars['ID']; hash: Scalars['String']; @@ -1036,75 +436,33 @@ export type AppRelease = { manifest: Scalars['JSON']; }; -export type BuildArtifact = { +type BuildArtifact = { __typename?: 'BuildArtifact'; url: Scalars['String']; manifestPlistUrl?: Maybe; }; -export type BuildLogs = { +type BuildLogs = { __typename?: 'BuildLogs'; url?: Maybe; format?: Maybe; }; -export enum BuildJobLogsFormat { +enum BuildJobLogsFormat { Raw = 'RAW', - Json = 'JSON' + Json = 'JSON', } -export enum BuildJobStatus { +enum BuildJobStatus { Pending = 'PENDING', Started = 'STARTED', InProgress = 'IN_PROGRESS', Errored = 'ERRORED', Finished = 'FINISHED', - SentToQueue = 'SENT_TO_QUEUE' + SentToQueue = 'SENT_TO_QUEUE', } -export type SubmissionFilter = { - platform?: Maybe; - status?: Maybe; -}; - -export type DeploymentOptions = { - /** Max number of associated builds to return */ - buildListMaxSize?: Maybe; -}; - -/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */ -export type Deployment = { - __typename?: 'Deployment'; - id: Scalars['ID']; - runtimeVersion: Scalars['String']; - /** - * The name of this deployment's associated channel. It is specified separately from the `channel` - * field to allow specifying a deployment before an EAS Update channel has been created. - */ - channelName: Scalars['String']; - channel?: Maybe; - recentBuilds: Array; - mostRecentlyUpdatedAt: Scalars['DateTime']; -}; - -export type UpdateChannel = { - __typename?: 'UpdateChannel'; - id: Scalars['ID']; - appId: Scalars['ID']; - name: Scalars['String']; - branchMapping: Scalars['String']; - createdAt: Scalars['DateTime']; - updatedAt: Scalars['DateTime']; - updateBranches: Array; -}; - - -export type UpdateChannelUpdateBranchesArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; -}; - -export type UpdateBranch = { +type UpdateBranch = { __typename?: 'UpdateBranch'; id: Scalars['ID']; appId: Scalars['ID']; @@ -1114,23 +472,7 @@ export type UpdateBranch = { updates: Array; }; - -export type UpdateBranchUpdatesArgs = { - offset: Scalars['Int']; - limit: Scalars['Int']; - filter?: Maybe; -}; - -export type UpdatesFilter = { - platform?: Maybe; - runtimeVersions?: Maybe>; -}; - -export type IosAppCredentialsFilter = { - appleAppIdentifierId?: Maybe; -}; - -export type IosAppCredentials = { +type IosAppCredentials = { __typename?: 'IosAppCredentials'; id: Scalars['ID']; app: App; @@ -1143,17 +485,7 @@ export type IosAppCredentials = { iosAppBuildCredentialsArray: Array; }; - -export type IosAppCredentialsIosAppBuildCredentialsListArgs = { - filter?: Maybe; -}; - - -export type IosAppCredentialsIosAppBuildCredentialsArrayArgs = { - filter?: Maybe; -}; - -export type AppleTeam = { +type AppleTeam = { __typename?: 'AppleTeam'; id: Scalars['ID']; account: Account; @@ -1166,17 +498,7 @@ export type AppleTeam = { appleDevices: Array; }; - -export type AppleTeamAppleAppIdentifiersArgs = { - bundleIdentifier?: Maybe; -}; - - -export type AppleTeamAppleProvisioningProfilesArgs = { - appleAppIdentifierId?: Maybe; -}; - -export type AppleAppIdentifier = { +type AppleAppIdentifier = { __typename?: 'AppleAppIdentifier'; id: Scalars['ID']; account: Account; @@ -1185,7 +507,7 @@ export type AppleAppIdentifier = { parentAppleAppIdentifier?: Maybe; }; -export type AppleDistributionCertificate = { +type AppleDistributionCertificate = { __typename?: 'AppleDistributionCertificate'; id: Scalars['ID']; account: Account; @@ -1202,7 +524,7 @@ export type AppleDistributionCertificate = { iosAppBuildCredentialsList: Array; }; -export type IosAppBuildCredentials = { +type IosAppBuildCredentials = { __typename?: 'IosAppBuildCredentials'; id: Scalars['ID']; distributionCertificate?: Maybe; @@ -1213,7 +535,7 @@ export type IosAppBuildCredentials = { appleDevices?: Maybe>>; }; -export type AppleProvisioningProfile = { +type AppleProvisioningProfile = { __typename?: 'AppleProvisioningProfile'; id: Scalars['ID']; account: Account; @@ -1229,7 +551,7 @@ export type AppleProvisioningProfile = { updatedAt: Scalars['DateTime']; }; -export type AppleDevice = { +type AppleDevice = { __typename?: 'AppleDevice'; id: Scalars['ID']; account: Account; @@ -1242,19 +564,19 @@ export type AppleDevice = { enabled?: Maybe; }; -export enum AppleDeviceClass { +enum AppleDeviceClass { Ipad = 'IPAD', - Iphone = 'IPHONE' + Iphone = 'IPHONE', } -export enum IosDistributionType { +enum IosDistributionType { AppStore = 'APP_STORE', Enterprise = 'ENTERPRISE', AdHoc = 'AD_HOC', - Development = 'DEVELOPMENT' + Development = 'DEVELOPMENT', } -export type ApplePushKey = { +type ApplePushKey = { __typename?: 'ApplePushKey'; id: Scalars['ID']; account: Account; @@ -1266,11 +588,7 @@ export type ApplePushKey = { iosAppCredentialsList: Array; }; -export type IosAppBuildCredentialsFilter = { - iosDistributionType?: Maybe; -}; - -export type AppStoreConnectApiKey = { +type AppStoreConnectApiKey = { __typename?: 'AppStoreConnectApiKey'; id: Scalars['ID']; account: Account; @@ -1283,7 +601,7 @@ export type AppStoreConnectApiKey = { updatedAt: Scalars['DateTime']; }; -export enum AppStoreConnectUserRole { +enum AppStoreConnectUserRole { Admin = 'ADMIN', Finance = 'FINANCE', Technical = 'TECHNICAL', @@ -1299,15 +617,10 @@ export enum AppStoreConnectUserRole { CloudManagedDeveloperId = 'CLOUD_MANAGED_DEVELOPER_ID', CloudManagedAppDistribution = 'CLOUD_MANAGED_APP_DISTRIBUTION', ImageManager = 'IMAGE_MANAGER', - Unknown = 'UNKNOWN' + Unknown = 'UNKNOWN', } -export type AndroidAppCredentialsFilter = { - legacyOnly?: Maybe; - applicationIdentifier?: Maybe; -}; - -export type AndroidAppCredentials = { +type AndroidAppCredentials = { __typename?: 'AndroidAppCredentials'; id: Scalars['ID']; app: App; @@ -1320,7 +633,7 @@ export type AndroidAppCredentials = { androidAppBuildCredentialsArray: Array; }; -export type AndroidFcm = { +type AndroidFcm = { __typename?: 'AndroidFcm'; id: Scalars['ID']; account: Account; @@ -1335,15 +648,15 @@ export type AndroidFcm = { updatedAt: Scalars['DateTime']; }; -export type FcmSnippet = FcmSnippetLegacy | FcmSnippetV1; +type FcmSnippet = FcmSnippetLegacy | FcmSnippetV1; -export type FcmSnippetLegacy = { +type FcmSnippetLegacy = { __typename?: 'FcmSnippetLegacy'; firstFourCharacters: Scalars['String']; lastFourCharacters: Scalars['String']; }; -export type FcmSnippetV1 = { +type FcmSnippetV1 = { __typename?: 'FcmSnippetV1'; projectId: Scalars['String']; keyId: Scalars['String']; @@ -1351,12 +664,12 @@ export type FcmSnippetV1 = { clientId?: Maybe; }; -export enum AndroidFcmVersion { +enum AndroidFcmVersion { Legacy = 'LEGACY', - V1 = 'V1' + V1 = 'V1', } -export type GoogleServiceAccountKey = { +type GoogleServiceAccountKey = { __typename?: 'GoogleServiceAccountKey'; id: Scalars['ID']; account: Account; @@ -1368,7 +681,7 @@ export type GoogleServiceAccountKey = { updatedAt: Scalars['DateTime']; }; -export type AndroidAppBuildCredentials = { +type AndroidAppBuildCredentials = { __typename?: 'AndroidAppBuildCredentials'; id: Scalars['ID']; name: Scalars['String']; @@ -1377,7 +690,7 @@ export type AndroidAppBuildCredentials = { isLegacy: Scalars['Boolean']; }; -export type AndroidKeystore = { +type AndroidKeystore = { __typename?: 'AndroidKeystore'; id: Scalars['ID']; account: Account; @@ -1393,13 +706,13 @@ export type AndroidKeystore = { updatedAt: Scalars['DateTime']; }; -export enum AndroidKeystoreType { +enum AndroidKeystoreType { Jks = 'JKS', Pkcs12 = 'PKCS12', - Unknown = 'UNKNOWN' + Unknown = 'UNKNOWN', } -export type EnvironmentSecret = { +type EnvironmentSecret = { __typename?: 'EnvironmentSecret'; id: Scalars['ID']; name: Scalars['String']; @@ -1407,16 +720,12 @@ export type EnvironmentSecret = { updatedAt: Scalars['DateTime']; }; -export type WebhookFilter = { - event?: Maybe; -}; - -export enum WebhookType { +enum WebhookType { Build = 'BUILD', - Submit = 'SUBMIT' + Submit = 'SUBMIT', } -export type Webhook = { +type Webhook = { __typename?: 'Webhook'; id: Scalars['ID']; appId: Scalars['ID']; @@ -1426,7 +735,7 @@ export type Webhook = { updatedAt: Scalars['DateTime']; }; -export type UserPermission = { +type UserPermission = { __typename?: 'UserPermission'; permissions: Array; role?: Maybe; @@ -1435,4319 +744,32 @@ export type UserPermission = { actor: Actor; }; -export type Billing = { - __typename?: 'Billing'; - id: Scalars['ID']; - payment?: Maybe; - subscription?: Maybe; - /** History of invoices */ - charges?: Maybe>>; -}; - -export type PaymentDetails = { - __typename?: 'PaymentDetails'; - id: Scalars['ID']; - card?: Maybe; - address?: Maybe
; -}; - -export type Card = { - __typename?: 'Card'; - cardHolder?: Maybe; - brand?: Maybe; - last4?: Maybe; - expYear?: Maybe; - expMonth?: Maybe; -}; - -export type Address = { - __typename?: 'Address'; - line1?: Maybe; - city?: Maybe; - state?: Maybe; - zip?: Maybe; - country?: Maybe; -}; - -export type SubscriptionDetails = { - __typename?: 'SubscriptionDetails'; - id: Scalars['ID']; - planId?: Maybe; - addons: Array; - name?: Maybe; - price: Scalars['Int']; - nextInvoice?: Maybe; - cancelledAt?: Maybe; - willCancel?: Maybe; - endedAt?: Maybe; - trialEnd?: Maybe; - status?: Maybe; - isDowngrading?: Maybe; -}; - -export type AddonDetails = { - __typename?: 'AddonDetails'; - id: Scalars['ID']; - planId: Scalars['String']; - name: Scalars['String']; - nextInvoice?: Maybe; - willCancel?: Maybe; -}; - -export type Charge = { - __typename?: 'Charge'; - id: Scalars['ID']; - paid?: Maybe; - invoiceId?: Maybe; - createdAt?: Maybe; - amount?: Maybe; - wasRefunded?: Maybe; - receiptUrl?: Maybe; -}; - -export type AccountQuery = { - __typename?: 'AccountQuery'; - /** Query an Account by ID */ - byId: Account; - /** Query an Account by name */ - byName: Account; -}; - - -export type AccountQueryByIdArgs = { - accountId: Scalars['String']; -}; - - -export type AccountQueryByNameArgs = { - accountName: Scalars['String']; -}; - -export type ActorQuery = { - __typename?: 'ActorQuery'; - /** Query an Actor by ID */ - byId: Actor; -}; - - -export type ActorQueryByIdArgs = { - id: Scalars['ID']; -}; - -export type AppleDeviceRegistrationRequestQuery = { - __typename?: 'AppleDeviceRegistrationRequestQuery'; - byId: AppleDeviceRegistrationRequest; -}; - - -export type AppleDeviceRegistrationRequestQueryByIdArgs = { - id: Scalars['ID']; -}; - -export type AppleDeviceRegistrationRequest = { - __typename?: 'AppleDeviceRegistrationRequest'; - id: Scalars['ID']; - account: Account; - appleTeam: AppleTeam; -}; - -export type AppleTeamQuery = { - __typename?: 'AppleTeamQuery'; - byAppleTeamIdentifier?: Maybe; -}; - - -export type AppleTeamQueryByAppleTeamIdentifierArgs = { - accountId: Scalars['ID']; - identifier: Scalars['String']; -}; - -export type AppQuery = { - __typename?: 'AppQuery'; - /** Look up app by app id */ - byId: App; - byFullName: App; - /** - * Public apps in the app directory - * @deprecated App directory no longer supported - */ - all: Array; -}; - - -export type AppQueryByIdArgs = { - appId: Scalars['String']; -}; - - -export type AppQueryByFullNameArgs = { - fullName: Scalars['String']; -}; - - -export type AppQueryAllArgs = { - filter: AppsFilter; - sort: AppSort; - offset?: Maybe; - limit?: Maybe; -}; - -export enum AppsFilter { - /** Featured Projects */ - Featured = 'FEATURED', - /** New Projects */ - New = 'NEW' -} - -export enum AppSort { - /** Sort by recently published */ - RecentlyPublished = 'RECENTLY_PUBLISHED', - /** Sort by highest trendScore */ - Viewed = 'VIEWED' -} - -/** Check to see if assets with given storageKeys exist */ -export type AssetQuery = { - __typename?: 'AssetQuery'; - metadata: Array; -}; - - -/** Check to see if assets with given storageKeys exist */ -export type AssetQueryMetadataArgs = { - storageKeys: Array; -}; - -export type AssetMetadataResult = { - __typename?: 'AssetMetadataResult'; - status: AssetMetadataStatus; - storageKey: Scalars['String']; -}; - -export enum AssetMetadataStatus { - Exists = 'EXISTS', - DoesNotExist = 'DOES_NOT_EXIST' -} - -export type BuildPublicDataQuery = { - __typename?: 'BuildPublicDataQuery'; - /** Get BuildPublicData by ID */ - byId?: Maybe; -}; - - -export type BuildPublicDataQueryByIdArgs = { - id: Scalars['ID']; -}; - -/** Publicly visible data for a Build. */ -export type BuildPublicData = { - __typename?: 'BuildPublicData'; - id: Scalars['ID']; - status: BuildStatus; - artifacts: PublicArtifacts; - project: ProjectPublicData; - platform: AppPlatform; - distribution?: Maybe; -}; - -export type PublicArtifacts = { - __typename?: 'PublicArtifacts'; - buildUrl?: Maybe; -}; - -export type ProjectPublicData = { - __typename?: 'ProjectPublicData'; +/** Represents a robot (not human) actor. */ +type Robot = Actor & { + __typename?: 'Robot'; id: Scalars['ID']; - fullName: Scalars['String']; -}; - -export type BuildJobQuery = { - __typename?: 'BuildJobQuery'; - /** - * get all build jobs by an optional filter - * @deprecated Prefer Account.buildJobs or App.buildJobs - */ - all: Array>; - byId: BuildJob; -}; - - -export type BuildJobQueryAllArgs = { - status?: Maybe; - username?: Maybe; - experienceSlug?: Maybe; - offset?: Maybe; - limit?: Maybe; - showAdminView?: Maybe; -}; - - -export type BuildJobQueryByIdArgs = { - buildId: Scalars['ID']; -}; - -export type BuildQuery = { - __typename?: 'BuildQuery'; - /** Look up EAS Build by build ID */ - byId: Build; - /** - * Get all builds for a specific app. - * They are sorted from latest to oldest. - * @deprecated Use App.builds instead - */ - allForApp: Array>; + firstName?: Maybe; + created: Scalars['DateTime']; + isExpoAdmin: Scalars['Boolean']; + displayName: Scalars['String']; + /** Associated accounts */ + accounts: Array; + /** Access Tokens belonging to this actor */ + accessTokens: Array; /** - * Get all builds. - * By default, they are sorted from latest to oldest. - * Available only for admin users. + * Server feature gate values for this actor, optionally filtering by desired gates. + * Only resolves for the viewer. */ - all: Array; -}; - - -export type BuildQueryByIdArgs = { - buildId: Scalars['ID']; -}; - - -export type BuildQueryAllForAppArgs = { - appId: Scalars['String']; - status?: Maybe; - platform?: Maybe; - offset?: Maybe; - limit?: Maybe; -}; - - -export type BuildQueryAllArgs = { - statuses?: Maybe>; - offset?: Maybe; - limit?: Maybe; - order?: Maybe; -}; - -export enum Order { - Desc = 'DESC', - Asc = 'ASC' -} - -export type ClientBuildQuery = { - __typename?: 'ClientBuildQuery'; - byId: ClientBuild; -}; - - -export type ClientBuildQueryByIdArgs = { - requestId: Scalars['ID']; -}; - -/** Represents a client build request */ -export type ClientBuild = { - __typename?: 'ClientBuild'; - id: Scalars['ID']; - status?: Maybe; - userFacingErrorMessage?: Maybe; - buildJobId?: Maybe; - manifestPlistUrl?: Maybe; - userId?: Maybe; -}; - -export type ExperimentationQuery = { - __typename?: 'ExperimentationQuery'; - /** Get user experimentation config */ - userConfig: Scalars['JSONObject']; - /** Get device experimentation config */ - deviceConfig: Scalars['JSONObject']; - /** Get experimentation unit to use for device experiments. In this case, it is the IP address. */ - deviceExperimentationUnit: Scalars['ID']; -}; - -export type InvoiceQuery = { - __typename?: 'InvoiceQuery'; - /** Preview an upgrade subscription invoice, with proration */ - previewInvoiceForSubscriptionUpdate: Invoice; -}; - - -export type InvoiceQueryPreviewInvoiceForSubscriptionUpdateArgs = { - accountId: Scalars['String']; - newPlanIdentifier: Scalars['String']; - couponCode?: Maybe; -}; - -export type Invoice = { - __typename?: 'Invoice'; - id: Scalars['ID']; - /** The total amount due for the invoice, in cents */ - amountDue: Scalars['Int']; - /** The total amount that has been paid, considering any discounts or account credit. Value is in cents. */ - amountPaid: Scalars['Int']; - /** The total amount that needs to be paid, considering any discounts or account credit. Value is in cents. */ - amountRemaining: Scalars['Int']; - discount?: Maybe; - totalDiscountedAmount: Scalars['Int']; - lineItems: Array; - period: InvoicePeriod; - startingBalance: Scalars['Int']; - subtotal: Scalars['Int']; - total: Scalars['Int']; -}; - -export type InvoiceDiscount = { - __typename?: 'InvoiceDiscount'; - id: Scalars['ID']; - name: Scalars['String']; - type: InvoiceDiscountType; - duration: Scalars['String']; - durationInMonths?: Maybe; - /** The coupon's discount value, in percentage or in dollar amount */ - amount: Scalars['Int']; -}; - -export enum InvoiceDiscountType { - Percentage = 'PERCENTAGE', - Amount = 'AMOUNT' -} - -export type InvoiceLineItem = { - __typename?: 'InvoiceLineItem'; - id: Scalars['ID']; - description: Scalars['String']; - /** Line-item amount in cents */ - amount: Scalars['Int']; - period: InvoicePeriod; - proration: Scalars['Boolean']; - quantity: Scalars['Int']; -}; - -export type InvoicePeriod = { - __typename?: 'InvoicePeriod'; - start: Scalars['DateTime']; - end: Scalars['DateTime']; -}; - -export type ProjectQuery = { - __typename?: 'ProjectQuery'; - byAccountNameAndSlug: Project; - /** @deprecated See byAccountNameAndSlug */ - byUsernameAndSlug: Project; - /** @deprecated Field no longer supported */ - byPaths: Array>; -}; - - -export type ProjectQueryByAccountNameAndSlugArgs = { - accountName: Scalars['String']; - slug: Scalars['String']; - platform?: Maybe; - sdkVersions?: Maybe>>; -}; - - -export type ProjectQueryByUsernameAndSlugArgs = { - username: Scalars['String']; - slug: Scalars['String']; - platform?: Maybe; - sdkVersions?: Maybe>>; + featureGates: Scalars['JSONObject']; }; - -export type ProjectQueryByPathsArgs = { - paths?: Maybe>>; +export type CurrentUserQuery = { __typename?: 'RootQuery' } & { + meActor?: Maybe< + | ({ __typename: 'User' } & Pick & { + accounts: Array<{ __typename?: 'Account' } & Pick>; + }) + | ({ __typename: 'Robot' } & Pick & { + accounts: Array<{ __typename?: 'Account' } & Pick>; + }) + >; }; - -export type SnackQuery = { - __typename?: 'SnackQuery'; - /** - * Get snack by hashId - * @deprecated Use byHashId - */ - byId: Snack; - /** Get snack by hashId */ - byHashId: Snack; -}; - - -export type SnackQueryByIdArgs = { - id: Scalars['ID']; -}; - - -export type SnackQueryByHashIdArgs = { - hashId: Scalars['ID']; -}; - -export type SubmissionQuery = { - __typename?: 'SubmissionQuery'; - /** Look up EAS Submission by submission ID */ - byId: Submission; -}; - - -export type SubmissionQueryByIdArgs = { - submissionId: Scalars['ID']; -}; - -export type UserInvitationPublicDataQuery = { - __typename?: 'UserInvitationPublicDataQuery'; - /** Get UserInvitationPublicData by token */ - byToken: UserInvitationPublicData; -}; - - -export type UserInvitationPublicDataQueryByTokenArgs = { - token: Scalars['ID']; -}; - -/** Publicly visible data for a UserInvitation. */ -export type UserInvitationPublicData = { - __typename?: 'UserInvitationPublicData'; - /** Email to which this invitation was sent */ - id: Scalars['ID']; - email: Scalars['String']; - created: Scalars['DateTime']; - accountName: Scalars['String']; -}; - -export type UserQuery = { - __typename?: 'UserQuery'; - /** Query a User by ID */ - byId: User; - /** Query a User by username */ - byUsername: User; -}; - - -export type UserQueryByIdArgs = { - userId: Scalars['String']; -}; - - -export type UserQueryByUsernameArgs = { - username: Scalars['String']; -}; - -export type WebhookQuery = { - __typename?: 'WebhookQuery'; - byId: Webhook; -}; - - -export type WebhookQueryByIdArgs = { - id: Scalars['ID']; -}; - -export type RootMutation = { - __typename?: 'RootMutation'; - /** - * This is a placeholder field - * @deprecated Not used. - */ - _doNotUse?: Maybe; - /** Mutations that create, read, update, and delete AccessTokens for Actors */ - accessToken: AccessTokenMutation; - /** Mutations that modify an Account */ - account?: Maybe; - /** Mutations that modify the build credentials for an Android app */ - androidAppBuildCredentials: AndroidAppBuildCredentialsMutation; - /** Mutations that modify the credentials for an Android app */ - androidAppCredentials: AndroidAppCredentialsMutation; - /** Mutations that modify an FCM credential */ - androidFcm: AndroidFcmMutation; - /** Mutations that modify a Keystore */ - androidKeystore: AndroidKeystoreMutation; - /** Mutations that modify a Google Service Account Key */ - googleServiceAccountKey: GoogleServiceAccountKeyMutation; - /** Mutations that modify an Identifier for an iOS App */ - appleAppIdentifier: AppleAppIdentifierMutation; - /** Mutations that modify an Apple Device */ - appleDevice: AppleDeviceMutation; - /** Mutations that modify an Apple Device registration request */ - appleDeviceRegistrationRequest: AppleDeviceRegistrationRequestMutation; - /** Mutations that modify a Distribution Certificate */ - appleDistributionCertificate: AppleDistributionCertificateMutation; - /** Mutations that modify a Provisioning Profile */ - appleProvisioningProfile: AppleProvisioningProfileMutation; - /** Mutations that modify an Apple Push Notification key */ - applePushKey: ApplePushKeyMutation; - /** Mutations that modify an Apple Team */ - appleTeam: AppleTeamMutation; - /** Mutations that modify an App Store Connect Api Key */ - appStoreConnectApiKey: AppStoreConnectApiKeyMutation; - /** Mutations that modify an App */ - app?: Maybe; - asset: AssetMutation; - /** Mutations that modify an BuildJob */ - buildJob?: Maybe; - /** Mutations that modify an EAS Build */ - build?: Maybe; - /** Mutations that modify the build credentials for an iOS app */ - iosAppBuildCredentials: IosAppBuildCredentialsMutation; - /** Mutations that modify the credentials for an iOS app */ - iosAppCredentials: IosAppCredentialsMutation; - keystoreGenerationUrl: KeystoreGenerationUrlMutation; - /** Mutations that create, update, and delete Robots */ - robot: RobotMutation; - /** Mutations that modify an EAS Submit submission */ - submission: SubmissionMutation; - updateChannel: UpdateChannelMutation; - update: UpdateMutation; - updateBranch: UpdateBranchMutation; - uploadSession: UploadSession; - /** Mutations that create, delete, and accept UserInvitations */ - userInvitation: UserInvitationMutation; - /** Mutations that modify the currently authenticated User */ - me?: Maybe; - /** Mutations that modify an EmailSubscription */ - emailSubscription: EmailSubscriptionMutation; - /** Mutations that create and delete EnvironmentSecrets */ - environmentSecret: EnvironmentSecretMutation; - /** Mutations that create, delete, update Webhooks */ - webhook: WebhookMutation; -}; - - -export type RootMutationAccountArgs = { - accountName: Scalars['ID']; -}; - - -export type RootMutationAppArgs = { - appId?: Maybe; -}; - - -export type RootMutationBuildJobArgs = { - buildId: Scalars['ID']; -}; - - -export type RootMutationBuildArgs = { - buildId?: Maybe; -}; - -export type AccessTokenMutation = { - __typename?: 'AccessTokenMutation'; - /** Create an AccessToken for an Actor */ - createAccessToken: CreateAccessTokenResponse; - /** Revoke an AccessToken */ - setAccessTokenRevoked: AccessToken; - /** Delete an AccessToken */ - deleteAccessToken: DeleteAccessTokenResult; -}; - - -export type AccessTokenMutationCreateAccessTokenArgs = { - createAccessTokenData: CreateAccessTokenInput; -}; - - -export type AccessTokenMutationSetAccessTokenRevokedArgs = { - id: Scalars['ID']; - revoked?: Maybe; -}; - - -export type AccessTokenMutationDeleteAccessTokenArgs = { - id: Scalars['ID']; -}; - -export type CreateAccessTokenInput = { - actorID: Scalars['ID']; - note?: Maybe; -}; - -export type CreateAccessTokenResponse = { - __typename?: 'CreateAccessTokenResponse'; - /** AccessToken created */ - accessToken?: Maybe; - /** Full token string to be used for authentication */ - token?: Maybe; -}; - -export type DeleteAccessTokenResult = { - __typename?: 'DeleteAccessTokenResult'; - id: Scalars['ID']; -}; - -export type AccountMutation = { - __typename?: 'AccountMutation'; - /** Add specified account Permissions for Actor. Actor must already have at least one permission on the account. */ - grantActorPermissions?: Maybe; - /** Revoke specified Permissions for Actor. Actor must already have at least one permission on the account. */ - revokeActorPermissions?: Maybe; - /** Add a subscription */ - subscribeToProduct?: Maybe; - /** Cancels the active subscription */ - cancelSubscription?: Maybe; - /** Upgrades or downgrades the active subscription to the newPlanIdentifier, which must be one of the EAS plans (i.e., Production or Enterprise). */ - changePlan: Account; - /** Cancel scheduled subscription change */ - cancelScheduledSubscriptionChange: Account; - /** Requests a refund for the specified charge. Returns true if auto-refund was possible, otherwise requests a manual refund from support and returns false. */ - requestRefund?: Maybe; - /** - * Makes a one time purchase - * @deprecated Build packs are no longer supported - */ - buyProduct?: Maybe; - /** - * Update setting to purchase new build packs when the current one is consumed - * @deprecated Build packs are no longer supported - */ - setBuildAutoRenew?: Maybe; - /** Set payment details */ - setPaymentSource?: Maybe; - /** Require authorization to send push notifications for experiences owned by this account */ - setPushSecurityEnabled?: Maybe; - /** Rename this account and the primary user's username if this account is a personal account */ - rename: Account; -}; - - -export type AccountMutationGrantActorPermissionsArgs = { - accountID: Scalars['ID']; - actorID: Scalars['ID']; - permissions?: Maybe>>; -}; - - -export type AccountMutationRevokeActorPermissionsArgs = { - accountID: Scalars['ID']; - actorID: Scalars['ID']; - permissions?: Maybe>>; -}; - - -export type AccountMutationSubscribeToProductArgs = { - accountName: Scalars['ID']; - productId: Scalars['ID']; - paymentSource: Scalars['ID']; -}; - - -export type AccountMutationCancelSubscriptionArgs = { - accountName: Scalars['ID']; -}; - - -export type AccountMutationChangePlanArgs = { - accountID: Scalars['ID']; - newPlanIdentifier: Scalars['String']; - couponCode?: Maybe; -}; - - -export type AccountMutationCancelScheduledSubscriptionChangeArgs = { - accountID: Scalars['ID']; -}; - - -export type AccountMutationRequestRefundArgs = { - accountID: Scalars['ID']; - chargeIdentifier: Scalars['ID']; -}; - - -export type AccountMutationBuyProductArgs = { - accountName: Scalars['ID']; - productId: Scalars['ID']; - paymentSource?: Maybe; - autoRenew?: Maybe; -}; - - -export type AccountMutationSetBuildAutoRenewArgs = { - accountName: Scalars['ID']; - autoRenew?: Maybe; -}; - - -export type AccountMutationSetPaymentSourceArgs = { - accountName: Scalars['ID']; - paymentSource: Scalars['ID']; -}; - - -export type AccountMutationSetPushSecurityEnabledArgs = { - accountID: Scalars['ID']; - pushSecurityEnabled: Scalars['Boolean']; -}; - - -export type AccountMutationRenameArgs = { - accountID: Scalars['ID']; - newName: Scalars['String']; -}; - -export type AndroidAppBuildCredentialsMutation = { - __typename?: 'AndroidAppBuildCredentialsMutation'; - /** Create a set of build credentials for an Android app */ - createAndroidAppBuildCredentials?: Maybe; - /** delete a set of build credentials for an Android app */ - deleteAndroidAppBuildCredentials?: Maybe; - /** Set the name of a set of build credentials to be used for an Android app */ - setName?: Maybe; - /** Set the keystore to be used for an Android app */ - setKeystore?: Maybe; - /** Set the build credentials to be the default for the Android app */ - setDefault?: Maybe; -}; - - -export type AndroidAppBuildCredentialsMutationCreateAndroidAppBuildCredentialsArgs = { - androidAppBuildCredentialsInput: AndroidAppBuildCredentialsInput; - androidAppCredentialsId: Scalars['ID']; -}; - - -export type AndroidAppBuildCredentialsMutationDeleteAndroidAppBuildCredentialsArgs = { - id: Scalars['ID']; -}; - - -export type AndroidAppBuildCredentialsMutationSetNameArgs = { - id: Scalars['ID']; - name: Scalars['String']; -}; - - -export type AndroidAppBuildCredentialsMutationSetKeystoreArgs = { - id: Scalars['ID']; - keystoreId: Scalars['ID']; -}; - - -export type AndroidAppBuildCredentialsMutationSetDefaultArgs = { - id: Scalars['ID']; - isDefault: Scalars['Boolean']; -}; - -/** @isDefault: if set, these build credentials will become the default for the Android app. All other build credentials will have their default status set to false. */ -export type AndroidAppBuildCredentialsInput = { - isDefault: Scalars['Boolean']; - name: Scalars['String']; - keystoreId: Scalars['ID']; -}; - -export type DeleteAndroidAppBuildCredentialsResult = { - __typename?: 'deleteAndroidAppBuildCredentialsResult'; - id: Scalars['ID']; -}; - -export type AndroidAppCredentialsMutation = { - __typename?: 'AndroidAppCredentialsMutation'; - /** Create a set of credentials for an Android app */ - createAndroidAppCredentials?: Maybe; - /** Set the FCM push key to be used in an Android app */ - setFcm?: Maybe; - /** Set the Google Service Account Key to be used for submitting an Android app */ - setGoogleServiceAccountKeyForSubmissions?: Maybe; -}; - - -export type AndroidAppCredentialsMutationCreateAndroidAppCredentialsArgs = { - androidAppCredentialsInput: AndroidAppCredentialsInput; - appId: Scalars['ID']; - applicationIdentifier: Scalars['String']; -}; - - -export type AndroidAppCredentialsMutationSetFcmArgs = { - id: Scalars['ID']; - fcmId: Scalars['ID']; -}; - - -export type AndroidAppCredentialsMutationSetGoogleServiceAccountKeyForSubmissionsArgs = { - id: Scalars['ID']; - googleServiceAccountKeyId: Scalars['ID']; -}; - -export type AndroidAppCredentialsInput = { - fcmId?: Maybe; - googleServiceAccountKeyForSubmissionsId?: Maybe; -}; - -export type AndroidFcmMutation = { - __typename?: 'AndroidFcmMutation'; - /** Create an FCM credential */ - createAndroidFcm: AndroidFcm; - /** Delete an FCM credential */ - deleteAndroidFcm: DeleteAndroidFcmResult; -}; - - -export type AndroidFcmMutationCreateAndroidFcmArgs = { - androidFcmInput: AndroidFcmInput; - accountId: Scalars['ID']; -}; - - -export type AndroidFcmMutationDeleteAndroidFcmArgs = { - id: Scalars['ID']; -}; - -export type AndroidFcmInput = { - credential: Scalars['String']; - version: AndroidFcmVersion; -}; - -export type DeleteAndroidFcmResult = { - __typename?: 'deleteAndroidFcmResult'; - id: Scalars['ID']; -}; - -export type AndroidKeystoreMutation = { - __typename?: 'AndroidKeystoreMutation'; - /** Create a Keystore */ - createAndroidKeystore?: Maybe; - /** Delete a Keystore */ - deleteAndroidKeystore: DeleteAndroidKeystoreResult; -}; - - -export type AndroidKeystoreMutationCreateAndroidKeystoreArgs = { - androidKeystoreInput: AndroidKeystoreInput; - accountId: Scalars['ID']; -}; - - -export type AndroidKeystoreMutationDeleteAndroidKeystoreArgs = { - id: Scalars['ID']; -}; - -export type AndroidKeystoreInput = { - base64EncodedKeystore: Scalars['String']; - keystorePassword: Scalars['String']; - keyAlias: Scalars['String']; - keyPassword?: Maybe; - type: AndroidKeystoreType; -}; - -export type DeleteAndroidKeystoreResult = { - __typename?: 'DeleteAndroidKeystoreResult'; - id: Scalars['ID']; -}; - -export type GoogleServiceAccountKeyMutation = { - __typename?: 'GoogleServiceAccountKeyMutation'; - /** Create a Google Service Account Key */ - createGoogleServiceAccountKey: GoogleServiceAccountKey; - /** Delete a Google Service Account Key */ - deleteGoogleServiceAccountKey: DeleteGoogleServiceAccountKeyResult; -}; - - -export type GoogleServiceAccountKeyMutationCreateGoogleServiceAccountKeyArgs = { - googleServiceAccountKeyInput: GoogleServiceAccountKeyInput; - accountId: Scalars['ID']; -}; - - -export type GoogleServiceAccountKeyMutationDeleteGoogleServiceAccountKeyArgs = { - id: Scalars['ID']; -}; - -export type GoogleServiceAccountKeyInput = { - jsonKey: Scalars['JSONObject']; -}; - -export type DeleteGoogleServiceAccountKeyResult = { - __typename?: 'DeleteGoogleServiceAccountKeyResult'; - id: Scalars['ID']; -}; - -export type AppleAppIdentifierMutation = { - __typename?: 'AppleAppIdentifierMutation'; - /** Create an Identifier for an iOS App */ - createAppleAppIdentifier?: Maybe; -}; - - -export type AppleAppIdentifierMutationCreateAppleAppIdentifierArgs = { - appleAppIdentifierInput: AppleAppIdentifierInput; - accountId: Scalars['ID']; -}; - -export type AppleAppIdentifierInput = { - bundleIdentifier: Scalars['String']; - appleTeamId?: Maybe; - parentAppleAppId?: Maybe; -}; - -export type AppleDeviceMutation = { - __typename?: 'AppleDeviceMutation'; - /** Create an Apple Device */ - createAppleDevice: AppleDevice; - /** Delete an Apple Device */ - deleteAppleDevice: DeleteAppleDeviceResult; -}; - - -export type AppleDeviceMutationCreateAppleDeviceArgs = { - appleDeviceInput: AppleDeviceInput; - accountId: Scalars['ID']; -}; - - -export type AppleDeviceMutationDeleteAppleDeviceArgs = { - id: Scalars['ID']; -}; - -export type AppleDeviceInput = { - appleTeamId: Scalars['ID']; - identifier: Scalars['String']; - name?: Maybe; - model?: Maybe; - deviceClass?: Maybe; - softwareVersion?: Maybe; - enabled?: Maybe; -}; - -export type DeleteAppleDeviceResult = { - __typename?: 'DeleteAppleDeviceResult'; - id: Scalars['ID']; -}; - -export type AppleDeviceRegistrationRequestMutation = { - __typename?: 'AppleDeviceRegistrationRequestMutation'; - /** Create an Apple Device registration request */ - createAppleDeviceRegistrationRequest: AppleDeviceRegistrationRequest; -}; - - -export type AppleDeviceRegistrationRequestMutationCreateAppleDeviceRegistrationRequestArgs = { - appleTeamId: Scalars['ID']; - accountId: Scalars['ID']; -}; - -export type AppleDistributionCertificateMutation = { - __typename?: 'AppleDistributionCertificateMutation'; - /** Create a Distribution Certificate */ - createAppleDistributionCertificate?: Maybe; - /** Delete a Distribution Certificate */ - deleteAppleDistributionCertificate: DeleteAppleDistributionCertificateResult; -}; - - -export type AppleDistributionCertificateMutationCreateAppleDistributionCertificateArgs = { - appleDistributionCertificateInput: AppleDistributionCertificateInput; - accountId: Scalars['ID']; -}; - - -export type AppleDistributionCertificateMutationDeleteAppleDistributionCertificateArgs = { - id: Scalars['ID']; -}; - -export type AppleDistributionCertificateInput = { - certP12: Scalars['String']; - certPassword: Scalars['String']; - certPrivateSigningKey?: Maybe; - developerPortalIdentifier?: Maybe; - appleTeamId?: Maybe; -}; - -export type DeleteAppleDistributionCertificateResult = { - __typename?: 'DeleteAppleDistributionCertificateResult'; - id: Scalars['ID']; -}; - -export type AppleProvisioningProfileMutation = { - __typename?: 'AppleProvisioningProfileMutation'; - /** Create a Provisioning Profile */ - createAppleProvisioningProfile: AppleProvisioningProfile; - /** Update a Provisioning Profile */ - updateAppleProvisioningProfile: AppleProvisioningProfile; - /** Delete a Provisioning Profile */ - deleteAppleProvisioningProfile: DeleteAppleProvisioningProfileResult; - /** Delete Provisioning Profiles */ - deleteAppleProvisioningProfiles: Array; -}; - - -export type AppleProvisioningProfileMutationCreateAppleProvisioningProfileArgs = { - appleProvisioningProfileInput: AppleProvisioningProfileInput; - accountId: Scalars['ID']; - appleAppIdentifierId: Scalars['ID']; -}; - - -export type AppleProvisioningProfileMutationUpdateAppleProvisioningProfileArgs = { - id: Scalars['ID']; - appleProvisioningProfileInput: AppleProvisioningProfileInput; -}; - - -export type AppleProvisioningProfileMutationDeleteAppleProvisioningProfileArgs = { - id: Scalars['ID']; -}; - - -export type AppleProvisioningProfileMutationDeleteAppleProvisioningProfilesArgs = { - ids: Array; -}; - -export type AppleProvisioningProfileInput = { - appleProvisioningProfile: Scalars['String']; - developerPortalIdentifier?: Maybe; -}; - -export type DeleteAppleProvisioningProfileResult = { - __typename?: 'DeleteAppleProvisioningProfileResult'; - id: Scalars['ID']; -}; - -export type ApplePushKeyMutation = { - __typename?: 'ApplePushKeyMutation'; - /** Create an Apple Push Notification key */ - createApplePushKey: ApplePushKey; - /** Delete an Apple Push Notification key */ - deleteApplePushKey: DeleteApplePushKeyResult; -}; - - -export type ApplePushKeyMutationCreateApplePushKeyArgs = { - applePushKeyInput: ApplePushKeyInput; - accountId: Scalars['ID']; -}; - - -export type ApplePushKeyMutationDeleteApplePushKeyArgs = { - id: Scalars['ID']; -}; - -export type ApplePushKeyInput = { - keyP8: Scalars['String']; - keyIdentifier: Scalars['String']; - appleTeamId?: Maybe; -}; - -export type DeleteApplePushKeyResult = { - __typename?: 'deleteApplePushKeyResult'; - id: Scalars['ID']; -}; - -export type AppleTeamMutation = { - __typename?: 'AppleTeamMutation'; - /** Create an Apple Team */ - createAppleTeam: AppleTeam; -}; - - -export type AppleTeamMutationCreateAppleTeamArgs = { - appleTeamInput: AppleTeamInput; - accountId: Scalars['ID']; -}; - -export type AppleTeamInput = { - appleTeamIdentifier: Scalars['String']; - appleTeamName?: Maybe; -}; - -export type AppStoreConnectApiKeyMutation = { - __typename?: 'AppStoreConnectApiKeyMutation'; - /** Create an App Store Connect Api Key for an Apple Team */ - createAppStoreConnectApiKey: AppStoreConnectApiKey; - /** Delete an App Store Connect Api Key */ - deleteAppStoreConnectApiKey: DeleteAppStoreConnectApiKeyResult; -}; - - -export type AppStoreConnectApiKeyMutationCreateAppStoreConnectApiKeyArgs = { - appStoreConnectApiKeyInput: AppStoreConnectApiKeyInput; - accountId: Scalars['ID']; -}; - - -export type AppStoreConnectApiKeyMutationDeleteAppStoreConnectApiKeyArgs = { - id: Scalars['ID']; -}; - -export type AppStoreConnectApiKeyInput = { - issuerIdentifier: Scalars['String']; - keyIdentifier: Scalars['String']; - keyP8: Scalars['String']; - name?: Maybe; - roles?: Maybe>; - appleTeamId?: Maybe; -}; - -export type DeleteAppStoreConnectApiKeyResult = { - __typename?: 'deleteAppStoreConnectApiKeyResult'; - id: Scalars['ID']; -}; - -export type AppMutation = { - __typename?: 'AppMutation'; - /** Create an unpublished app */ - createApp: App; - /** @deprecated Field no longer supported */ - grantAccess?: Maybe; - /** Require api token to send push notifs for experience */ - setPushSecurityEnabled?: Maybe; -}; - - -export type AppMutationCreateAppArgs = { - appInput: AppInput; -}; - - -export type AppMutationGrantAccessArgs = { - toUser: Scalars['ID']; - accessLevel?: Maybe; -}; - - -export type AppMutationSetPushSecurityEnabledArgs = { - appId: Scalars['ID']; - pushSecurityEnabled: Scalars['Boolean']; -}; - -export type AppInput = { - accountId: Scalars['ID']; - projectName: Scalars['String']; - privacy: AppPrivacy; -}; - -export type AssetMutation = { - __typename?: 'AssetMutation'; - /** - * Returns an array of specifications for upload. Each URL is valid for an hour. - * The content type of the asset you wish to upload must be specified. - */ - getSignedAssetUploadSpecifications?: Maybe; -}; - - -export type AssetMutationGetSignedAssetUploadSpecificationsArgs = { - assetContentTypes: Array>; -}; - -export type GetSignedAssetUploadSpecificationsResult = { - __typename?: 'GetSignedAssetUploadSpecificationsResult'; - specifications: Array>; -}; - -export type BuildJobMutation = { - __typename?: 'BuildJobMutation'; - cancel?: Maybe; - del?: Maybe; - restart?: Maybe; -}; - -export type BuildMutation = { - __typename?: 'BuildMutation'; - /** Cancel an EAS Build build */ - cancelBuild: Build; - /** Delete an EAS Build build */ - deleteBuild: Build; - /** - * Cancel an EAS Build build - * @deprecated Use cancelBuild instead - */ - cancel: Build; - /** Create an Android build */ - createAndroidBuild: CreateBuildResult; - /** Create an iOS build */ - createIosBuild: CreateBuildResult; -}; - - -export type BuildMutationCancelBuildArgs = { - buildId: Scalars['ID']; -}; - - -export type BuildMutationDeleteBuildArgs = { - buildId: Scalars['ID']; -}; - - -export type BuildMutationCreateAndroidBuildArgs = { - appId: Scalars['ID']; - job: AndroidJobInput; - metadata?: Maybe; -}; - - -export type BuildMutationCreateIosBuildArgs = { - appId: Scalars['ID']; - job: IosJobInput; - metadata?: Maybe; -}; - -export type AndroidJobInput = { - type: BuildWorkflow; - projectArchive: ProjectArchiveSourceInput; - projectRootDirectory: Scalars['String']; - releaseChannel?: Maybe; - updates?: Maybe; - developmentClient?: Maybe; - secrets?: Maybe; - builderEnvironment?: Maybe; - cache?: Maybe; - gradleCommand?: Maybe; - artifactPath?: Maybe; - buildType?: Maybe; - username?: Maybe; -}; - -export enum BuildWorkflow { - Generic = 'GENERIC', - Managed = 'MANAGED' -} - -export type ProjectArchiveSourceInput = { - type: ProjectArchiveSourceType; - bucketKey?: Maybe; - url?: Maybe; -}; - -export enum ProjectArchiveSourceType { - S3 = 'S3', - Url = 'URL' -} - -export type BuildUpdatesInput = { - channel?: Maybe; -}; - -export type AndroidJobSecretsInput = { - buildCredentials?: Maybe; - environmentSecrets?: Maybe; -}; - -export type AndroidJobBuildCredentialsInput = { - keystore: AndroidJobKeystoreInput; -}; - -export type AndroidJobKeystoreInput = { - dataBase64: Scalars['String']; - keystorePassword: Scalars['String']; - keyAlias: Scalars['String']; - keyPassword?: Maybe; -}; - -export type AndroidBuilderEnvironmentInput = { - image?: Maybe; - node?: Maybe; - yarn?: Maybe; - ndk?: Maybe; - expoCli?: Maybe; - env?: Maybe; -}; - -export type BuildCacheInput = { - disabled?: Maybe; - clear?: Maybe; - key?: Maybe; - cacheDefaultPaths?: Maybe; - customPaths?: Maybe>>; -}; - -export enum AndroidBuildType { - Apk = 'APK', - AppBundle = 'APP_BUNDLE', - /** @deprecated Use developmentClient option instead. */ - DevelopmentClient = 'DEVELOPMENT_CLIENT' -} - -export type BuildMetadataInput = { - trackingContext?: Maybe; - appVersion?: Maybe; - appBuildVersion?: Maybe; - cliVersion?: Maybe; - workflow?: Maybe; - credentialsSource?: Maybe; - sdkVersion?: Maybe; - runtimeVersion?: Maybe; - reactNativeVersion?: Maybe; - releaseChannel?: Maybe; - channel?: Maybe; - distribution?: Maybe; - iosEnterpriseProvisioning?: Maybe; - appName?: Maybe; - appIdentifier?: Maybe; - buildProfile?: Maybe; - gitCommitHash?: Maybe; - isGitWorkingTreeDirty?: Maybe; - username?: Maybe; -}; - -export enum BuildCredentialsSource { - Local = 'LOCAL', - Remote = 'REMOTE' -} - -export type CreateBuildResult = { - __typename?: 'CreateBuildResult'; - build: Build; - deprecationInfo?: Maybe; -}; - -export type EasBuildDeprecationInfo = { - __typename?: 'EASBuildDeprecationInfo'; - type: EasBuildDeprecationInfoType; - message: Scalars['String']; -}; - -export enum EasBuildDeprecationInfoType { - UserFacing = 'USER_FACING', - Internal = 'INTERNAL' -} - -export type IosJobInput = { - type: BuildWorkflow; - projectArchive: ProjectArchiveSourceInput; - projectRootDirectory: Scalars['String']; - releaseChannel?: Maybe; - updates?: Maybe; - /** @deprecated */ - distribution?: Maybe; - simulator?: Maybe; - developmentClient?: Maybe; - secrets?: Maybe; - builderEnvironment?: Maybe; - cache?: Maybe; - scheme?: Maybe; - buildConfiguration?: Maybe; - artifactPath?: Maybe; - /** @deprecated */ - buildType?: Maybe; - username?: Maybe; -}; - -export type IosJobSecretsInput = { - buildCredentials?: Maybe>>; - environmentSecrets?: Maybe; -}; - -export type IosJobTargetCredentialsInput = { - targetName: Scalars['String']; - provisioningProfileBase64: Scalars['String']; - distributionCertificate: IosJobDistributionCertificateInput; -}; - -export type IosJobDistributionCertificateInput = { - dataBase64: Scalars['String']; - password: Scalars['String']; -}; - -export type IosBuilderEnvironmentInput = { - image?: Maybe; - node?: Maybe; - yarn?: Maybe; - bundler?: Maybe; - fastlane?: Maybe; - cocoapods?: Maybe; - expoCli?: Maybe; - env?: Maybe; -}; - -/** @deprecated Use developmentClient option instead. */ -export enum IosBuildType { - Release = 'RELEASE', - DevelopmentClient = 'DEVELOPMENT_CLIENT' -} - -export type IosAppBuildCredentialsMutation = { - __typename?: 'IosAppBuildCredentialsMutation'; - /** Create a set of build credentials for an iOS app */ - createIosAppBuildCredentials: IosAppBuildCredentials; - /** Set the distribution certificate to be used for an iOS app */ - setDistributionCertificate: IosAppBuildCredentials; - /** Set the provisioning profile to be used for an iOS app */ - setProvisioningProfile: IosAppBuildCredentials; -}; - - -export type IosAppBuildCredentialsMutationCreateIosAppBuildCredentialsArgs = { - iosAppBuildCredentialsInput: IosAppBuildCredentialsInput; - iosAppCredentialsId: Scalars['ID']; -}; - - -export type IosAppBuildCredentialsMutationSetDistributionCertificateArgs = { - id: Scalars['ID']; - distributionCertificateId: Scalars['ID']; -}; - - -export type IosAppBuildCredentialsMutationSetProvisioningProfileArgs = { - id: Scalars['ID']; - provisioningProfileId: Scalars['ID']; -}; - -export type IosAppBuildCredentialsInput = { - iosDistributionType: IosDistributionType; - distributionCertificateId: Scalars['ID']; - provisioningProfileId: Scalars['ID']; -}; - -export type IosAppCredentialsMutation = { - __typename?: 'IosAppCredentialsMutation'; - /** Create a set of credentials for an iOS app */ - createIosAppCredentials: IosAppCredentials; - /** Set the push key to be used in an iOS app */ - setPushKey: IosAppCredentials; - /** Set the App Store Connect Api Key to be used for submitting an iOS app */ - setAppStoreConnectApiKeyForSubmissions: IosAppCredentials; -}; - - -export type IosAppCredentialsMutationCreateIosAppCredentialsArgs = { - iosAppCredentialsInput: IosAppCredentialsInput; - appId: Scalars['ID']; - appleAppIdentifierId: Scalars['ID']; -}; - - -export type IosAppCredentialsMutationSetPushKeyArgs = { - id: Scalars['ID']; - pushKeyId: Scalars['ID']; -}; - - -export type IosAppCredentialsMutationSetAppStoreConnectApiKeyForSubmissionsArgs = { - id: Scalars['ID']; - ascApiKeyId: Scalars['ID']; -}; - -export type IosAppCredentialsInput = { - appleTeamId?: Maybe; - pushKeyId?: Maybe; - appStoreConnectApiKeyForSubmissionsId?: Maybe; -}; - -export type KeystoreGenerationUrlMutation = { - __typename?: 'KeystoreGenerationUrlMutation'; - /** Create a Keystore Generation URL */ - createKeystoreGenerationUrl: KeystoreGenerationUrl; -}; - -export type KeystoreGenerationUrl = { - __typename?: 'KeystoreGenerationUrl'; - id: Scalars['ID']; - url: Scalars['String']; -}; - -export type RobotMutation = { - __typename?: 'RobotMutation'; - /** Create a Robot and grant it Permissions on an Account */ - createRobotForAccount: Robot; - /** Update a Robot */ - updateRobot: Robot; - /** Delete a Robot */ - deleteRobot: DeleteRobotResult; -}; - - -export type RobotMutationCreateRobotForAccountArgs = { - robotData?: Maybe; - accountID: Scalars['String']; - permissions: Array>; -}; - - -export type RobotMutationUpdateRobotArgs = { - id: Scalars['String']; - robotData: RobotDataInput; -}; - - -export type RobotMutationDeleteRobotArgs = { - id: Scalars['String']; -}; - -export type RobotDataInput = { - name?: Maybe; -}; - -/** Represents a robot (not human) actor. */ -export type Robot = Actor & { - __typename?: 'Robot'; - id: Scalars['ID']; - firstName?: Maybe; - created: Scalars['DateTime']; - isExpoAdmin: Scalars['Boolean']; - displayName: Scalars['String']; - /** Associated accounts */ - accounts: Array; - /** Access Tokens belonging to this actor */ - accessTokens: Array; - /** - * Server feature gate values for this actor, optionally filtering by desired gates. - * Only resolves for the viewer. - */ - featureGates: Scalars['JSONObject']; -}; - - -/** Represents a robot (not human) actor. */ -export type RobotFeatureGatesArgs = { - filter?: Maybe>; -}; - -export type DeleteRobotResult = { - __typename?: 'DeleteRobotResult'; - id: Scalars['ID']; -}; - -export type SubmissionMutation = { - __typename?: 'SubmissionMutation'; - /** - * Create an EAS Submit submission - * @deprecated Use createIosSubmission / createAndroidSubmission instead - */ - createSubmission: CreateSubmissionResult; - /** Create an iOS EAS Submit submission */ - createIosSubmission: CreateSubmissionResult; - /** Create an Android EAS Submit submission */ - createAndroidSubmission: CreateSubmissionResult; -}; - - -export type SubmissionMutationCreateSubmissionArgs = { - input: CreateSubmissionInput; -}; - - -export type SubmissionMutationCreateIosSubmissionArgs = { - input: CreateIosSubmissionInput; -}; - - -export type SubmissionMutationCreateAndroidSubmissionArgs = { - input: CreateAndroidSubmissionInput; -}; - -export type CreateSubmissionInput = { - appId: Scalars['ID']; - platform: AppPlatform; - config: Scalars['JSONObject']; - submittedBuildId?: Maybe; -}; - -export type CreateSubmissionResult = { - __typename?: 'CreateSubmissionResult'; - /** Created submission */ - submission: Submission; -}; - -export type CreateIosSubmissionInput = { - appId: Scalars['ID']; - config: IosSubmissionConfigInput; - submittedBuildId?: Maybe; -}; - -export type IosSubmissionConfigInput = { - appleAppSpecificPassword?: Maybe; - ascApiKey?: Maybe; - ascApiKeyId?: Maybe; - archiveUrl?: Maybe; - appleIdUsername?: Maybe; - ascAppIdentifier: Scalars['String']; -}; - -export type AscApiKeyInput = { - keyP8: Scalars['String']; - keyIdentifier: Scalars['String']; - issuerIdentifier: Scalars['String']; -}; - -export type CreateAndroidSubmissionInput = { - appId: Scalars['ID']; - config: AndroidSubmissionConfigInput; - submittedBuildId?: Maybe; -}; - -export type AndroidSubmissionConfigInput = { - googleServiceAccountKeyId?: Maybe; - googleServiceAccountKeyJson?: Maybe; - archiveUrl?: Maybe; - applicationIdentifier?: Maybe; - track: SubmissionAndroidTrack; - releaseStatus?: Maybe; - changesNotSentForReview?: Maybe; -}; - -export type UpdateChannelMutation = { - __typename?: 'UpdateChannelMutation'; - /** - * Create an EAS channel for an app. - * - * In order to work with GraphQL formatting, the branchMapping should be a - * stringified JSON supplied to the mutation as a variable. - */ - createUpdateChannelForApp?: Maybe; - /** - * Edit an EAS channel. - * - * In order to work with GraphQL formatting, the branchMapping should be a - * stringified JSON supplied to the mutation as a variable. - */ - editUpdateChannel?: Maybe; - /** delete an EAS channel that doesn't point to any branches */ - deleteUpdateChannel: DeleteUpdateChannelResult; -}; - - -export type UpdateChannelMutationCreateUpdateChannelForAppArgs = { - appId: Scalars['ID']; - name: Scalars['String']; - branchMapping?: Maybe; -}; - - -export type UpdateChannelMutationEditUpdateChannelArgs = { - channelId: Scalars['ID']; - branchMapping: Scalars['String']; -}; - - -export type UpdateChannelMutationDeleteUpdateChannelArgs = { - channelId: Scalars['ID']; -}; - -export type DeleteUpdateChannelResult = { - __typename?: 'DeleteUpdateChannelResult'; - id: Scalars['ID']; -}; - -export type UpdateMutation = { - __typename?: 'UpdateMutation'; - /** Delete an EAS update group */ - deleteUpdateGroup: DeleteUpdateGroupResult; -}; - - -export type UpdateMutationDeleteUpdateGroupArgs = { - group: Scalars['ID']; -}; - -export type DeleteUpdateGroupResult = { - __typename?: 'DeleteUpdateGroupResult'; - group: Scalars['ID']; -}; - -export type UpdateBranchMutation = { - __typename?: 'UpdateBranchMutation'; - /** Create an EAS branch for an app */ - createUpdateBranchForApp?: Maybe; - /** - * Edit an EAS branch. The branch can be specified either by its ID or - * with the combination of (appId, name). - */ - editUpdateBranch: UpdateBranch; - /** Delete an EAS branch and all of its updates as long as the branch is not being used by any channels */ - deleteUpdateBranch: DeleteUpdateBranchResult; - /** Publish an update group to a branch */ - publishUpdateGroups: Array; -}; - - -export type UpdateBranchMutationCreateUpdateBranchForAppArgs = { - appId: Scalars['ID']; - name: Scalars['String']; -}; - - -export type UpdateBranchMutationEditUpdateBranchArgs = { - input: EditUpdateBranchInput; -}; - - -export type UpdateBranchMutationDeleteUpdateBranchArgs = { - branchId: Scalars['ID']; -}; - - -export type UpdateBranchMutationPublishUpdateGroupsArgs = { - publishUpdateGroupsInput: Array; -}; - -export type EditUpdateBranchInput = { - id?: Maybe; - appId?: Maybe; - name?: Maybe; - newName: Scalars['String']; -}; - -export type DeleteUpdateBranchResult = { - __typename?: 'DeleteUpdateBranchResult'; - id: Scalars['ID']; -}; - -export type PublishUpdateGroupInput = { - branchId: Scalars['String']; - updateInfoGroup: UpdateInfoGroup; - runtimeVersion: Scalars['String']; - message?: Maybe; -}; - -export type UpdateInfoGroup = { - android?: Maybe; - ios?: Maybe; - web?: Maybe; -}; - -export type PartialManifest = { - launchAsset: PartialManifestAsset; - assets: Array>; - extra?: Maybe; -}; - -export type PartialManifestAsset = { - fileSHA256: Scalars['String']; - bundleKey: Scalars['String']; - contentType: Scalars['String']; - fileExtension?: Maybe; - storageKey: Scalars['String']; -}; - -export type UploadSession = { - __typename?: 'UploadSession'; - /** Create an Upload Session */ - createUploadSession: Scalars['JSONObject']; -}; - - -export type UploadSessionCreateUploadSessionArgs = { - type: UploadSessionType; -}; - -export enum UploadSessionType { - EasBuildProjectSources = 'EAS_BUILD_PROJECT_SOURCES', - EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE' -} - -export type UserInvitationMutation = { - __typename?: 'UserInvitationMutation'; - /** - * Create a UserInvitation for an email that when accepted grants - * the specified permissions on an Account - */ - createUserInvitationForAccount: UserInvitation; - /** Re-send UserInivitation by ID */ - resendUserInvitation: UserInvitation; - /** Rescind UserInvitation by ID */ - deleteUserInvitation: RescindUserInvitationResult; - /** - * Delete UserInvitation by token. Note that the viewer's email is not required to match - * the email on the invitation. - */ - deleteUserInvitationByToken: RescindUserInvitationResult; - /** Accept UserInvitation by ID. Viewer must have matching email and email must be verified. */ - acceptUserInvitationAsViewer: AcceptUserInvitationResult; - /** - * Accept UserInvitation by token. Note that the viewer's email is not required to match - * the email on the invitation. If viewer's email does match that of the invitation, - * their email will also be verified. - */ - acceptUserInvitationByTokenAsViewer: AcceptUserInvitationResult; -}; - - -export type UserInvitationMutationCreateUserInvitationForAccountArgs = { - accountID: Scalars['ID']; - email: Scalars['String']; - permissions: Array>; -}; - - -export type UserInvitationMutationResendUserInvitationArgs = { - id: Scalars['ID']; -}; - - -export type UserInvitationMutationDeleteUserInvitationArgs = { - id: Scalars['ID']; -}; - - -export type UserInvitationMutationDeleteUserInvitationByTokenArgs = { - token: Scalars['ID']; -}; - - -export type UserInvitationMutationAcceptUserInvitationAsViewerArgs = { - id: Scalars['ID']; -}; - - -export type UserInvitationMutationAcceptUserInvitationByTokenAsViewerArgs = { - token: Scalars['ID']; -}; - -export type RescindUserInvitationResult = { - __typename?: 'RescindUserInvitationResult'; - id: Scalars['ID']; -}; - -export type AcceptUserInvitationResult = { - __typename?: 'AcceptUserInvitationResult'; - success?: Maybe; -}; - -export type MeMutation = { - __typename?: 'MeMutation'; - /** Update the current user's data */ - updateProfile?: Maybe; - /** Update an App that the current user owns */ - updateApp?: Maybe; - /** Unpublish an App that the current user owns */ - unpublishApp?: Maybe; - /** Transfer project to a different Account */ - transferApp: App; - /** Delete a Snack that the current user owns */ - deleteSnack?: Maybe; - /** Create a new Account and grant this User the owner Role */ - createAccount?: Maybe; - /** Delete an Account created via createAccount */ - deleteAccount: DeleteAccountResult; - /** Leave an Account (revoke own permissions on Account) */ - leaveAccount: LeaveAccountResult; - /** Initiate setup of two-factor authentication for the current user */ - initiateSecondFactorAuthentication: SecondFactorInitiationResult; - /** Purge unfinished two-factor authentication setup for the current user if not fully-set-up */ - purgeUnfinishedSecondFactorAuthentication: SecondFactorBooleanResult; - /** Regenerate backup codes for the current user */ - regenerateSecondFactorBackupCodes: SecondFactorRegenerateBackupCodesResult; - /** Send SMS OTP to a second factor device for use during device setup or during change confirmation */ - sendSMSOTPToSecondFactorDevice: SecondFactorBooleanResult; - /** Certify an initiated second factor authentication method for the current user */ - certifySecondFactorDevice: SecondFactorBooleanResult; - /** Set the user's primary second factor device */ - setPrimarySecondFactorDevice: SecondFactorBooleanResult; - /** Add an additional second factor device */ - addSecondFactorDevice: SecondFactorDeviceConfigurationResult; - /** Delete a second factor device */ - deleteSecondFactorDevice: SecondFactorBooleanResult; - /** Disable all second factor authentication for the current user */ - disableSecondFactorAuthentication: SecondFactorBooleanResult; -}; - - -export type MeMutationUpdateProfileArgs = { - userData: UserDataInput; -}; - - -export type MeMutationUpdateAppArgs = { - appData: AppDataInput; -}; - - -export type MeMutationUnpublishAppArgs = { - appId: Scalars['ID']; -}; - - -export type MeMutationTransferAppArgs = { - appId: Scalars['ID']; - destinationAccountId: Scalars['ID']; -}; - - -export type MeMutationDeleteSnackArgs = { - snackId: Scalars['ID']; -}; - - -export type MeMutationCreateAccountArgs = { - accountData: AccountDataInput; -}; - - -export type MeMutationDeleteAccountArgs = { - accountId: Scalars['ID']; -}; - - -export type MeMutationLeaveAccountArgs = { - accountId: Scalars['ID']; -}; - - -export type MeMutationInitiateSecondFactorAuthenticationArgs = { - deviceConfigurations: Array>; - recaptchaResponseToken?: Maybe; -}; - - -export type MeMutationRegenerateSecondFactorBackupCodesArgs = { - otp: Scalars['String']; -}; - - -export type MeMutationSendSmsotpToSecondFactorDeviceArgs = { - userSecondFactorDeviceId: Scalars['ID']; -}; - - -export type MeMutationCertifySecondFactorDeviceArgs = { - otp: Scalars['String']; -}; - - -export type MeMutationSetPrimarySecondFactorDeviceArgs = { - userSecondFactorDeviceId: Scalars['ID']; -}; - - -export type MeMutationAddSecondFactorDeviceArgs = { - deviceConfiguration: SecondFactorDeviceConfiguration; - otp: Scalars['String']; -}; - - -export type MeMutationDeleteSecondFactorDeviceArgs = { - userSecondFactorDeviceId: Scalars['ID']; - otp: Scalars['String']; -}; - - -export type MeMutationDisableSecondFactorAuthenticationArgs = { - otp: Scalars['String']; -}; - -export type UserDataInput = { - id?: Maybe; - username?: Maybe; - industry?: Maybe; - location?: Maybe; - githubUsername?: Maybe; - twitterUsername?: Maybe; - email?: Maybe; - firstName?: Maybe; - lastName?: Maybe; - fullName?: Maybe; - profilePhoto?: Maybe; - isOnboarded?: Maybe; - isLegacy?: Maybe; - isEmailUnsubscribed?: Maybe; - wasLegacy?: Maybe; - appetizeCode?: Maybe; -}; - -export type AppDataInput = { - id: Scalars['ID']; - privacy?: Maybe; -}; - -export type AccountDataInput = { - name: Scalars['String']; -}; - -export type DeleteAccountResult = { - __typename?: 'DeleteAccountResult'; - id: Scalars['ID']; -}; - -export type LeaveAccountResult = { - __typename?: 'LeaveAccountResult'; - success: Scalars['Boolean']; -}; - -export type SecondFactorDeviceConfiguration = { - method: SecondFactorMethod; - smsPhoneNumber?: Maybe; - name: Scalars['String']; - isPrimary: Scalars['Boolean']; -}; - -export type SecondFactorInitiationResult = { - __typename?: 'SecondFactorInitiationResult'; - configurationResults: Array>; - plaintextBackupCodes: Array>; -}; - -export type SecondFactorDeviceConfigurationResult = { - __typename?: 'SecondFactorDeviceConfigurationResult'; - secondFactorDevice: UserSecondFactorDevice; - secret: Scalars['String']; - keyURI: Scalars['String']; -}; - -export type SecondFactorBooleanResult = { - __typename?: 'SecondFactorBooleanResult'; - success: Scalars['Boolean']; -}; - -export type SecondFactorRegenerateBackupCodesResult = { - __typename?: 'SecondFactorRegenerateBackupCodesResult'; - plaintextBackupCodes: Array>; -}; - -export type EmailSubscriptionMutation = { - __typename?: 'EmailSubscriptionMutation'; - addUser?: Maybe; -}; - - -export type EmailSubscriptionMutationAddUserArgs = { - addUserInput: AddUserInput; -}; - -export type AddUserInput = { - email: Scalars['String']; - tags?: Maybe>; - audience?: Maybe; -}; - -export enum MailchimpTag { - EasMasterList = 'EAS_MASTER_LIST', - DevClientUsers = 'DEV_CLIENT_USERS' -} - -export enum MailchimpAudience { - ExpoDevelopers = 'EXPO_DEVELOPERS' -} - -export type AddUserPayload = { - __typename?: 'AddUserPayload'; - id?: Maybe; - email_address?: Maybe; - status?: Maybe; - timestamp_signup?: Maybe; - tags?: Maybe>; - list_id?: Maybe; -}; - -export type MailchimpTagPayload = { - __typename?: 'MailchimpTagPayload'; - id?: Maybe; - name?: Maybe; -}; - -export type EnvironmentSecretMutation = { - __typename?: 'EnvironmentSecretMutation'; - /** Create an environment secret for an Account */ - createEnvironmentSecretForAccount: EnvironmentSecret; - /** Create an environment secret for an App */ - createEnvironmentSecretForApp: EnvironmentSecret; - /** Delete an environment secret */ - deleteEnvironmentSecret: DeleteEnvironmentSecretResult; -}; - - -export type EnvironmentSecretMutationCreateEnvironmentSecretForAccountArgs = { - environmentSecretData: CreateEnvironmentSecretInput; - accountId: Scalars['String']; -}; - - -export type EnvironmentSecretMutationCreateEnvironmentSecretForAppArgs = { - environmentSecretData: CreateEnvironmentSecretInput; - appId: Scalars['String']; -}; - - -export type EnvironmentSecretMutationDeleteEnvironmentSecretArgs = { - id: Scalars['String']; -}; - -export type CreateEnvironmentSecretInput = { - name: Scalars['String']; - value: Scalars['String']; -}; - -export type DeleteEnvironmentSecretResult = { - __typename?: 'DeleteEnvironmentSecretResult'; - id: Scalars['ID']; -}; - -export type WebhookMutation = { - __typename?: 'WebhookMutation'; - /** Create a Webhook */ - createWebhook: Webhook; - /** Update a Webhook */ - updateWebhook: Webhook; - /** Delete a Webhook */ - deleteWebhook: DeleteWebhookResult; -}; - - -export type WebhookMutationCreateWebhookArgs = { - appId: Scalars['String']; - webhookInput: WebhookInput; -}; - - -export type WebhookMutationUpdateWebhookArgs = { - webhookId: Scalars['ID']; - webhookInput: WebhookInput; -}; - - -export type WebhookMutationDeleteWebhookArgs = { - webhookId: Scalars['ID']; -}; - -export type WebhookInput = { - url: Scalars['String']; - secret: Scalars['String']; - event: WebhookType; -}; - -export type DeleteWebhookResult = { - __typename?: 'DeleteWebhookResult'; - id: Scalars['ID']; -}; - -export enum StandardOffer { - /** $29 USD per month, 30 day trial */ - Default = 'DEFAULT', - /** $348 USD per year, 30 day trial */ - YearlySub = 'YEARLY_SUB', - /** $29 USD per month, 1 year trial */ - YcDeals = 'YC_DEALS', - /** $800 USD per month */ - Support = 'SUPPORT' -} - -export enum IosSchemeBuildConfiguration { - Release = 'RELEASE', - Debug = 'DEBUG' -} - -/** @deprecated Use developmentClient option instead. */ -export enum IosManagedBuildType { - Release = 'RELEASE', - DevelopmentClient = 'DEVELOPMENT_CLIENT' -} - -export enum CacheControlScope { - Public = 'PUBLIC', - Private = 'PRIVATE' -} - - -export type CreateUpdateBranchForAppMutationVariables = Exact<{ - appId: Scalars['ID']; - name: Scalars['String']; -}>; - - -export type CreateUpdateBranchForAppMutation = ( - { __typename?: 'RootMutation' } - & { updateBranch: ( - { __typename?: 'UpdateBranchMutation' } - & { createUpdateBranchForApp?: Maybe<( - { __typename?: 'UpdateBranch' } - & Pick - )> } - ) } -); - -export type GetBranchInfoQueryVariables = Exact<{ - appId: Scalars['String']; - name: Scalars['String']; -}>; - - -export type GetBranchInfoQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { updateBranchByName?: Maybe<( - { __typename?: 'UpdateBranch' } - & Pick - )> } - ) } - ) } -); - -export type DeleteUpdateBranchMutationVariables = Exact<{ - branchId: Scalars['ID']; -}>; - - -export type DeleteUpdateBranchMutation = ( - { __typename?: 'RootMutation' } - & { updateBranch: ( - { __typename?: 'UpdateBranchMutation' } - & { deleteUpdateBranch: ( - { __typename?: 'DeleteUpdateBranchResult' } - & Pick - ) } - ) } -); - -export type BranchesByAppQueryVariables = Exact<{ - appId: Scalars['String']; - limit: Scalars['Int']; -}>; - - -export type BranchesByAppQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { updateBranches: Array<( - { __typename?: 'UpdateBranch' } - & Pick - & UpdateBranchFragment - )> } - ) } - ) } -); - -export type EditUpdateBranchMutationVariables = Exact<{ - input: EditUpdateBranchInput; -}>; - - -export type EditUpdateBranchMutation = ( - { __typename?: 'RootMutation' } - & { updateBranch: ( - { __typename?: 'UpdateBranchMutation' } - & { editUpdateBranch: ( - { __typename?: 'UpdateBranch' } - & Pick - ) } - ) } -); - -export type ViewBranchQueryVariables = Exact<{ - appId: Scalars['String']; - name: Scalars['String']; - limit: Scalars['Int']; -}>; - - -export type ViewBranchQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { updateBranchByName?: Maybe<( - { __typename?: 'UpdateBranch' } - & Pick - & { updates: Array<( - { __typename?: 'Update' } - & Pick - & { actor?: Maybe<( - { __typename?: 'User' } - & Pick - ) | ( - { __typename?: 'Robot' } - & Pick - )> } - )> } - )> } - ) } - ) } -); - -export type CancelBuildMutationVariables = Exact<{ - buildId: Scalars['ID']; -}>; - - -export type CancelBuildMutation = ( - { __typename?: 'RootMutation' } - & { build?: Maybe<( - { __typename?: 'BuildMutation' } - & { cancel: ( - { __typename?: 'Build' } - & Pick - ) } - )> } -); - -export type CreateUpdateChannelOnAppMutationVariables = Exact<{ - appId: Scalars['ID']; - name: Scalars['String']; - branchMapping: Scalars['String']; -}>; - - -export type CreateUpdateChannelOnAppMutation = ( - { __typename?: 'RootMutation' } - & { updateChannel: ( - { __typename?: 'UpdateChannelMutation' } - & { createUpdateChannelForApp?: Maybe<( - { __typename?: 'UpdateChannel' } - & Pick - )> } - ) } -); - -export type GetChannelInfoQueryVariables = Exact<{ - appId: Scalars['String']; - name: Scalars['String']; -}>; - - -export type GetChannelInfoQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { updateChannelByName?: Maybe<( - { __typename?: 'UpdateChannel' } - & Pick - )> } - ) } - ) } -); - -export type DeleteUpdateChannelMutationVariables = Exact<{ - channelId: Scalars['ID']; -}>; - - -export type DeleteUpdateChannelMutation = ( - { __typename?: 'RootMutation' } - & { updateChannel: ( - { __typename?: 'UpdateChannelMutation' } - & { deleteUpdateChannel: ( - { __typename?: 'DeleteUpdateChannelResult' } - & Pick - ) } - ) } -); - -export type GetChannelByNameToEditQueryVariables = Exact<{ - appId: Scalars['String']; - channelName: Scalars['String']; -}>; - - -export type GetChannelByNameToEditQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { updateChannelByName?: Maybe<( - { __typename?: 'UpdateChannel' } - & Pick - & { updateBranches: Array<( - { __typename?: 'UpdateBranch' } - & Pick - )> } - )> } - ) } - ) } -); - -export type UpdateChannelBranchMappingMutationVariables = Exact<{ - channelId: Scalars['ID']; - branchMapping: Scalars['String']; -}>; - - -export type UpdateChannelBranchMappingMutation = ( - { __typename?: 'RootMutation' } - & { updateChannel: ( - { __typename?: 'UpdateChannelMutation' } - & { editUpdateChannel?: Maybe<( - { __typename?: 'UpdateChannel' } - & Pick - )> } - ) } -); - -export type GetAllChannelsForAppQueryVariables = Exact<{ - appId: Scalars['String']; - offset: Scalars['Int']; - limit: Scalars['Int']; -}>; - - -export type GetAllChannelsForAppQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { updateChannels: Array<( - { __typename?: 'UpdateChannel' } - & Pick - & { updateBranches: Array<( - { __typename?: 'UpdateBranch' } - & Pick - & { updates: Array<( - { __typename?: 'Update' } - & Pick - & { actor?: Maybe<( - { __typename?: 'User' } - & Pick - ) | ( - { __typename?: 'Robot' } - & Pick - )> } - )> } - )> } - )> } - ) } - ) } -); - -export type GetChannelByNameForAppQueryVariables = Exact<{ - appId: Scalars['String']; - channelName: Scalars['String']; -}>; - - -export type GetChannelByNameForAppQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { updateChannelByName?: Maybe<( - { __typename?: 'UpdateChannel' } - & Pick - & { updateBranches: Array<( - { __typename?: 'UpdateBranch' } - & Pick - & { updates: Array<( - { __typename?: 'Update' } - & Pick - & { actor?: Maybe<( - { __typename?: 'User' } - & Pick - ) | ( - { __typename?: 'Robot' } - & Pick - )> } - )> } - )> } - )> } - ) } - ) } -); - -export type AppInfoQueryVariables = Exact<{ - appId: Scalars['String']; -}>; - - -export type AppInfoQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - ) } - ) } -); - -export type DeleteUpdateGroupMutationVariables = Exact<{ - group: Scalars['ID']; -}>; - - -export type DeleteUpdateGroupMutation = ( - { __typename?: 'RootMutation' } - & { update: ( - { __typename?: 'UpdateMutation' } - & { deleteUpdateGroup: ( - { __typename?: 'DeleteUpdateGroupResult' } - & Pick - ) } - ) } -); - -export type GetUpdateGroupAsyncQueryVariables = Exact<{ - group: Scalars['ID']; -}>; - - -export type GetUpdateGroupAsyncQuery = ( - { __typename?: 'RootQuery' } - & { updatesByGroup: Array<( - { __typename?: 'Update' } - & Pick - )> } -); - -export type UpdatesByGroupQueryVariables = Exact<{ - groupId: Scalars['ID']; -}>; - - -export type UpdatesByGroupQuery = ( - { __typename?: 'RootQuery' } - & { updatesByGroup: Array<( - { __typename?: 'Update' } - & Pick - & { actor?: Maybe<( - { __typename?: 'User' } - & Pick - ) | ( - { __typename?: 'Robot' } - & Pick - )> } - )> } -); - -export type CreateAndroidAppBuildCredentialsMutationVariables = Exact<{ - androidAppBuildCredentialsInput: AndroidAppBuildCredentialsInput; - androidAppCredentialsId: Scalars['ID']; -}>; - - -export type CreateAndroidAppBuildCredentialsMutation = ( - { __typename?: 'RootMutation' } - & { androidAppBuildCredentials: ( - { __typename?: 'AndroidAppBuildCredentialsMutation' } - & { createAndroidAppBuildCredentials?: Maybe<( - { __typename?: 'AndroidAppBuildCredentials' } - & Pick - & AndroidAppBuildCredentialsFragment - )> } - ) } -); - -export type SetKeystoreMutationVariables = Exact<{ - androidAppBuildCredentialsId: Scalars['ID']; - keystoreId: Scalars['ID']; -}>; - - -export type SetKeystoreMutation = ( - { __typename?: 'RootMutation' } - & { androidAppBuildCredentials: ( - { __typename?: 'AndroidAppBuildCredentialsMutation' } - & { setKeystore?: Maybe<( - { __typename?: 'AndroidAppBuildCredentials' } - & Pick - & AndroidAppBuildCredentialsFragment - )> } - ) } -); - -export type CreateAndroidAppCredentialsMutationVariables = Exact<{ - androidAppCredentialsInput: AndroidAppCredentialsInput; - appId: Scalars['ID']; - applicationIdentifier: Scalars['String']; -}>; - - -export type CreateAndroidAppCredentialsMutation = ( - { __typename?: 'RootMutation' } - & { androidAppCredentials: ( - { __typename?: 'AndroidAppCredentialsMutation' } - & { createAndroidAppCredentials?: Maybe<( - { __typename?: 'AndroidAppCredentials' } - & Pick - & CommonAndroidAppCredentialsFragment - )> } - ) } -); - -export type SetFcmMutationVariables = Exact<{ - androidAppCredentialsId: Scalars['ID']; - fcmId: Scalars['ID']; -}>; - - -export type SetFcmMutation = ( - { __typename?: 'RootMutation' } - & { androidAppCredentials: ( - { __typename?: 'AndroidAppCredentialsMutation' } - & { setFcm?: Maybe<( - { __typename?: 'AndroidAppCredentials' } - & Pick - & CommonAndroidAppCredentialsFragment - )> } - ) } -); - -export type SetGoogleServiceAccountKeyForSubmissionsMutationVariables = Exact<{ - androidAppCredentialsId: Scalars['ID']; - googleServiceAccountKeyId: Scalars['ID']; -}>; - - -export type SetGoogleServiceAccountKeyForSubmissionsMutation = ( - { __typename?: 'RootMutation' } - & { androidAppCredentials: ( - { __typename?: 'AndroidAppCredentialsMutation' } - & { setGoogleServiceAccountKeyForSubmissions?: Maybe<( - { __typename?: 'AndroidAppCredentials' } - & Pick - & CommonAndroidAppCredentialsFragment - )> } - ) } -); - -export type CreateAndroidFcmMutationVariables = Exact<{ - androidFcmInput: AndroidFcmInput; - accountId: Scalars['ID']; -}>; - - -export type CreateAndroidFcmMutation = ( - { __typename?: 'RootMutation' } - & { androidFcm: ( - { __typename?: 'AndroidFcmMutation' } - & { createAndroidFcm: ( - { __typename?: 'AndroidFcm' } - & Pick - & AndroidFcmFragment - ) } - ) } -); - -export type DeleteAndroidFcmMutationVariables = Exact<{ - androidFcmId: Scalars['ID']; -}>; - - -export type DeleteAndroidFcmMutation = ( - { __typename?: 'RootMutation' } - & { androidFcm: ( - { __typename?: 'AndroidFcmMutation' } - & { deleteAndroidFcm: ( - { __typename?: 'deleteAndroidFcmResult' } - & Pick - ) } - ) } -); - -export type CreateAndroidKeystoreMutationVariables = Exact<{ - androidKeystoreInput: AndroidKeystoreInput; - accountId: Scalars['ID']; -}>; - - -export type CreateAndroidKeystoreMutation = ( - { __typename?: 'RootMutation' } - & { androidKeystore: ( - { __typename?: 'AndroidKeystoreMutation' } - & { createAndroidKeystore?: Maybe<( - { __typename?: 'AndroidKeystore' } - & Pick - & AndroidKeystoreFragment - )> } - ) } -); - -export type DeleteAndroidKeystoreMutationVariables = Exact<{ - androidKeystoreId: Scalars['ID']; -}>; - - -export type DeleteAndroidKeystoreMutation = ( - { __typename?: 'RootMutation' } - & { androidKeystore: ( - { __typename?: 'AndroidKeystoreMutation' } - & { deleteAndroidKeystore: ( - { __typename?: 'DeleteAndroidKeystoreResult' } - & Pick - ) } - ) } -); - -export type CreateGoogleServiceAccountKeyMutationVariables = Exact<{ - googleServiceAccountKeyInput: GoogleServiceAccountKeyInput; - accountId: Scalars['ID']; -}>; - - -export type CreateGoogleServiceAccountKeyMutation = ( - { __typename?: 'RootMutation' } - & { googleServiceAccountKey: ( - { __typename?: 'GoogleServiceAccountKeyMutation' } - & { createGoogleServiceAccountKey: ( - { __typename?: 'GoogleServiceAccountKey' } - & Pick - & GoogleServiceAccountKeyFragment - ) } - ) } -); - -export type DeleteGoogleServiceAccountKeyMutationVariables = Exact<{ - googleServiceAccountKeyId: Scalars['ID']; -}>; - - -export type DeleteGoogleServiceAccountKeyMutation = ( - { __typename?: 'RootMutation' } - & { googleServiceAccountKey: ( - { __typename?: 'GoogleServiceAccountKeyMutation' } - & { deleteGoogleServiceAccountKey: ( - { __typename?: 'DeleteGoogleServiceAccountKeyResult' } - & Pick - ) } - ) } -); - -export type CommonAndroidAppCredentialsWithBuildCredentialsByApplicationIdentifierQueryVariables = Exact<{ - projectFullName: Scalars['String']; - applicationIdentifier?: Maybe; - legacyOnly?: Maybe; -}>; - - -export type CommonAndroidAppCredentialsWithBuildCredentialsByApplicationIdentifierQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byFullName: ( - { __typename?: 'App' } - & Pick - & { androidAppCredentials: Array<( - { __typename?: 'AndroidAppCredentials' } - & Pick - & CommonAndroidAppCredentialsFragment - )> } - ) } - ) } -); - -export type GoogleServiceAccountKeyByAccountQueryVariables = Exact<{ - accountName: Scalars['String']; -}>; - - -export type GoogleServiceAccountKeyByAccountQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { googleServiceAccountKeys: Array<( - { __typename?: 'GoogleServiceAccountKey' } - & Pick - & GoogleServiceAccountKeyFragment - )> } - ) } - ) } -); - -export type CreateAppStoreConnectApiKeyMutationVariables = Exact<{ - appStoreConnectApiKeyInput: AppStoreConnectApiKeyInput; - accountId: Scalars['ID']; -}>; - - -export type CreateAppStoreConnectApiKeyMutation = ( - { __typename?: 'RootMutation' } - & { appStoreConnectApiKey: ( - { __typename?: 'AppStoreConnectApiKeyMutation' } - & { createAppStoreConnectApiKey: ( - { __typename?: 'AppStoreConnectApiKey' } - & Pick - & AppStoreConnectApiKeyFragment - ) } - ) } -); - -export type DeleteAppStoreConnectApiKeyMutationVariables = Exact<{ - appStoreConnectApiKeyId: Scalars['ID']; -}>; - - -export type DeleteAppStoreConnectApiKeyMutation = ( - { __typename?: 'RootMutation' } - & { appStoreConnectApiKey: ( - { __typename?: 'AppStoreConnectApiKeyMutation' } - & { deleteAppStoreConnectApiKey: ( - { __typename?: 'deleteAppStoreConnectApiKeyResult' } - & Pick - ) } - ) } -); - -export type CreateAppleAppIdentifierMutationVariables = Exact<{ - appleAppIdentifierInput: AppleAppIdentifierInput; - accountId: Scalars['ID']; -}>; - - -export type CreateAppleAppIdentifierMutation = ( - { __typename?: 'RootMutation' } - & { appleAppIdentifier: ( - { __typename?: 'AppleAppIdentifierMutation' } - & { createAppleAppIdentifier?: Maybe<( - { __typename?: 'AppleAppIdentifier' } - & Pick - & AppleAppIdentifierFragment - )> } - ) } -); - -export type CreateAppleDeviceMutationVariables = Exact<{ - appleDeviceInput: AppleDeviceInput; - accountId: Scalars['ID']; -}>; - - -export type CreateAppleDeviceMutation = ( - { __typename?: 'RootMutation' } - & { appleDevice: ( - { __typename?: 'AppleDeviceMutation' } - & { createAppleDevice: ( - { __typename?: 'AppleDevice' } - & Pick - & AppleDeviceFragment - ) } - ) } -); - -export type CreateAppleDeviceRegistrationRequestMutationVariables = Exact<{ - appleTeamId: Scalars['ID']; - accountId: Scalars['ID']; -}>; - - -export type CreateAppleDeviceRegistrationRequestMutation = ( - { __typename?: 'RootMutation' } - & { appleDeviceRegistrationRequest: ( - { __typename?: 'AppleDeviceRegistrationRequestMutation' } - & { createAppleDeviceRegistrationRequest: ( - { __typename?: 'AppleDeviceRegistrationRequest' } - & Pick - & AppleDeviceRegistrationRequestFragment - ) } - ) } -); - -export type CreateAppleDistributionCertificateMutationVariables = Exact<{ - appleDistributionCertificateInput: AppleDistributionCertificateInput; - accountId: Scalars['ID']; -}>; - - -export type CreateAppleDistributionCertificateMutation = ( - { __typename?: 'RootMutation' } - & { appleDistributionCertificate: ( - { __typename?: 'AppleDistributionCertificateMutation' } - & { createAppleDistributionCertificate?: Maybe<( - { __typename?: 'AppleDistributionCertificate' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )> } - & AppleDistributionCertificateFragment - )> } - ) } -); - -export type DeleteAppleDistributionCertificateMutationVariables = Exact<{ - appleDistributionCertificateId: Scalars['ID']; -}>; - - -export type DeleteAppleDistributionCertificateMutation = ( - { __typename?: 'RootMutation' } - & { appleDistributionCertificate: ( - { __typename?: 'AppleDistributionCertificateMutation' } - & { deleteAppleDistributionCertificate: ( - { __typename?: 'DeleteAppleDistributionCertificateResult' } - & Pick - ) } - ) } -); - -export type CreateAppleProvisioningProfileMutationVariables = Exact<{ - appleProvisioningProfileInput: AppleProvisioningProfileInput; - accountId: Scalars['ID']; - appleAppIdentifierId: Scalars['ID']; -}>; - - -export type CreateAppleProvisioningProfileMutation = ( - { __typename?: 'RootMutation' } - & { appleProvisioningProfile: ( - { __typename?: 'AppleProvisioningProfileMutation' } - & { createAppleProvisioningProfile: ( - { __typename?: 'AppleProvisioningProfile' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )> } - & AppleProvisioningProfileFragment - ) } - ) } -); - -export type UpdateAppleProvisioningProfileMutationVariables = Exact<{ - appleProvisioningProfileId: Scalars['ID']; - appleProvisioningProfileInput: AppleProvisioningProfileInput; -}>; - - -export type UpdateAppleProvisioningProfileMutation = ( - { __typename?: 'RootMutation' } - & { appleProvisioningProfile: ( - { __typename?: 'AppleProvisioningProfileMutation' } - & { updateAppleProvisioningProfile: ( - { __typename?: 'AppleProvisioningProfile' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )> } - & AppleProvisioningProfileFragment - ) } - ) } -); - -export type DeleteAppleProvisioningProfilesMutationVariables = Exact<{ - appleProvisioningProfileIds: Array; -}>; - - -export type DeleteAppleProvisioningProfilesMutation = ( - { __typename?: 'RootMutation' } - & { appleProvisioningProfile: ( - { __typename?: 'AppleProvisioningProfileMutation' } - & { deleteAppleProvisioningProfiles: Array<( - { __typename?: 'DeleteAppleProvisioningProfileResult' } - & Pick - )> } - ) } -); - -export type CreateApplePushKeyMutationVariables = Exact<{ - applePushKeyInput: ApplePushKeyInput; - accountId: Scalars['ID']; -}>; - - -export type CreateApplePushKeyMutation = ( - { __typename?: 'RootMutation' } - & { applePushKey: ( - { __typename?: 'ApplePushKeyMutation' } - & { createApplePushKey: ( - { __typename?: 'ApplePushKey' } - & Pick - & ApplePushKeyFragment - ) } - ) } -); - -export type DeleteApplePushKeyMutationVariables = Exact<{ - applePushKeyId: Scalars['ID']; -}>; - - -export type DeleteApplePushKeyMutation = ( - { __typename?: 'RootMutation' } - & { applePushKey: ( - { __typename?: 'ApplePushKeyMutation' } - & { deleteApplePushKey: ( - { __typename?: 'deleteApplePushKeyResult' } - & Pick - ) } - ) } -); - -export type CreateAppleTeamMutationVariables = Exact<{ - appleTeamInput: AppleTeamInput; - accountId: Scalars['ID']; -}>; - - -export type CreateAppleTeamMutation = ( - { __typename?: 'RootMutation' } - & { appleTeam: ( - { __typename?: 'AppleTeamMutation' } - & { createAppleTeam: ( - { __typename?: 'AppleTeam' } - & Pick - & { account: ( - { __typename?: 'Account' } - & Pick - ) } - & AppleTeamFragment - ) } - ) } -); - -export type CreateIosAppBuildCredentialsMutationVariables = Exact<{ - iosAppBuildCredentialsInput: IosAppBuildCredentialsInput; - iosAppCredentialsId: Scalars['ID']; -}>; - - -export type CreateIosAppBuildCredentialsMutation = ( - { __typename?: 'RootMutation' } - & { iosAppBuildCredentials: ( - { __typename?: 'IosAppBuildCredentialsMutation' } - & { createIosAppBuildCredentials: ( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & IosAppBuildCredentialsFragment - ) } - ) } -); - -export type SetDistributionCertificateMutationVariables = Exact<{ - iosAppBuildCredentialsId: Scalars['ID']; - distributionCertificateId: Scalars['ID']; -}>; - - -export type SetDistributionCertificateMutation = ( - { __typename?: 'RootMutation' } - & { iosAppBuildCredentials: ( - { __typename?: 'IosAppBuildCredentialsMutation' } - & { setDistributionCertificate: ( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & IosAppBuildCredentialsFragment - ) } - ) } -); - -export type SetProvisioningProfileMutationVariables = Exact<{ - iosAppBuildCredentialsId: Scalars['ID']; - provisioningProfileId: Scalars['ID']; -}>; - - -export type SetProvisioningProfileMutation = ( - { __typename?: 'RootMutation' } - & { iosAppBuildCredentials: ( - { __typename?: 'IosAppBuildCredentialsMutation' } - & { setProvisioningProfile: ( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & IosAppBuildCredentialsFragment - ) } - ) } -); - -export type CreateIosAppCredentialsMutationVariables = Exact<{ - iosAppCredentialsInput: IosAppCredentialsInput; - appId: Scalars['ID']; - appleAppIdentifierId: Scalars['ID']; -}>; - - -export type CreateIosAppCredentialsMutation = ( - { __typename?: 'RootMutation' } - & { iosAppCredentials: ( - { __typename?: 'IosAppCredentialsMutation' } - & { createIosAppCredentials: ( - { __typename?: 'IosAppCredentials' } - & Pick - & CommonIosAppCredentialsFragment - ) } - ) } -); - -export type SetPushKeyMutationVariables = Exact<{ - iosAppCredentialsId: Scalars['ID']; - pushKeyId: Scalars['ID']; -}>; - - -export type SetPushKeyMutation = ( - { __typename?: 'RootMutation' } - & { iosAppCredentials: ( - { __typename?: 'IosAppCredentialsMutation' } - & { setPushKey: ( - { __typename?: 'IosAppCredentials' } - & Pick - & CommonIosAppCredentialsFragment - ) } - ) } -); - -export type SetAppStoreConnectApiKeyForSubmissionsMutationVariables = Exact<{ - iosAppCredentialsId: Scalars['ID']; - ascApiKeyId: Scalars['ID']; -}>; - - -export type SetAppStoreConnectApiKeyForSubmissionsMutation = ( - { __typename?: 'RootMutation' } - & { iosAppCredentials: ( - { __typename?: 'IosAppCredentialsMutation' } - & { setAppStoreConnectApiKeyForSubmissions: ( - { __typename?: 'IosAppCredentials' } - & Pick - & CommonIosAppCredentialsFragment - ) } - ) } -); - -export type AppByFullNameQueryVariables = Exact<{ - fullName: Scalars['String']; -}>; - - -export type AppByFullNameQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byFullName: ( - { __typename?: 'App' } - & Pick - & AppFragment - ) } - ) } -); - -export type AppStoreConnectApiKeyByAccountQueryVariables = Exact<{ - accountName: Scalars['String']; -}>; - - -export type AppStoreConnectApiKeyByAccountQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { appStoreConnectApiKeys: Array<( - { __typename?: 'AppStoreConnectApiKey' } - & Pick - & AppStoreConnectApiKeyFragment - )> } - ) } - ) } -); - -export type AppleAppIdentifierByBundleIdQueryVariables = Exact<{ - accountName: Scalars['String']; - bundleIdentifier: Scalars['String']; -}>; - - -export type AppleAppIdentifierByBundleIdQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { appleAppIdentifiers: Array<( - { __typename?: 'AppleAppIdentifier' } - & Pick - & AppleAppIdentifierFragment - )> } - ) } - ) } -); - -export type AppleDevicesByAppleTeamQueryVariables = Exact<{ - accountId: Scalars['ID']; - appleTeamIdentifier: Scalars['String']; -}>; - - -export type AppleDevicesByAppleTeamQuery = ( - { __typename?: 'RootQuery' } - & { appleTeam: ( - { __typename?: 'AppleTeamQuery' } - & { byAppleTeamIdentifier?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & { appleDevices: Array<( - { __typename?: 'AppleDevice' } - & Pick - & { appleTeam: ( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - ) } - & AppleDeviceFragment - )> } - & AppleTeamFragment - )> } - ) } -); - -export type AppleDevicesByTeamIdentifierQueryVariables = Exact<{ - accountName: Scalars['String']; - appleTeamIdentifier: Scalars['String']; -}>; - - -export type AppleDevicesByTeamIdentifierQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { appleTeams: Array<( - { __typename?: 'AppleTeam' } - & Pick - & { appleDevices: Array<( - { __typename?: 'AppleDevice' } - & Pick - )> } - )> } - ) } - ) } -); - -export type AppleDevicesByIdentifierQueryVariables = Exact<{ - accountName: Scalars['String']; - identifier: Scalars['String']; -}>; - - -export type AppleDevicesByIdentifierQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { appleDevices: Array<( - { __typename?: 'AppleDevice' } - & Pick - & { appleTeam: ( - { __typename?: 'AppleTeam' } - & Pick - ) } - )> } - ) } - ) } -); - -export type AppleDistributionCertificateByAppQueryVariables = Exact<{ - projectFullName: Scalars['String']; - appleAppIdentifierId: Scalars['String']; - iosDistributionType: IosDistributionType; -}>; - - -export type AppleDistributionCertificateByAppQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byFullName: ( - { __typename?: 'App' } - & Pick - & { iosAppCredentials: Array<( - { __typename?: 'IosAppCredentials' } - & Pick - & { iosAppBuildCredentialsList: Array<( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & { distributionCertificate?: Maybe<( - { __typename?: 'AppleDistributionCertificate' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )> } - & AppleDistributionCertificateFragment - )> } - )> } - )> } - ) } - ) } -); - -export type AppleDistributionCertificateByAccountQueryVariables = Exact<{ - accountName: Scalars['String']; -}>; - - -export type AppleDistributionCertificateByAccountQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { appleDistributionCertificates: Array<( - { __typename?: 'AppleDistributionCertificate' } - & Pick - & AppleDistributionCertificateFragment - )> } - ) } - ) } -); - -export type AppleProvisioningProfilesByAppQueryVariables = Exact<{ - projectFullName: Scalars['String']; - appleAppIdentifierId: Scalars['String']; - iosDistributionType: IosDistributionType; -}>; - - -export type AppleProvisioningProfilesByAppQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byFullName: ( - { __typename?: 'App' } - & Pick - & { iosAppCredentials: Array<( - { __typename?: 'IosAppCredentials' } - & Pick - & { iosAppBuildCredentialsList: Array<( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & { provisioningProfile?: Maybe<( - { __typename?: 'AppleProvisioningProfile' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )>, appleDevices: Array<( - { __typename?: 'AppleDevice' } - & Pick - & AppleDeviceFragment - )>, appleAppIdentifier: ( - { __typename?: 'AppleAppIdentifier' } - & Pick - & AppleAppIdentifierFragment - ) } - & AppleProvisioningProfileFragment - )> } - )> } - )> } - ) } - ) } -); - -export type ApplePushKeyByAccountQueryVariables = Exact<{ - accountName: Scalars['String']; -}>; - - -export type ApplePushKeyByAccountQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { applePushKeys: Array<( - { __typename?: 'ApplePushKey' } - & Pick - & ApplePushKeyFragment - )> } - ) } - ) } -); - -export type AppleTeamsByAccountNameQueryVariables = Exact<{ - accountName: Scalars['String']; -}>; - - -export type AppleTeamsByAccountNameQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { appleTeams: Array<( - { __typename?: 'AppleTeam' } - & Pick - )> } - ) } - ) } -); - -export type AppleTeamByIdentifierQueryVariables = Exact<{ - accountId: Scalars['ID']; - appleTeamIdentifier: Scalars['String']; -}>; - - -export type AppleTeamByIdentifierQuery = ( - { __typename?: 'RootQuery' } - & { appleTeam: ( - { __typename?: 'AppleTeamQuery' } - & { byAppleTeamIdentifier?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )> } - ) } -); - -export type IosAppBuildCredentialsByAppleAppIdentiferAndDistributionQueryVariables = Exact<{ - projectFullName: Scalars['String']; - appleAppIdentifierId: Scalars['String']; - iosDistributionType: IosDistributionType; -}>; - - -export type IosAppBuildCredentialsByAppleAppIdentiferAndDistributionQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byFullName: ( - { __typename?: 'App' } - & Pick - & { iosAppCredentials: Array<( - { __typename?: 'IosAppCredentials' } - & Pick - & { iosAppBuildCredentialsList: Array<( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & IosAppBuildCredentialsFragment - )> } - )> } - ) } - ) } -); - -export type IosAppCredentialsWithBuildCredentialsByAppIdentifierIdQueryVariables = Exact<{ - projectFullName: Scalars['String']; - appleAppIdentifierId: Scalars['String']; - iosDistributionType?: Maybe; -}>; - - -export type IosAppCredentialsWithBuildCredentialsByAppIdentifierIdQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byFullName: ( - { __typename?: 'App' } - & Pick - & { iosAppCredentials: Array<( - { __typename?: 'IosAppCredentials' } - & Pick - & { iosAppBuildCredentialsList: Array<( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & IosAppBuildCredentialsFragment - )> } - & CommonIosAppCredentialsWithoutBuildCredentialsFragment - )> } - ) } - ) } -); - -export type CommonIosAppCredentialsWithBuildCredentialsByAppIdentifierIdQueryVariables = Exact<{ - projectFullName: Scalars['String']; - appleAppIdentifierId: Scalars['String']; -}>; - - -export type CommonIosAppCredentialsWithBuildCredentialsByAppIdentifierIdQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byFullName: ( - { __typename?: 'App' } - & Pick - & { iosAppCredentials: Array<( - { __typename?: 'IosAppCredentials' } - & Pick - & CommonIosAppCredentialsFragment - )> } - ) } - ) } -); - -export type CreateAppMutationVariables = Exact<{ - appInput: AppInput; -}>; - - -export type CreateAppMutation = ( - { __typename?: 'RootMutation' } - & { app?: Maybe<( - { __typename?: 'AppMutation' } - & { createApp: ( - { __typename?: 'App' } - & Pick - ) } - )> } -); - -export type CreateAndroidBuildMutationVariables = Exact<{ - appId: Scalars['ID']; - job: AndroidJobInput; - metadata?: Maybe; -}>; - - -export type CreateAndroidBuildMutation = ( - { __typename?: 'RootMutation' } - & { build?: Maybe<( - { __typename?: 'BuildMutation' } - & { createAndroidBuild: ( - { __typename?: 'CreateBuildResult' } - & { build: ( - { __typename?: 'Build' } - & Pick - & BuildFragment - ), deprecationInfo?: Maybe<( - { __typename?: 'EASBuildDeprecationInfo' } - & Pick - )> } - ) } - )> } -); - -export type CreateIosBuildMutationVariables = Exact<{ - appId: Scalars['ID']; - job: IosJobInput; - metadata?: Maybe; -}>; - - -export type CreateIosBuildMutation = ( - { __typename?: 'RootMutation' } - & { build?: Maybe<( - { __typename?: 'BuildMutation' } - & { createIosBuild: ( - { __typename?: 'CreateBuildResult' } - & { build: ( - { __typename?: 'Build' } - & Pick - & BuildFragment - ), deprecationInfo?: Maybe<( - { __typename?: 'EASBuildDeprecationInfo' } - & Pick - )> } - ) } - )> } -); - -export type CreateEnvironmentSecretForAccountMutationVariables = Exact<{ - input: CreateEnvironmentSecretInput; - accountId: Scalars['String']; -}>; - - -export type CreateEnvironmentSecretForAccountMutation = ( - { __typename?: 'RootMutation' } - & { environmentSecret: ( - { __typename?: 'EnvironmentSecretMutation' } - & { createEnvironmentSecretForAccount: ( - { __typename?: 'EnvironmentSecret' } - & Pick - & EnvironmentSecretFragment - ) } - ) } -); - -export type CreateEnvironmentSecretForAppMutationVariables = Exact<{ - input: CreateEnvironmentSecretInput; - appId: Scalars['String']; -}>; - - -export type CreateEnvironmentSecretForAppMutation = ( - { __typename?: 'RootMutation' } - & { environmentSecret: ( - { __typename?: 'EnvironmentSecretMutation' } - & { createEnvironmentSecretForApp: ( - { __typename?: 'EnvironmentSecret' } - & Pick - & EnvironmentSecretFragment - ) } - ) } -); - -export type DeleteEnvironmentSecretMutationVariables = Exact<{ - id: Scalars['String']; -}>; - - -export type DeleteEnvironmentSecretMutation = ( - { __typename?: 'RootMutation' } - & { environmentSecret: ( - { __typename?: 'EnvironmentSecretMutation' } - & { deleteEnvironmentSecret: ( - { __typename?: 'DeleteEnvironmentSecretResult' } - & Pick - ) } - ) } -); - -export type CreateKeystoreGenerationUrlMutationVariables = Exact<{ [key: string]: never; }>; - - -export type CreateKeystoreGenerationUrlMutation = ( - { __typename?: 'RootMutation' } - & { keystoreGenerationUrl: ( - { __typename?: 'KeystoreGenerationUrlMutation' } - & { createKeystoreGenerationUrl: ( - { __typename?: 'KeystoreGenerationUrl' } - & Pick - ) } - ) } -); - -export type GetSignedUploadMutationVariables = Exact<{ - contentTypes: Array; -}>; - - -export type GetSignedUploadMutation = ( - { __typename?: 'RootMutation' } - & { asset: ( - { __typename?: 'AssetMutation' } - & { getSignedAssetUploadSpecifications?: Maybe<( - { __typename?: 'GetSignedAssetUploadSpecificationsResult' } - & Pick - )> } - ) } -); - -export type UpdatePublishMutationVariables = Exact<{ - publishUpdateGroupsInput: Array; -}>; - - -export type UpdatePublishMutation = ( - { __typename?: 'RootMutation' } - & { updateBranch: ( - { __typename?: 'UpdateBranchMutation' } - & { publishUpdateGroups: Array<( - { __typename?: 'Update' } - & Pick - )> } - ) } -); - -export type CreateAndroidSubmissionMutationVariables = Exact<{ - appId: Scalars['ID']; - config: AndroidSubmissionConfigInput; - submittedBuildId?: Maybe; -}>; - - -export type CreateAndroidSubmissionMutation = ( - { __typename?: 'RootMutation' } - & { submission: ( - { __typename?: 'SubmissionMutation' } - & { createAndroidSubmission: ( - { __typename?: 'CreateSubmissionResult' } - & { submission: ( - { __typename?: 'Submission' } - & Pick - & SubmissionFragment - ) } - ) } - ) } -); - -export type CreateIosSubmissionMutationVariables = Exact<{ - appId: Scalars['ID']; - config: IosSubmissionConfigInput; - submittedBuildId?: Maybe; -}>; - - -export type CreateIosSubmissionMutation = ( - { __typename?: 'RootMutation' } - & { submission: ( - { __typename?: 'SubmissionMutation' } - & { createIosSubmission: ( - { __typename?: 'CreateSubmissionResult' } - & { submission: ( - { __typename?: 'Submission' } - & Pick - & SubmissionFragment - ) } - ) } - ) } -); - -export type CreateUploadSessionMutationVariables = Exact<{ - type: UploadSessionType; -}>; - - -export type CreateUploadSessionMutation = ( - { __typename?: 'RootMutation' } - & { uploadSession: ( - { __typename?: 'UploadSession' } - & Pick - ) } -); - -export type CreateWebhookMutationVariables = Exact<{ - appId: Scalars['String']; - webhookInput: WebhookInput; -}>; - - -export type CreateWebhookMutation = ( - { __typename?: 'RootMutation' } - & { webhook: ( - { __typename?: 'WebhookMutation' } - & { createWebhook: ( - { __typename?: 'Webhook' } - & Pick - & WebhookFragment - ) } - ) } -); - -export type UpdateWebhookMutationVariables = Exact<{ - webhookId: Scalars['ID']; - webhookInput: WebhookInput; -}>; - - -export type UpdateWebhookMutation = ( - { __typename?: 'RootMutation' } - & { webhook: ( - { __typename?: 'WebhookMutation' } - & { updateWebhook: ( - { __typename?: 'Webhook' } - & Pick - & WebhookFragment - ) } - ) } -); - -export type DeleteWebhookMutationVariables = Exact<{ - webhookId: Scalars['ID']; -}>; - - -export type DeleteWebhookMutation = ( - { __typename?: 'RootMutation' } - & { webhook: ( - { __typename?: 'WebhookMutation' } - & { deleteWebhook: ( - { __typename?: 'DeleteWebhookResult' } - & Pick - ) } - ) } -); - -export type BuildsByIdQueryVariables = Exact<{ - buildId: Scalars['ID']; -}>; - - -export type BuildsByIdQuery = ( - { __typename?: 'RootQuery' } - & { builds: ( - { __typename?: 'BuildQuery' } - & { byId: ( - { __typename?: 'Build' } - & Pick - & BuildFragment - ) } - ) } -); - -export type GetAllBuildsForAppQueryVariables = Exact<{ - appId: Scalars['String']; - offset: Scalars['Int']; - limit: Scalars['Int']; - filter?: Maybe; -}>; - - -export type GetAllBuildsForAppQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { builds: Array<( - { __typename?: 'Build' } - & Pick - & BuildFragment - )> } - ) } - ) } -); - -export type EnvironmentSecretsByAccountNameQueryVariables = Exact<{ - accountName: Scalars['String']; -}>; - - -export type EnvironmentSecretsByAccountNameQuery = ( - { __typename?: 'RootQuery' } - & { account: ( - { __typename?: 'AccountQuery' } - & { byName: ( - { __typename?: 'Account' } - & Pick - & { environmentSecrets: Array<( - { __typename?: 'EnvironmentSecret' } - & Pick - & EnvironmentSecretFragment - )> } - ) } - ) } -); - -export type EnvironmentSecretsByAppIdQueryVariables = Exact<{ - appId: Scalars['String']; -}>; - - -export type EnvironmentSecretsByAppIdQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { environmentSecrets: Array<( - { __typename?: 'EnvironmentSecret' } - & Pick - & EnvironmentSecretFragment - )> } - ) } - ) } -); - -export type ProjectByUsernameAndSlugQueryVariables = Exact<{ - username: Scalars['String']; - slug: Scalars['String']; -}>; - - -export type ProjectByUsernameAndSlugQuery = ( - { __typename?: 'RootQuery' } - & { project: ( - { __typename?: 'ProjectQuery' } - & { byUsernameAndSlug: ( - { __typename?: 'Snack' } - & Pick - ) | ( - { __typename?: 'App' } - & Pick - ) } - ) } -); - -export type GetAssetMetadataQueryVariables = Exact<{ - storageKeys: Array; -}>; - - -export type GetAssetMetadataQuery = ( - { __typename?: 'RootQuery' } - & { asset: ( - { __typename?: 'AssetQuery' } - & { metadata: Array<( - { __typename?: 'AssetMetadataResult' } - & Pick - )> } - ) } -); - -export type SubmissionsByIdQueryVariables = Exact<{ - submissionId: Scalars['ID']; -}>; - - -export type SubmissionsByIdQuery = ( - { __typename?: 'RootQuery' } - & { submissions: ( - { __typename?: 'SubmissionQuery' } - & { byId: ( - { __typename?: 'Submission' } - & Pick - & SubmissionFragment - ) } - ) } -); - -export type GetAllSubmissionsForAppQueryVariables = Exact<{ - appId: Scalars['String']; - offset: Scalars['Int']; - limit: Scalars['Int']; - status?: Maybe; - platform?: Maybe; -}>; - - -export type GetAllSubmissionsForAppQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { submissions: Array<( - { __typename?: 'Submission' } - & Pick - & SubmissionFragment - )> } - ) } - ) } -); - -export type CurrentUserQueryVariables = Exact<{ [key: string]: never; }>; - - -export type CurrentUserQuery = ( - { __typename?: 'RootQuery' } - & { meActor?: Maybe<( - { __typename: 'User' } - & Pick - & { accounts: Array<( - { __typename?: 'Account' } - & Pick - )> } - ) | ( - { __typename: 'Robot' } - & Pick - & { accounts: Array<( - { __typename?: 'Account' } - & Pick - )> } - )> } -); - -export type WebhooksByAppIdQueryVariables = Exact<{ - appId: Scalars['String']; - webhookFilter?: Maybe; -}>; - - -export type WebhooksByAppIdQuery = ( - { __typename?: 'RootQuery' } - & { app: ( - { __typename?: 'AppQuery' } - & { byId: ( - { __typename?: 'App' } - & Pick - & { webhooks: Array<( - { __typename?: 'Webhook' } - & Pick - & WebhookFragment - )> } - ) } - ) } -); - -export type WebhookByIdQueryVariables = Exact<{ - webhookId: Scalars['ID']; -}>; - - -export type WebhookByIdQuery = ( - { __typename?: 'RootQuery' } - & { webhook: ( - { __typename?: 'WebhookQuery' } - & { byId: ( - { __typename?: 'Webhook' } - & Pick - & WebhookFragment - ) } - ) } -); - -export type AppFragment = ( - { __typename?: 'App' } - & Pick -); - -export type BuildFragment = ( - { __typename?: 'Build' } - & Pick - & { error?: Maybe<( - { __typename?: 'BuildError' } - & Pick - )>, artifacts?: Maybe<( - { __typename?: 'BuildArtifacts' } - & Pick - )>, initiatingActor?: Maybe<( - { __typename: 'User' } - & Pick - ) | ( - { __typename: 'Robot' } - & Pick - )>, project: ( - { __typename: 'Snack' } - & Pick - ) | ( - { __typename: 'App' } - & Pick - & { ownerAccount: ( - { __typename?: 'Account' } - & Pick - ) } - ) } -); - -export type EnvironmentSecretFragment = ( - { __typename?: 'EnvironmentSecret' } - & Pick -); - -export type SubmissionFragment = ( - { __typename?: 'Submission' } - & Pick - & { app: ( - { __typename?: 'App' } - & Pick - & { ownerAccount: ( - { __typename?: 'Account' } - & Pick - ) } - ), androidConfig?: Maybe<( - { __typename?: 'AndroidSubmissionConfig' } - & Pick - )>, iosConfig?: Maybe<( - { __typename?: 'IosSubmissionConfig' } - & Pick - )>, error?: Maybe<( - { __typename?: 'SubmissionError' } - & Pick - )> } -); - -export type UpdateBranchFragment = ( - { __typename?: 'UpdateBranch' } - & Pick - & { updates: Array<( - { __typename?: 'Update' } - & Pick - & { actor?: Maybe<( - { __typename: 'User' } - & Pick - ) | ( - { __typename: 'Robot' } - & Pick - )> } - )> } -); - -export type WebhookFragment = ( - { __typename?: 'Webhook' } - & Pick -); - -export type AndroidAppBuildCredentialsFragment = ( - { __typename?: 'AndroidAppBuildCredentials' } - & Pick - & { androidKeystore?: Maybe<( - { __typename?: 'AndroidKeystore' } - & Pick - & AndroidKeystoreFragment - )> } -); - -export type CommonAndroidAppCredentialsFragment = ( - { __typename?: 'AndroidAppCredentials' } - & Pick - & { app: ( - { __typename?: 'App' } - & Pick - & AppFragment - ), androidFcm?: Maybe<( - { __typename?: 'AndroidFcm' } - & Pick - & AndroidFcmFragment - )>, googleServiceAccountKeyForSubmissions?: Maybe<( - { __typename?: 'GoogleServiceAccountKey' } - & Pick - & GoogleServiceAccountKeyFragment - )>, androidAppBuildCredentialsList: Array<( - { __typename?: 'AndroidAppBuildCredentials' } - & Pick - & AndroidAppBuildCredentialsFragment - )> } -); - -export type AndroidFcmFragment = ( - { __typename?: 'AndroidFcm' } - & Pick - & { snippet: ( - { __typename?: 'FcmSnippetLegacy' } - & Pick - ) | ( - { __typename?: 'FcmSnippetV1' } - & Pick - ) } -); - -export type AndroidKeystoreFragment = ( - { __typename?: 'AndroidKeystore' } - & Pick -); - -export type AppStoreConnectApiKeyFragment = ( - { __typename?: 'AppStoreConnectApiKey' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )> } -); - -export type AppleAppIdentifierFragment = ( - { __typename?: 'AppleAppIdentifier' } - & Pick -); - -export type AppleDeviceFragment = ( - { __typename?: 'AppleDevice' } - & Pick -); - -export type AppleDeviceRegistrationRequestFragment = ( - { __typename?: 'AppleDeviceRegistrationRequest' } - & Pick -); - -export type AppleDistributionCertificateFragment = ( - { __typename?: 'AppleDistributionCertificate' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )>, iosAppBuildCredentialsList: Array<( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & { iosAppCredentials: ( - { __typename?: 'IosAppCredentials' } - & Pick - & { app: ( - { __typename?: 'App' } - & Pick - & AppFragment - ), appleAppIdentifier: ( - { __typename?: 'AppleAppIdentifier' } - & Pick - & AppleAppIdentifierFragment - ) } - ), provisioningProfile?: Maybe<( - { __typename?: 'AppleProvisioningProfile' } - & Pick - & AppleProvisioningProfileIdentifiersFragment - )> } - )> } -); - -export type AppleProvisioningProfileFragment = ( - { __typename?: 'AppleProvisioningProfile' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )>, appleDevices: Array<( - { __typename?: 'AppleDevice' } - & Pick - & AppleDeviceFragment - )> } -); - -export type AppleProvisioningProfileIdentifiersFragment = ( - { __typename?: 'AppleProvisioningProfile' } - & Pick -); - -export type ApplePushKeyFragment = ( - { __typename?: 'ApplePushKey' } - & Pick - & { appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )>, iosAppCredentialsList: Array<( - { __typename?: 'IosAppCredentials' } - & Pick - & { app: ( - { __typename?: 'App' } - & Pick - & AppFragment - ), appleAppIdentifier: ( - { __typename?: 'AppleAppIdentifier' } - & Pick - & AppleAppIdentifierFragment - ) } - )> } -); - -export type AppleTeamFragment = ( - { __typename?: 'AppleTeam' } - & Pick -); - -export type GoogleServiceAccountKeyFragment = ( - { __typename?: 'GoogleServiceAccountKey' } - & Pick -); - -export type IosAppBuildCredentialsFragment = ( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & { distributionCertificate?: Maybe<( - { __typename?: 'AppleDistributionCertificate' } - & Pick - & AppleDistributionCertificateFragment - )>, provisioningProfile?: Maybe<( - { __typename?: 'AppleProvisioningProfile' } - & Pick - & AppleProvisioningProfileFragment - )> } -); - -export type CommonIosAppCredentialsWithoutBuildCredentialsFragment = ( - { __typename?: 'IosAppCredentials' } - & Pick - & { app: ( - { __typename?: 'App' } - & Pick - & AppFragment - ), appleTeam?: Maybe<( - { __typename?: 'AppleTeam' } - & Pick - & AppleTeamFragment - )>, appleAppIdentifier: ( - { __typename?: 'AppleAppIdentifier' } - & Pick - & AppleAppIdentifierFragment - ), pushKey?: Maybe<( - { __typename?: 'ApplePushKey' } - & Pick - & ApplePushKeyFragment - )>, appStoreConnectApiKeyForSubmissions?: Maybe<( - { __typename?: 'AppStoreConnectApiKey' } - & Pick - & AppStoreConnectApiKeyFragment - )> } -); - -export type CommonIosAppCredentialsFragment = ( - { __typename?: 'IosAppCredentials' } - & Pick - & { iosAppBuildCredentialsList: Array<( - { __typename?: 'IosAppBuildCredentials' } - & Pick - & IosAppBuildCredentialsFragment - )> } - & CommonIosAppCredentialsWithoutBuildCredentialsFragment -); From d46a78d34f1a1a48b6f206ae3403ad36f9920335 Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Wed, 26 Jan 2022 19:32:02 -0700 Subject: [PATCH 04/14] Update index-test.ts --- packages/expo/e2e/__tests__/index-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo/e2e/__tests__/index-test.ts b/packages/expo/e2e/__tests__/index-test.ts index c6bf9cfaa33a2..efb926fbc64aa 100644 --- a/packages/expo/e2e/__tests__/index-test.ts +++ b/packages/expo/e2e/__tests__/index-test.ts @@ -29,7 +29,7 @@ it('runs `npx expo --help`', async () => { $ npx expo Available commands - config, prebuild + config, login, logout, prebuild, register, whoami Options --version, -v Version number From 5ef009aa3efba819a1c23441e5cb20af6dc14ccd Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Wed, 26 Jan 2022 19:49:48 -0700 Subject: [PATCH 05/14] Added module loading tests drop amplitude Update rudderstackClient.ts --- packages/expo/cli/utils/__tests__/api-test.ts | 3 +- .../cli/utils/analytics/rudderstackClient.ts | 36 ++++++------------- packages/expo/cli/utils/api.ts | 27 +++++++++++++- packages/expo/cli/utils/env.ts | 3 ++ packages/expo/cli/utils/errors.ts | 35 +++--------------- packages/expo/cli/utils/user/UserSettings.ts | 2 -- .../cli/utils/user/__tests__/actions-test.ts | 9 +++-- packages/expo/cli/utils/user/actions.ts | 3 +- packages/expo/e2e/__tests__/login-test.ts | 18 +++++++++- packages/expo/e2e/__tests__/logout-test.ts | 18 +++++++++- packages/expo/e2e/__tests__/register-test.ts | 18 +++++++++- packages/expo/e2e/__tests__/whoami-test.ts | 18 +++++++++- 12 files changed, 122 insertions(+), 68 deletions(-) diff --git a/packages/expo/cli/utils/__tests__/api-test.ts b/packages/expo/cli/utils/__tests__/api-test.ts index 5555985774feb..6978085c4ef17 100644 --- a/packages/expo/cli/utils/__tests__/api-test.ts +++ b/packages/expo/cli/utils/__tests__/api-test.ts @@ -2,8 +2,7 @@ import assert from 'assert'; import { RequestError } from 'got/dist/source'; import nock from 'nock'; -import { apiClient, getExpoApiBaseUrl } from '../api'; -import { ApiV2Error } from '../errors'; +import { ApiV2Error, apiClient, getExpoApiBaseUrl } from '../api'; it('converts Expo APIv2 error to ApiV2Error', async () => { nock(getExpoApiBaseUrl()) diff --git a/packages/expo/cli/utils/analytics/rudderstackClient.ts b/packages/expo/cli/utils/analytics/rudderstackClient.ts index 9dc753e84a9f0..848710770dca4 100644 --- a/packages/expo/cli/utils/analytics/rudderstackClient.ts +++ b/packages/expo/cli/utils/analytics/rudderstackClient.ts @@ -3,6 +3,7 @@ import os from 'os'; import { URL } from 'url'; import { v4 as uuidv4 } from 'uuid'; +import { EXPO_LOCAL, EXPO_STAGING, EXPO_NO_TELEMETRY } from '../env'; import UserSettings from '../user/UserSettings'; const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = { @@ -12,7 +13,7 @@ const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = { }; let rudderstackClient: RudderAnalytics | null = null; -let userIdentified = false; +let identifier = false; let identifyData: { userId: string; deviceId: string; @@ -20,26 +21,9 @@ let identifyData: { } | null = null; export async function initAsync(): Promise { - // TODO: remove after some time - const amplitudeEnabled = await UserSettings.getAsync('amplitudeEnabled', null); - if (amplitudeEnabled !== null) { - await UserSettings.setAsync('analyticsEnabled', amplitudeEnabled); - await UserSettings.deleteKeyAsync('amplitudeEnabled'); - } - const amplitudeDeviceId = await UserSettings.getAsync('amplitudeDeviceId', null); - if (amplitudeDeviceId !== null) { - await UserSettings.setAsync('analyticsDeviceId', amplitudeDeviceId); - await UserSettings.deleteKeyAsync('amplitudeDeviceId'); - } - // TODO: cut here - if (process.env.DISABLE_EAS_ANALYTICS) { - await UserSettings.setAsync('analyticsEnabled', false); - } - - const analyticsEnabled = await UserSettings.getAsync('analyticsEnabled', true); - if (analyticsEnabled) { + if (EXPO_NO_TELEMETRY) { const config = - process.env.EXPO_STAGING || process.env.EXPO_LOCAL + EXPO_STAGING || EXPO_LOCAL ? { // staging environment rudderstackWriteKey: '1wpX20Da4ltFGSXbPFYUL00Chb7', @@ -74,7 +58,7 @@ export async function setUserDataAsync(userId: string, traits: Record { @@ -87,7 +71,7 @@ export function logEvent(name: string, properties: Record = {}): vo if (!rudderstackClient) { return; } - ensureUserIdentified(); + ensureIdentified(); const { userId, deviceId } = identifyData ?? {}; const commonEventProperties = { source_version: process.env.__EXPO_VERSION, source: 'expo' }; @@ -101,8 +85,8 @@ export function logEvent(name: string, properties: Record = {}): vo }); } -function ensureUserIdentified(): void { - if (!rudderstackClient || userIdentified || !identifyData) { +function ensureIdentified(): void { + if (!rudderstackClient || identifier || !identifyData) { return; } @@ -111,7 +95,7 @@ function ensureUserIdentified(): void { anonymousId: identifyData.deviceId, traits: identifyData.traits, }); - userIdentified = true; + identifier = true; } function getRudderStackContext(): Record { @@ -119,7 +103,7 @@ function getRudderStackContext(): Record { return { os: { name: platform, version: os.release() }, device: { type: platform, model: platform }, - app: { name: 'expo', version: process.env.__EXPO_VERSION ?? undefined }, + app: { name: 'expo', version: process.env.__EXPO_VERSION }, }; } diff --git a/packages/expo/cli/utils/api.ts b/packages/expo/cli/utils/api.ts index bc887be04b374..a74d5c41ddabd 100644 --- a/packages/expo/cli/utils/api.ts +++ b/packages/expo/cli/utils/api.ts @@ -1,9 +1,34 @@ +import { JSONValue } from '@expo/json-file'; import got, { HTTPError, NormalizedOptions, RequestError } from 'got'; import { EXPO_LOCAL, EXPO_STAGING } from './env'; -import { ApiV2Error } from './errors'; import { getAccessToken, getSessionSecret } from './user/sessionStorage'; +export class ApiV2Error extends RequestError { + readonly name = 'ApiV2Error'; + readonly expoApiV2ErrorCode: string; + readonly expoApiV2ErrorDetails?: JSONValue; + readonly expoApiV2ErrorServerStack?: string; + readonly expoApiV2ErrorMetadata?: object; + + constructor( + originalError: HTTPError, + response: { + message: string; + code: string; + stack?: string; + details?: JSONValue; + metadata?: object; + } + ) { + super(response.message, originalError, originalError.request); + this.expoApiV2ErrorCode = response.code; + this.expoApiV2ErrorDetails = response.details; + this.expoApiV2ErrorServerStack = response.stack; + this.expoApiV2ErrorMetadata = response.metadata; + } +} + export const apiClient = got.extend({ prefixUrl: getExpoApiBaseUrl() + '/v2/', hooks: { diff --git a/packages/expo/cli/utils/env.ts b/packages/expo/cli/utils/env.ts index 497dc62e6eda1..d08a58925a1f1 100644 --- a/packages/expo/cli/utils/env.ts +++ b/packages/expo/cli/utils/env.ts @@ -16,3 +16,6 @@ export const EXPO_LOCAL = boolish('EXPO_LOCAL', false); /** Is running in non-interactive CI mode */ export const CI = boolish('CI', false); + +/** Disable telemetry (analytics) */ +export const EXPO_NO_TELEMETRY = boolish('EXPO_NO_TELEMETRY', false); diff --git a/packages/expo/cli/utils/errors.ts b/packages/expo/cli/utils/errors.ts index 1110e6a7e7467..d3c9d0c94a246 100644 --- a/packages/expo/cli/utils/errors.ts +++ b/packages/expo/cli/utils/errors.ts @@ -1,35 +1,7 @@ -import { JSONValue } from '@expo/json-file'; import { AssertionError } from 'assert'; import chalk from 'chalk'; -import { HTTPError, RequestError } from 'got/dist/source'; import { exit } from '../log'; -import { EXPO_DEBUG } from './env'; - -export class ApiV2Error extends RequestError { - readonly name = 'ApiV2Error'; - readonly expoApiV2ErrorCode: string; - readonly expoApiV2ErrorDetails?: JSONValue; - readonly expoApiV2ErrorServerStack?: string; - readonly expoApiV2ErrorMetadata?: object; - - constructor( - originalError: HTTPError, - response: { - message: string; - code: string; - stack?: string; - details?: JSONValue; - metadata?: object; - } - ) { - super(response.message, originalError, originalError.request); - this.expoApiV2ErrorCode = response.code; - this.expoApiV2ErrorDetails = response.details; - this.expoApiV2ErrorServerStack = response.stack; - this.expoApiV2ErrorMetadata = response.metadata; - } -} const ERROR_PREFIX = 'Error: '; @@ -82,10 +54,13 @@ export function logCmdError(error: Error): never { } else if ( error instanceof CommandError || error instanceof AssertionError || - error instanceof ApiV2Error + error.name === 'ApiV2Error' ) { // Print the stack trace in debug mode only. - exit(chalk.red(error.toString()) + (EXPO_DEBUG ? '\n' + chalk.gray(error.stack) : '')); + exit( + chalk.red(error.toString()) + + (require('./env').EXPO_DEBUG ? '\n' + chalk.gray(error.stack) : '') + ); } exit(chalk.red(error.toString()) + '\n' + chalk.gray(error.stack)); diff --git a/packages/expo/cli/utils/user/UserSettings.ts b/packages/expo/cli/utils/user/UserSettings.ts index 7c00cc56a8b13..b8d42a6b68cd1 100644 --- a/packages/expo/cli/utils/user/UserSettings.ts +++ b/packages/expo/cli/utils/user/UserSettings.ts @@ -27,8 +27,6 @@ const SETTINGS_FILE_PATH = path.join(getConfigDirectory(), 'user-settings.json') export type UserSettingsData = { appleId?: string; - amplitudeDeviceId?: string; - amplitudeEnabled?: boolean; analyticsDeviceId?: string; analyticsEnabled?: boolean; }; diff --git a/packages/expo/cli/utils/user/__tests__/actions-test.ts b/packages/expo/cli/utils/user/__tests__/actions-test.ts index 877487f9c0dba..3ede956dabf0e 100644 --- a/packages/expo/cli/utils/user/__tests__/actions-test.ts +++ b/packages/expo/cli/utils/user/__tests__/actions-test.ts @@ -1,11 +1,16 @@ -import { ApiV2Error } from '../../errors'; +import { ApiV2Error } from '../../api'; import { promptAsync } from '../../prompts'; import { ensureActorHasUsername, showLoginPromptAsync } from '../actions'; import { retryUsernamePasswordAuthWithOTPAsync, UserSecondFactorDeviceMethod } from '../otp'; import { Actor, loginAsync } from '../user'; jest.mock('../../prompts'); -jest.mock('../../api'); +jest.mock('../../api', () => { + const { ApiV2Error } = jest.requireActual('../../api'); + return { + ApiV2Error, + }; +}); jest.mock('../otp'); jest.mock('../user'); diff --git a/packages/expo/cli/utils/user/actions.ts b/packages/expo/cli/utils/user/actions.ts index a949122ace118..cdb2eee69e34c 100644 --- a/packages/expo/cli/utils/user/actions.ts +++ b/packages/expo/cli/utils/user/actions.ts @@ -2,7 +2,8 @@ import assert from 'assert'; import chalk from 'chalk'; import * as Log from '../../log'; -import { ApiV2Error, CommandError } from '../errors'; +import { ApiV2Error } from '../api'; +import { CommandError } from '../errors'; import { learnMore } from '../link'; import promptAsync, { Question } from '../prompts'; import { retryUsernamePasswordAuthWithOTPAsync } from './otp'; diff --git a/packages/expo/e2e/__tests__/login-test.ts b/packages/expo/e2e/__tests__/login-test.ts index af04ab3a2e256..0ecfa0df18527 100644 --- a/packages/expo/e2e/__tests__/login-test.ts +++ b/packages/expo/e2e/__tests__/login-test.ts @@ -1,7 +1,7 @@ /* eslint-env jest */ import fs from 'fs/promises'; -import { execute, projectRoot } from './utils'; +import { execute, getLoadedModulesAsync, projectRoot } from './utils'; const originalForceColor = process.env.FORCE_COLOR; const originalCI = process.env.CI; @@ -15,6 +15,22 @@ afterAll(() => { process.env.CI = originalCI; }); +it('loads expected modules by default', async () => { + const modules = await getLoadedModulesAsync(`require('../../build-cli/cli/login');`); + expect(modules).toStrictEqual([ + 'node_modules/ansi-styles/index.js', + 'node_modules/arg/index.js', + 'node_modules/chalk/source/index.js', + 'node_modules/chalk/source/util.js', + 'node_modules/has-flag/index.js', + 'node_modules/supports-color/index.js', + 'packages/expo/build-cli/cli/log.js', + 'packages/expo/build-cli/cli/login/index.js', + 'packages/expo/build-cli/cli/utils/args.js', + 'packages/expo/build-cli/cli/utils/errors.js', + ]); +}); + it('runs `npx expo login --help`', async () => { const results = await execute('login', '--help'); expect(results.stdout).toMatchInlineSnapshot(` diff --git a/packages/expo/e2e/__tests__/logout-test.ts b/packages/expo/e2e/__tests__/logout-test.ts index 7e914b5cdf683..f7d9d2185223e 100644 --- a/packages/expo/e2e/__tests__/logout-test.ts +++ b/packages/expo/e2e/__tests__/logout-test.ts @@ -1,7 +1,7 @@ /* eslint-env jest */ import fs from 'fs/promises'; -import { execute, projectRoot } from './utils'; +import { execute, getLoadedModulesAsync, projectRoot } from './utils'; const originalForceColor = process.env.FORCE_COLOR; @@ -13,6 +13,22 @@ afterAll(() => { process.env.FORCE_COLOR = originalForceColor; }); +it('loads expected modules by default', async () => { + const modules = await getLoadedModulesAsync(`require('../../build-cli/cli/logout');`); + expect(modules).toStrictEqual([ + 'node_modules/ansi-styles/index.js', + 'node_modules/arg/index.js', + 'node_modules/chalk/source/index.js', + 'node_modules/chalk/source/util.js', + 'node_modules/has-flag/index.js', + 'node_modules/supports-color/index.js', + 'packages/expo/build-cli/cli/log.js', + 'packages/expo/build-cli/cli/logout/index.js', + 'packages/expo/build-cli/cli/utils/args.js', + 'packages/expo/build-cli/cli/utils/errors.js', + ]); +}); + it('runs `npx expo logout --help`', async () => { const results = await execute('logout', '--help'); expect(results.stdout).toMatchInlineSnapshot(` diff --git a/packages/expo/e2e/__tests__/register-test.ts b/packages/expo/e2e/__tests__/register-test.ts index e3192e9cf3593..3c19063827714 100644 --- a/packages/expo/e2e/__tests__/register-test.ts +++ b/packages/expo/e2e/__tests__/register-test.ts @@ -1,7 +1,7 @@ /* eslint-env jest */ import fs from 'fs/promises'; -import { execute, projectRoot } from './utils'; +import { execute, getLoadedModulesAsync, projectRoot } from './utils'; const originalForceColor = process.env.FORCE_COLOR; const originalCI = process.env.CI; @@ -15,6 +15,22 @@ afterAll(() => { process.env.CI = originalCI; }); +it('loads expected modules by default', async () => { + const modules = await getLoadedModulesAsync(`require('../../build-cli/cli/register');`); + expect(modules).toStrictEqual([ + 'node_modules/ansi-styles/index.js', + 'node_modules/arg/index.js', + 'node_modules/chalk/source/index.js', + 'node_modules/chalk/source/util.js', + 'node_modules/has-flag/index.js', + 'node_modules/supports-color/index.js', + 'packages/expo/build-cli/cli/log.js', + 'packages/expo/build-cli/cli/register/index.js', + 'packages/expo/build-cli/cli/utils/args.js', + 'packages/expo/build-cli/cli/utils/errors.js', + ]); +}); + it('runs `npx expo register --help`', async () => { const results = await execute('register', '--help'); expect(results.stdout).toMatchInlineSnapshot(` diff --git a/packages/expo/e2e/__tests__/whoami-test.ts b/packages/expo/e2e/__tests__/whoami-test.ts index 6ff100b972d1e..4715ab935d63b 100644 --- a/packages/expo/e2e/__tests__/whoami-test.ts +++ b/packages/expo/e2e/__tests__/whoami-test.ts @@ -2,7 +2,7 @@ import fs from 'fs/promises'; import os from 'os'; -import { execute, projectRoot } from './utils'; +import { execute, getLoadedModulesAsync, projectRoot } from './utils'; const originalForceColor = process.env.FORCE_COLOR; beforeAll(async () => { @@ -13,6 +13,22 @@ afterAll(() => { process.env.FORCE_COLOR = originalForceColor; }); +it('loads expected modules by default', async () => { + const modules = await getLoadedModulesAsync(`require('../../build-cli/cli/whoami');`); + expect(modules).toStrictEqual([ + 'node_modules/ansi-styles/index.js', + 'node_modules/arg/index.js', + 'node_modules/chalk/source/index.js', + 'node_modules/chalk/source/util.js', + 'node_modules/has-flag/index.js', + 'node_modules/supports-color/index.js', + 'packages/expo/build-cli/cli/log.js', + 'packages/expo/build-cli/cli/utils/args.js', + 'packages/expo/build-cli/cli/utils/errors.js', + 'packages/expo/build-cli/cli/whoami/index.js', + ]); +}); + it('runs `npx expo whoami --help`', async () => { const results = await execute('whoami', '--help'); expect(results.stdout).toMatchInlineSnapshot(` From c09589cb845d12ff39c5baefe09ebcf37d03fbdb Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Thu, 27 Jan 2022 10:13:43 -0700 Subject: [PATCH 06/14] Apply suggestions from code review Co-authored-by: James Ide Co-authored-by: Cedric van Putten --- packages/expo/cli/login/index.ts | 2 +- packages/expo/cli/logout/index.ts | 2 +- packages/expo/cli/utils/analytics/rudderstackClient.ts | 2 +- packages/expo/cli/utils/graphql/client.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/expo/cli/login/index.ts b/packages/expo/cli/login/index.ts index 2ae3c2f06b9b8..53ba828a3c881 100644 --- a/packages/expo/cli/login/index.ts +++ b/packages/expo/cli/login/index.ts @@ -26,7 +26,7 @@ export const expoLogin: Command = async (argv) => { Log.exit( chalk` {bold Description} - Login to an Expo account + Log in to an Expo account {bold Usage} $ npx expo login diff --git a/packages/expo/cli/logout/index.ts b/packages/expo/cli/logout/index.ts index 603fd81384a6f..2c99b122a92b3 100644 --- a/packages/expo/cli/logout/index.ts +++ b/packages/expo/cli/logout/index.ts @@ -21,7 +21,7 @@ export const expoLogout: Command = async (argv) => { Log.exit( chalk` {bold Description} - Logout of an Expo account + Log out of an Expo account {bold Usage} $ npx expo logout diff --git a/packages/expo/cli/utils/analytics/rudderstackClient.ts b/packages/expo/cli/utils/analytics/rudderstackClient.ts index 848710770dca4..d2be3edcb0441 100644 --- a/packages/expo/cli/utils/analytics/rudderstackClient.ts +++ b/packages/expo/cli/utils/analytics/rudderstackClient.ts @@ -108,5 +108,5 @@ function getRudderStackContext(): Record { } export enum AnalyticsEvent { - ACTION = 'action', // generic event type which is used to determine the 'daily active user' stat, include an `action: eas ${subcommand}` property inside of the event properties object + ACTION = 'action', // generic event type which is used to determine the 'daily active user' stat, include an `action: expo ${subcommand}` property inside of the event properties object } diff --git a/packages/expo/cli/utils/graphql/client.ts b/packages/expo/cli/utils/graphql/client.ts index 6617c775ea05e..7c23eec5b30a7 100644 --- a/packages/expo/cli/utils/graphql/client.ts +++ b/packages/expo/cli/utils/graphql/client.ts @@ -81,8 +81,8 @@ export async function withErrorHandlingAsync(promise: Promise Date: Thu, 27 Jan 2022 10:15:25 -0700 Subject: [PATCH 07/14] clean up lazy load analytics Update prompts.ts Update actions.ts --- packages/expo/cli/register/registerAsync.ts | 16 +++-- .../cli/utils/analytics/rudderstackClient.ts | 71 ++++++++----------- packages/expo/cli/utils/api.ts | 20 ------ packages/expo/cli/utils/prompts.ts | 1 + packages/expo/cli/utils/user/actions.ts | 5 +- 5 files changed, 41 insertions(+), 72 deletions(-) diff --git a/packages/expo/cli/register/registerAsync.ts b/packages/expo/cli/register/registerAsync.ts index 29d57613aac45..a17cffec291df 100644 --- a/packages/expo/cli/register/registerAsync.ts +++ b/packages/expo/cli/register/registerAsync.ts @@ -2,27 +2,31 @@ import openBrowserAsync from 'better-opn'; import { CI } from '../utils/env'; import { CommandError } from '../utils/errors'; +import { learnMore } from '../utils/link'; import { ora } from '../utils/ora'; export async function registerAsync() { - const REGISTRATION_URL = `https://expo.dev/signup`; if (CI) { throw new CommandError( 'NON_INTERACTIVE', - `Cannot register an account in CI. Register an account at: ${REGISTRATION_URL}` + `Cannot register an account in CI. Use the EXPO_TOKEN environment variable to authenticate in CI (${learnMore( + 'https://docs.expo.dev/accounts/programmatic-access/' + )})` ); } - const spinner = ora(`Opening ${REGISTRATION_URL}`).start(); + const registrationUrl = `https://expo.dev/signup`; + + const spinner = ora(`Opening ${registrationUrl}`).start(); try { - const opened = openBrowserAsync(REGISTRATION_URL); + const opened = openBrowserAsync(registrationUrl); if (opened) { - spinner.succeed(`Opened ${REGISTRATION_URL}`); + spinner.succeed(`Opened ${registrationUrl}`); } return; } catch (error) { - spinner.fail(`Unable to open a web browser. Register an account at: ${REGISTRATION_URL}`); + spinner.fail(`Unable to open a web browser. Register an account at: ${registrationUrl}`); throw error; } } diff --git a/packages/expo/cli/utils/analytics/rudderstackClient.ts b/packages/expo/cli/utils/analytics/rudderstackClient.ts index d2be3edcb0441..90835da495250 100644 --- a/packages/expo/cli/utils/analytics/rudderstackClient.ts +++ b/packages/expo/cli/utils/analytics/rudderstackClient.ts @@ -1,6 +1,5 @@ import RudderAnalytics from '@expo/rudder-sdk-node'; import os from 'os'; -import { URL } from 'url'; import { v4 as uuidv4 } from 'uuid'; import { EXPO_LOCAL, EXPO_STAGING, EXPO_NO_TELEMETRY } from '../env'; @@ -12,40 +11,38 @@ const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = { linux: 'Linux', }; -let rudderstackClient: RudderAnalytics | null = null; -let identifier = false; +let client: RudderAnalytics | null = null; +let identified = false; let identifyData: { userId: string; deviceId: string; traits: Record; } | null = null; -export async function initAsync(): Promise { - if (EXPO_NO_TELEMETRY) { - const config = - EXPO_STAGING || EXPO_LOCAL - ? { - // staging environment - rudderstackWriteKey: '1wpX20Da4ltFGSXbPFYUL00Chb7', - rudderstackDataPlaneURL: 'https://cdp.expo.dev', - } - : { - // prod environment - rudderstackWriteKey: '1wpXLFxmujq86etH6G6cc90hPcC', - rudderstackDataPlaneURL: 'https://cdp.expo.dev', - }; - - rudderstackClient = new RudderAnalytics( - config.rudderstackWriteKey, - new URL('/v1/batch', config.rudderstackDataPlaneURL).toString(), - { - flushInterval: 300, - } - ); +function getClient(): RudderAnalytics { + if (client) { + return client; } + + client = new RudderAnalytics( + EXPO_STAGING || EXPO_LOCAL ? '1wpX20Da4ltFGSXbPFYUL00Chb7' : '1wpXLFxmujq86etH6G6cc90hPcC', + 'https://cdp.expo.dev/v1/batch', + { + flushInterval: 300, + } + ); + + // Install flush on exit... + process.on('SIGINT', () => client?.flush?.()); + process.on('SIGTERM', () => client?.flush?.()); + + return client; } export async function setUserDataAsync(userId: string, traits: Record): Promise { + if (EXPO_NO_TELEMETRY) { + return; + } const savedDeviceId = await UserSettings.getAsync('analyticsDeviceId', null); const deviceId = savedDeviceId ?? uuidv4(); if (!savedDeviceId) { @@ -61,14 +58,8 @@ export async function setUserDataAsync(userId: string, traits: Record { - if (rudderstackClient) { - await rudderstackClient.flush(); - } -} - export function logEvent(name: string, properties: Record = {}): void { - if (!rudderstackClient) { + if (EXPO_NO_TELEMETRY) { return; } ensureIdentified(); @@ -77,28 +68,28 @@ export function logEvent(name: string, properties: Record = {}): vo const commonEventProperties = { source_version: process.env.__EXPO_VERSION, source: 'expo' }; const identity = { userId: userId ?? undefined, anonymousId: deviceId ?? uuidv4() }; - rudderstackClient.track({ + getClient().track({ event: name, properties: { ...properties, ...commonEventProperties }, ...identity, - context: getRudderStackContext(), + context: getContext(), }); } function ensureIdentified(): void { - if (!rudderstackClient || identifier || !identifyData) { + if (EXPO_NO_TELEMETRY || identified || !identifyData) { return; } - rudderstackClient.identify({ + getClient().identify({ userId: identifyData.userId, anonymousId: identifyData.deviceId, traits: identifyData.traits, }); - identifier = true; + identified = true; } -function getRudderStackContext(): Record { +function getContext(): Record { const platform = PLATFORM_TO_ANALYTICS_PLATFORM[os.platform()] || os.platform(); return { os: { name: platform, version: os.release() }, @@ -106,7 +97,3 @@ function getRudderStackContext(): Record { app: { name: 'expo', version: process.env.__EXPO_VERSION }, }; } - -export enum AnalyticsEvent { - ACTION = 'action', // generic event type which is used to determine the 'daily active user' stat, include an `action: expo ${subcommand}` property inside of the event properties object -} diff --git a/packages/expo/cli/utils/api.ts b/packages/expo/cli/utils/api.ts index a74d5c41ddabd..c83222bd4b797 100644 --- a/packages/expo/cli/utils/api.ts +++ b/packages/expo/cli/utils/api.ts @@ -73,23 +73,3 @@ export function getExpoApiBaseUrl(): string { return `https://api.expo.dev`; } } - -export function getExpoWebsiteBaseUrl(): string { - if (EXPO_STAGING) { - return `https://staging.expo.dev`; - } else if (EXPO_LOCAL) { - return `http://expo.test`; - } else { - return `https://expo.dev`; - } -} - -export function getEASUpdateURL(projectId: string): string { - if (EXPO_STAGING) { - return new URL(projectId, `https://staging-u.expo.dev`).href; - } else if (EXPO_LOCAL) { - return new URL(`expo-updates/${projectId}`, `http://127.0.0.1:3000`).href; - } else { - return new URL(projectId, `https://u.expo.dev`).href; - } -} diff --git a/packages/expo/cli/utils/prompts.ts b/packages/expo/cli/utils/prompts.ts index 53f4997ae7f18..30494288f3f3b 100644 --- a/packages/expo/cli/utils/prompts.ts +++ b/packages/expo/cli/utils/prompts.ts @@ -68,6 +68,7 @@ export async function confirmAsync( return value ?? null; } +/** Select an option from a list of options. */ export async function selectAsync( message: string, choices: ExpoChoice[], diff --git a/packages/expo/cli/utils/user/actions.ts b/packages/expo/cli/utils/user/actions.ts index cdb2eee69e34c..7e45267473add 100644 --- a/packages/expo/cli/utils/user/actions.ts +++ b/packages/expo/cli/utils/user/actions.ts @@ -76,10 +76,7 @@ export async function showLoginPromptAsync({ /** Ensure the user is logged in, if not, prompt to login. */ export async function ensureLoggedInAsync(): Promise { - let user: Actor | undefined; - try { - user = await getUserAsync(); - } catch {} + let user = await getUserAsync().catch(() => null); if (!user) { Log.warn(chalk.yellow`An Expo user account is required to proceed.`); From c076d8906bbdb0bf3f0934008e6e479ee4b7c62f Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Thu, 27 Jan 2022 13:55:23 -0700 Subject: [PATCH 08/14] fix tests --- packages/expo/e2e/__tests__/login-test.ts | 8 ++++---- packages/expo/e2e/__tests__/logout-test.ts | 8 ++++---- packages/expo/e2e/__tests__/register-test.ts | 6 +++--- packages/expo/e2e/__tests__/whoami-test.ts | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/expo/e2e/__tests__/login-test.ts b/packages/expo/e2e/__tests__/login-test.ts index 0ecfa0df18527..8da68647fc9a6 100644 --- a/packages/expo/e2e/__tests__/login-test.ts +++ b/packages/expo/e2e/__tests__/login-test.ts @@ -7,7 +7,7 @@ const originalForceColor = process.env.FORCE_COLOR; const originalCI = process.env.CI; beforeAll(async () => { await fs.mkdir(projectRoot, { recursive: true }); - process.env.FORCE_COLOR = '1'; + process.env.FORCE_COLOR = '0'; process.env.CI = '1'; }); afterAll(() => { @@ -35,10 +35,10 @@ it('runs `npx expo login --help`', async () => { const results = await execute('login', '--help'); expect(results.stdout).toMatchInlineSnapshot(` " - Description - Login to an Expo account + Description + Log in to an Expo account - Usage + Usage $ npx expo login Options diff --git a/packages/expo/e2e/__tests__/logout-test.ts b/packages/expo/e2e/__tests__/logout-test.ts index f7d9d2185223e..f1061eb2e676e 100644 --- a/packages/expo/e2e/__tests__/logout-test.ts +++ b/packages/expo/e2e/__tests__/logout-test.ts @@ -7,7 +7,7 @@ const originalForceColor = process.env.FORCE_COLOR; beforeAll(async () => { await fs.mkdir(projectRoot, { recursive: true }); - process.env.FORCE_COLOR = '1'; + process.env.FORCE_COLOR = '0'; }); afterAll(() => { process.env.FORCE_COLOR = originalForceColor; @@ -33,10 +33,10 @@ it('runs `npx expo logout --help`', async () => { const results = await execute('logout', '--help'); expect(results.stdout).toMatchInlineSnapshot(` " - Description - Logout of an Expo account + Description + Log out of an Expo account - Usage + Usage $ npx expo logout Options diff --git a/packages/expo/e2e/__tests__/register-test.ts b/packages/expo/e2e/__tests__/register-test.ts index 3c19063827714..a551081b98467 100644 --- a/packages/expo/e2e/__tests__/register-test.ts +++ b/packages/expo/e2e/__tests__/register-test.ts @@ -7,7 +7,7 @@ const originalForceColor = process.env.FORCE_COLOR; const originalCI = process.env.CI; beforeAll(async () => { await fs.mkdir(projectRoot, { recursive: true }); - process.env.FORCE_COLOR = '1'; + process.env.FORCE_COLOR = '0'; process.env.CI = '1'; }); afterAll(() => { @@ -35,10 +35,10 @@ it('runs `npx expo register --help`', async () => { const results = await execute('register', '--help'); expect(results.stdout).toMatchInlineSnapshot(` " - Description + Description Sign up for a new Expo account - Usage + Usage $ npx expo register Options diff --git a/packages/expo/e2e/__tests__/whoami-test.ts b/packages/expo/e2e/__tests__/whoami-test.ts index 4715ab935d63b..78bdfeb76fe36 100644 --- a/packages/expo/e2e/__tests__/whoami-test.ts +++ b/packages/expo/e2e/__tests__/whoami-test.ts @@ -7,7 +7,7 @@ import { execute, getLoadedModulesAsync, projectRoot } from './utils'; const originalForceColor = process.env.FORCE_COLOR; beforeAll(async () => { await fs.mkdir(projectRoot, { recursive: true }); - process.env.FORCE_COLOR = '1'; + process.env.FORCE_COLOR = '0'; }); afterAll(() => { process.env.FORCE_COLOR = originalForceColor; @@ -33,10 +33,10 @@ it('runs `npx expo whoami --help`', async () => { const results = await execute('whoami', '--help'); expect(results.stdout).toMatchInlineSnapshot(` " - Description + Description Show the currently authenticated username - Usage + Usage $ npx expo whoami Options From c36aae540b9457b9013a3542483eecfed23d5cc1 Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Thu, 27 Jan 2022 14:11:46 -0700 Subject: [PATCH 09/14] Update user.ts --- packages/expo/cli/utils/user/user.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/expo/cli/utils/user/user.ts b/packages/expo/cli/utils/user/user.ts index 4a5779db251a9..2a9d5882eb800 100644 --- a/packages/expo/cli/utils/user/user.ts +++ b/packages/expo/cli/utils/user/user.ts @@ -46,18 +46,12 @@ export async function getUserAsync(): Promise { return currentUser; } -export async function loginAsync({ - username, - password, - otp, -}: { +export async function loginAsync(json: { username: string; password: string; otp?: string; }): Promise { - const body = await apiClient - .post('auth/loginAsync', { json: { username, password, otp } }) - .json(); + const body = await apiClient.post('auth/loginAsync', { json }).json(); const { sessionSecret } = (body as any).data; const result = await graphqlClient .query( @@ -80,11 +74,13 @@ export async function loginAsync({ } ) .toPromise(); - const { data } = result; + const { + data: { viewer }, + } = result; await setSessionAsync({ sessionSecret, - userId: data.viewer.id, - username: data.viewer.username, + userId: viewer.id, + username: viewer.username, currentConnection: 'Username-Password-Authentication', }); } From 979519b5bb1e32ff1d7a70cc31b1eee972a30e55 Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Fri, 28 Jan 2022 12:20:59 -0700 Subject: [PATCH 10/14] Fix up code Update registerAsync.ts Remove unused settings Drop unused Update otp.ts Update whoamiAsync.ts --- packages/expo/cli/register/registerAsync.ts | 8 +++-- packages/expo/cli/utils/graphql/generated.ts | 2 -- packages/expo/cli/utils/user/UserSettings.ts | 2 -- .../cli/utils/user/__tests__/actions-test.ts | 30 ++----------------- packages/expo/cli/utils/user/actions.ts | 8 ----- packages/expo/cli/utils/user/otp.ts | 14 ++++----- packages/expo/cli/whoami/whoamiAsync.ts | 2 +- 7 files changed, 13 insertions(+), 53 deletions(-) diff --git a/packages/expo/cli/register/registerAsync.ts b/packages/expo/cli/register/registerAsync.ts index a17cffec291df..372f27300a585 100644 --- a/packages/expo/cli/register/registerAsync.ts +++ b/packages/expo/cli/register/registerAsync.ts @@ -16,17 +16,19 @@ export async function registerAsync() { } const registrationUrl = `https://expo.dev/signup`; - + const failedMessage = `Unable to open a web browser. Register an account at: ${registrationUrl}`; const spinner = ora(`Opening ${registrationUrl}`).start(); try { - const opened = openBrowserAsync(registrationUrl); + const opened = await openBrowserAsync(registrationUrl); if (opened) { spinner.succeed(`Opened ${registrationUrl}`); + } else { + spinner.fail(failedMessage); } return; } catch (error) { - spinner.fail(`Unable to open a web browser. Register an account at: ${registrationUrl}`); + spinner.fail(failedMessage); throw error; } } diff --git a/packages/expo/cli/utils/graphql/generated.ts b/packages/expo/cli/utils/graphql/generated.ts index f42a8defd0fcc..3f3c723f4ba56 100644 --- a/packages/expo/cli/utils/graphql/generated.ts +++ b/packages/expo/cli/utils/graphql/generated.ts @@ -87,8 +87,6 @@ type Account = { appCount: Scalars['Int']; /** Build Jobs associated with this account */ buildJobs: Array; - /** (EAS Build) Builds associated with this account */ - builds: Array; /** * Coalesced Build (EAS) or BuildJob (Classic) for all apps belonging to this account. * @deprecated Use activityTimelineProjectActivities with filterTypes instead diff --git a/packages/expo/cli/utils/user/UserSettings.ts b/packages/expo/cli/utils/user/UserSettings.ts index b8d42a6b68cd1..4321664a40333 100644 --- a/packages/expo/cli/utils/user/UserSettings.ts +++ b/packages/expo/cli/utils/user/UserSettings.ts @@ -26,9 +26,7 @@ function getConfigDirectory() { const SETTINGS_FILE_PATH = path.join(getConfigDirectory(), 'user-settings.json'); export type UserSettingsData = { - appleId?: string; analyticsDeviceId?: string; - analyticsEnabled?: boolean; }; const UserSettings = new JsonFile(SETTINGS_FILE_PATH, { diff --git a/packages/expo/cli/utils/user/__tests__/actions-test.ts b/packages/expo/cli/utils/user/__tests__/actions-test.ts index 3ede956dabf0e..c4cb7fc64dee0 100644 --- a/packages/expo/cli/utils/user/__tests__/actions-test.ts +++ b/packages/expo/cli/utils/user/__tests__/actions-test.ts @@ -1,8 +1,8 @@ import { ApiV2Error } from '../../api'; import { promptAsync } from '../../prompts'; -import { ensureActorHasUsername, showLoginPromptAsync } from '../actions'; +import { showLoginPromptAsync } from '../actions'; import { retryUsernamePasswordAuthWithOTPAsync, UserSecondFactorDeviceMethod } from '../otp'; -import { Actor, loginAsync } from '../user'; +import { loginAsync } from '../user'; jest.mock('../../prompts'); jest.mock('../../api', () => { @@ -25,32 +25,6 @@ beforeEach(() => { asMock(loginAsync).mockReset(); }); -const userStub: Actor = { - __typename: 'User', - id: 'userId', - username: 'username', - accounts: [], - isExpoAdmin: false, -}; - -const robotStub: Actor = { - __typename: 'Robot', - id: 'userId', - firstName: 'GLaDOS', - accounts: [], - isExpoAdmin: false, -}; - -describe('ensureActorHasUsername', () => { - it('returns username for user actors', () => { - expect(ensureActorHasUsername(userStub)).toBe(userStub.username); - }); - - it('throws for robot actors', () => { - expect(() => ensureActorHasUsername(robotStub)).toThrow('not supported for robot'); - }); -}); - describe(showLoginPromptAsync, () => { it('prompts for OTP when 2FA is enabled', async () => { asMock(promptAsync) diff --git a/packages/expo/cli/utils/user/actions.ts b/packages/expo/cli/utils/user/actions.ts index 7e45267473add..80a162bc6e43a 100644 --- a/packages/expo/cli/utils/user/actions.ts +++ b/packages/expo/cli/utils/user/actions.ts @@ -3,7 +3,6 @@ import chalk from 'chalk'; import * as Log from '../../log'; import { ApiV2Error } from '../api'; -import { CommandError } from '../errors'; import { learnMore } from '../link'; import promptAsync, { Question } from '../prompts'; import { retryUsernamePasswordAuthWithOTPAsync } from './otp'; @@ -87,10 +86,3 @@ export async function ensureLoggedInAsync(): Promise { assert(user, 'User should be logged in'); return user; } - -export function ensureActorHasUsername(user: Actor): string { - if (user.__typename === 'User') { - return user.username; - } - throw new CommandError('This action is not supported for robot users.'); -} diff --git a/packages/expo/cli/utils/user/otp.ts b/packages/expo/cli/utils/user/otp.ts index e2054b0f3a8a1..936c4333c2831 100644 --- a/packages/expo/cli/utils/user/otp.ts +++ b/packages/expo/cli/utils/user/otp.ts @@ -3,7 +3,7 @@ import chalk from 'chalk'; import * as Log from '../../log'; import { apiClient } from '../api'; -import { AbortCommandError } from '../errors'; +import { AbortCommandError, CommandError } from '../errors'; import { learnMore } from '../link'; import { promptAsync, selectAsync } from '../prompts'; import { loginAsync } from './user'; @@ -31,8 +31,8 @@ const nonInteractiveHelp = `Use the EXPO_TOKEN environment variable to authentic async function promptForOTPAsync(cancelBehavior: 'cancel' | 'menu'): Promise { const enterMessage = cancelBehavior === 'cancel' - ? `press ${chalk.bold('Enter')} to cancel` - : `press ${chalk.bold('Enter')} for more options`; + ? chalk`press {bold Enter} to cancel` + : chalk`press {bold Enter} for more options`; const { otp } = await promptAsync( { type: 'text', @@ -41,11 +41,7 @@ async function promptForOTPAsync(cancelBehavior: 'cancel' | 'menu'): Promise !device.is_primary); if (nonPrimarySecondFactorDevices.length === 0) { - throw new Error( + throw new CommandError( 'No other second-factor devices set up. Ensure you have set up and certified a backup device.' ); } diff --git a/packages/expo/cli/whoami/whoamiAsync.ts b/packages/expo/cli/whoami/whoamiAsync.ts index 3b534677a3dc3..be9fcd4bce17d 100644 --- a/packages/expo/cli/whoami/whoamiAsync.ts +++ b/packages/expo/cli/whoami/whoamiAsync.ts @@ -8,6 +8,6 @@ export async function whoamiAsync() { if (user) { Log.exit(chalk.green(getActorDisplayName(user)), 0); } else { - Log.exit('Not logged in', 1); + Log.exit('Not logged in'); } } From dccca707cffb6ba69a1bb6bfb7bb31ec4e2b4d06 Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Fri, 28 Jan 2022 16:57:18 -0700 Subject: [PATCH 11/14] Update api-test.ts --- packages/expo/cli/utils/__tests__/api-test.ts | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/expo/cli/utils/__tests__/api-test.ts b/packages/expo/cli/utils/__tests__/api-test.ts index 6978085c4ef17..56916f10b9044 100644 --- a/packages/expo/cli/utils/__tests__/api-test.ts +++ b/packages/expo/cli/utils/__tests__/api-test.ts @@ -19,32 +19,30 @@ it('converts Expo APIv2 error to ApiV2Error', async () => { ], }); - let error: Error | null = null; + expect.assertions(5); + try { await apiClient.post('test'); - } catch (e: any) { - error = e; - } - - expect(error).toBeInstanceOf(ApiV2Error); - assert(error instanceof ApiV2Error); + } catch (error: any) { + assert(error instanceof ApiV2Error); - expect(error.message).toEqual('hellomessage'); - expect(error.expoApiV2ErrorCode).toEqual('TEST_CODE'); - expect(error.expoApiV2ErrorDetails).toEqual({ who: 'world' }); - expect(error.expoApiV2ErrorMetadata).toEqual({ an: 'object' }); - expect(error.expoApiV2ErrorServerStack).toEqual('line 1: hello'); + expect(error.message).toEqual('hellomessage'); + expect(error.expoApiV2ErrorCode).toEqual('TEST_CODE'); + expect(error.expoApiV2ErrorDetails).toEqual({ who: 'world' }); + expect(error.expoApiV2ErrorMetadata).toEqual({ an: 'object' }); + expect(error.expoApiV2ErrorServerStack).toEqual('line 1: hello'); + } }); it('does not convert non-APIv2 error to ApiV2Error', async () => { nock(getExpoApiBaseUrl()).post('/v2/test').reply(500, 'Something went wrong'); - let error: Error | null = null; + expect.assertions(2); + try { await apiClient.post('test'); - } catch (e: any) { - error = e; + } catch (error: any) { + expect(error).toBeInstanceOf(RequestError); + expect(error).not.toBeInstanceOf(ApiV2Error); } - expect(error).toBeInstanceOf(RequestError); - expect(error).not.toBeInstanceOf(ApiV2Error); }); From 9b16681b9a5b96a6402519f368aca3f02c955b1f Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Mon, 31 Jan 2022 22:59:22 -0700 Subject: [PATCH 12/14] Apply suggestions from code review Co-authored-by: kgc00 --- packages/expo/cli/utils/analytics/rudderstackClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo/cli/utils/analytics/rudderstackClient.ts b/packages/expo/cli/utils/analytics/rudderstackClient.ts index 90835da495250..c157c5447cfd8 100644 --- a/packages/expo/cli/utils/analytics/rudderstackClient.ts +++ b/packages/expo/cli/utils/analytics/rudderstackClient.ts @@ -25,7 +25,7 @@ function getClient(): RudderAnalytics { } client = new RudderAnalytics( - EXPO_STAGING || EXPO_LOCAL ? '1wpX20Da4ltFGSXbPFYUL00Chb7' : '1wpXLFxmujq86etH6G6cc90hPcC', + EXPO_STAGING || EXPO_LOCAL ? '24TKICqYKilXM480mA7ktgVDdea' : '24TKR7CQAaGgIrLTgu3Fp4OdOkI', // expo unified 'https://cdp.expo.dev/v1/batch', { flushInterval: 300, From 61abe1d59a17d3f451ce6c2a96abf032e2b0fd4a Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Mon, 31 Jan 2022 23:00:54 -0700 Subject: [PATCH 13/14] Update rudderstackClient.ts --- packages/expo/cli/utils/analytics/rudderstackClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/expo/cli/utils/analytics/rudderstackClient.ts b/packages/expo/cli/utils/analytics/rudderstackClient.ts index c157c5447cfd8..65df0bf0fbd80 100644 --- a/packages/expo/cli/utils/analytics/rudderstackClient.ts +++ b/packages/expo/cli/utils/analytics/rudderstackClient.ts @@ -58,7 +58,7 @@ export async function setUserDataAsync(userId: string, traits: Record = {}): void { +export function logEvent(event: string, properties: Record = {}): void { if (EXPO_NO_TELEMETRY) { return; } @@ -69,7 +69,7 @@ export function logEvent(name: string, properties: Record = {}): vo const identity = { userId: userId ?? undefined, anonymousId: deviceId ?? uuidv4() }; getClient().track({ - event: name, + event, properties: { ...properties, ...commonEventProperties }, ...identity, context: getContext(), From facf647ff6c0dcd20a878efc1f6067fedb421287 Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Mon, 31 Jan 2022 23:02:16 -0700 Subject: [PATCH 14/14] Update rudderstackClient.ts --- packages/expo/cli/utils/analytics/rudderstackClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo/cli/utils/analytics/rudderstackClient.ts b/packages/expo/cli/utils/analytics/rudderstackClient.ts index 65df0bf0fbd80..0a80d95347f35 100644 --- a/packages/expo/cli/utils/analytics/rudderstackClient.ts +++ b/packages/expo/cli/utils/analytics/rudderstackClient.ts @@ -58,7 +58,7 @@ export async function setUserDataAsync(userId: string, traits: Record = {}): void { +export function logEvent(event: 'action', properties: Record = {}): void { if (EXPO_NO_TELEMETRY) { return; }