@expo/cli 55.0.31 → 55.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bin/cli +1 -1
- package/build/src/api/rest/client.js +27 -12
- package/build/src/api/rest/client.js.map +1 -1
- package/build/src/api/user/UserSettings.js +4 -2
- package/build/src/api/user/UserSettings.js.map +1 -1
- package/build/src/events/index.js +1 -1
- package/build/src/export/embed/exportEmbedAsync.js +2 -2
- package/build/src/export/embed/exportEmbedAsync.js.map +1 -1
- package/build/src/prebuild/resolveTemplate.js +10 -5
- package/build/src/prebuild/resolveTemplate.js.map +1 -1
- package/build/src/start/platforms/android/adb.js +16 -15
- package/build/src/start/platforms/android/adb.js.map +1 -1
- package/build/src/start/server/getStaticRenderFunctions.js +2 -1
- package/build/src/start/server/getStaticRenderFunctions.js.map +1 -1
- package/build/src/start/server/metro/debugging/createDebugMiddleware.js +6 -5
- package/build/src/start/server/metro/debugging/createDebugMiddleware.js.map +1 -1
- package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js +17 -1
- package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js.map +1 -1
- package/build/src/start/server/metro/dev-server/createMetroMiddleware.js +7 -1
- package/build/src/start/server/metro/dev-server/createMetroMiddleware.js.map +1 -1
- package/build/src/start/server/metro/instantiateMetro.js +3 -1
- package/build/src/start/server/metro/instantiateMetro.js.map +1 -1
- package/build/src/start/server/metro/metroErrorInterface.js +5 -2
- package/build/src/start/server/metro/metroErrorInterface.js.map +1 -1
- package/build/src/start/server/middleware/InterstitialPageMiddleware.js +7 -4
- package/build/src/start/server/middleware/InterstitialPageMiddleware.js.map +1 -1
- package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js +14 -24
- package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js.map +1 -1
- package/build/src/utils/codesigning.js +6 -0
- package/build/src/utils/codesigning.js.map +1 -1
- package/build/src/utils/net.js +13 -0
- package/build/src/utils/net.js.map +1 -1
- package/build/src/utils/telemetry/clients/FetchClient.js +1 -1
- package/build/src/utils/telemetry/utils/context.js +1 -1
- package/build/src/utils/url.js +0 -12
- package/build/src/utils/url.js.map +1 -1
- package/package.json +9 -9
- package/static/loading-page/index.html +10 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/codesigning.ts"],"sourcesContent":["import {\n convertCertificatePEMToCertificate,\n convertKeyPairToPEM,\n convertCSRToCSRPEM,\n generateKeyPair,\n generateCSR,\n convertPrivateKeyPEMToPrivateKey,\n validateSelfSignedCertificate,\n signBufferRSASHA256AndVerify,\n} from '@expo/code-signing-certificates';\nimport { ExpoConfig } from '@expo/config';\nimport JsonFile, { JSONObject } from '@expo/json-file';\nimport { promises as fs } from 'fs';\nimport { pki as PKI } from 'node-forge';\nimport path from 'path';\nimport { Dictionary, parseDictionary } from 'structured-headers';\n\nimport { env } from './env';\nimport { CommandError } from './errors';\nimport { getExpoGoIntermediateCertificateAsync } from '../api/getExpoGoIntermediateCertificate';\nimport { getProjectDevelopmentCertificateAsync } from '../api/getProjectDevelopmentCertificate';\nimport { UnexpectedServerError, UnexpectedServerData } from '../api/graphql/client';\nimport { AppQuery, type App } from '../api/graphql/queries/AppQuery';\nimport { getExpoHomeDirectory } from '../api/user/UserSettings';\nimport { tryGetUserAsync } from '../api/user/actions';\nimport { Actor } from '../api/user/user';\nimport * as Log from '../log';\nimport { learnMore } from '../utils/link';\n\nconst debug = require('debug')('expo:codesigning') as typeof console.log;\n\nexport type CodeSigningInfo = {\n keyId: string;\n privateKey: string;\n certificateForPrivateKey: string;\n /**\n * Chain of certificates to serve in the manifest multipart body \"certificate_chain\" part.\n * The leaf certificate must be the 0th element of the array, followed by any intermediate certificates\n * necessary to evaluate the chain of trust ending in the implicitly trusted root certificate embedded in\n * the client.\n *\n * An empty array indicates that there is no need to serve the certificate chain in the multipart response.\n */\n certificateChainForResponse: string[];\n /**\n * Scope key cached for the project when certificate is development Expo Go code signing.\n * For project-specific code signing (keyId == the project's generated keyId) this is undefined.\n */\n scopeKey: string | null;\n};\n\ntype StoredDevelopmentExpoRootCodeSigningInfo = {\n easProjectId: string | null;\n scopeKey: string | null;\n privateKey: string | null;\n certificateChain: string[] | null;\n};\nconst DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME = 'development-code-signing-settings-2.json';\n\nexport function getDevelopmentCodeSigningDirectory(): string {\n return path.join(getExpoHomeDirectory(), 'codesigning');\n}\n\nfunction getProjectDevelopmentCodeSigningInfoFile<T extends JSONObject>(defaults: T) {\n function getFile(easProjectId: string): JsonFile<T> {\n const filePath = path.join(\n getDevelopmentCodeSigningDirectory(),\n easProjectId,\n DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME\n );\n return new JsonFile<T>(filePath);\n }\n\n async function readAsync(easProjectId: string): Promise<T> {\n let projectSettings;\n try {\n projectSettings = await getFile(easProjectId).readAsync();\n } catch {\n projectSettings = await getFile(easProjectId).writeAsync(defaults, { ensureDir: true });\n }\n // Set defaults for any missing fields\n return { ...defaults, ...projectSettings };\n }\n\n async function setAsync(easProjectId: string, json: Partial<T>): Promise<T> {\n try {\n return await getFile(easProjectId).mergeAsync(json, {\n cantReadFileDefault: defaults,\n });\n } catch {\n return await getFile(easProjectId).writeAsync(\n {\n ...defaults,\n ...json,\n },\n { ensureDir: true }\n );\n }\n }\n\n return {\n getFile,\n readAsync,\n setAsync,\n };\n}\n\nexport const DevelopmentCodeSigningInfoFile =\n getProjectDevelopmentCodeSigningInfoFile<StoredDevelopmentExpoRootCodeSigningInfo>({\n easProjectId: null,\n scopeKey: null,\n privateKey: null,\n certificateChain: null,\n });\n\n/**\n * Get info necessary to generate a response `expo-signature` header given a project and incoming request `expo-expect-signature` header.\n * This only knows how to serve two code signing keyids:\n * - `expo-root` indicates that it should use a development certificate in the `expo-root` chain. See {@link getExpoRootDevelopmentCodeSigningInfoAsync}\n * - <developer's expo-updates keyid> indicates that it should sign with the configured certificate. See {@link getProjectCodeSigningCertificateAsync}\n */\nexport async function getCodeSigningInfoAsync(\n exp: ExpoConfig,\n expectSignatureHeader: string | null,\n privateKeyPath: string | undefined\n): Promise<CodeSigningInfo | null> {\n if (!expectSignatureHeader) {\n return null;\n }\n\n let parsedExpectSignature: Dictionary;\n try {\n parsedExpectSignature = parseDictionary(expectSignatureHeader);\n } catch {\n throw new CommandError('Invalid value for expo-expect-signature header');\n }\n\n const expectedKeyIdOuter = parsedExpectSignature.get('keyid');\n if (!expectedKeyIdOuter) {\n throw new CommandError('keyid not present in expo-expect-signature header');\n }\n\n const expectedKeyId = expectedKeyIdOuter[0];\n if (typeof expectedKeyId !== 'string') {\n throw new CommandError(\n `Invalid value for keyid in expo-expect-signature header: ${expectedKeyId}`\n );\n }\n\n let expectedAlg: string | null = null;\n const expectedAlgOuter = parsedExpectSignature.get('alg');\n if (expectedAlgOuter) {\n const expectedAlgTemp = expectedAlgOuter[0];\n if (typeof expectedAlgTemp !== 'string') {\n throw new CommandError('Invalid value for alg in expo-expect-signature header');\n }\n expectedAlg = expectedAlgTemp;\n }\n\n if (expectedKeyId === 'expo-root') {\n return await getExpoRootDevelopmentCodeSigningInfoAsync(exp);\n } else if (expectedKeyId === 'expo-go') {\n throw new CommandError(\n 'Invalid certificate requested: cannot sign with embedded keyid=expo-go key'\n );\n } else {\n return await getProjectCodeSigningCertificateAsync(\n exp,\n privateKeyPath,\n expectedKeyId,\n expectedAlg\n );\n }\n}\n\n/**\n * Get a development code signing certificate for the expo-root -> expo-go -> (development certificate) certificate chain.\n * This requires the user be logged in and online, otherwise try to use the cached development certificate.\n */\nasync function getExpoRootDevelopmentCodeSigningInfoAsync(\n exp: ExpoConfig\n): Promise<CodeSigningInfo | null> {\n const easProjectId = exp.extra?.eas?.projectId;\n // can't check for scope key validity since scope key is derived on the server from projectId and we may be offline.\n // we rely upon the client certificate check to validate the scope key\n if (!easProjectId) {\n debug(\n `WARN: Expo Application Services (EAS) is not configured for your project. Configuring EAS enables a more secure development experience amongst many other benefits. ${learnMore(\n 'https://docs.expo.dev/eas/'\n )}`\n );\n return null;\n }\n\n const developmentCodeSigningInfoFromFile =\n await DevelopmentCodeSigningInfoFile.readAsync(easProjectId);\n const validatedCodeSigningInfo = validateStoredDevelopmentExpoRootCertificateCodeSigningInfo(\n developmentCodeSigningInfoFromFile,\n easProjectId\n );\n\n // 1. If online, ensure logged in, generate key pair and CSR, fetch and cache certificate chain for projectId\n // (overwriting existing dev cert in case projectId changed or it has expired)\n if (!env.EXPO_OFFLINE) {\n try {\n const newCodeSigningInfo =\n await fetchAndCacheNewDevelopmentCodeSigningInfoAsync(easProjectId);\n\n if (newCodeSigningInfo) {\n return newCodeSigningInfo;\n // fall back to cached certificate if we couldn't fetch a new one\n } else if (validatedCodeSigningInfo) {\n Log.warn(\n 'Could not fetch new Expo development certificate, falling back to cached certificate'\n );\n return validatedCodeSigningInfo;\n } else {\n return null;\n }\n } catch (e: any) {\n if (validatedCodeSigningInfo) {\n Log.warn(\n 'There was an error fetching the Expo development certificate, falling back to cached certificate'\n );\n return validatedCodeSigningInfo;\n } else {\n // need to return null here and say a message\n throw e;\n }\n }\n }\n\n // 2. check for cached cert/private key matching projectId and scopeKey of project, if found and valid return private key and cert chain including expo-go cert\n if (validatedCodeSigningInfo) {\n return validatedCodeSigningInfo;\n }\n\n // 3. if offline, return null\n Log.warn('Offline and no cached development certificate found, unable to sign manifest');\n return null;\n}\n\n/**\n * Get the certificate configured for expo-updates for this project.\n */\nasync function getProjectCodeSigningCertificateAsync(\n exp: ExpoConfig,\n privateKeyPath: string | undefined,\n expectedKeyId: string,\n expectedAlg: string | null\n): Promise<CodeSigningInfo | null> {\n const codeSigningCertificatePath = exp.updates?.codeSigningCertificate;\n if (!codeSigningCertificatePath) {\n return null;\n }\n\n if (!privateKeyPath) {\n throw new CommandError(\n 'Must specify --private-key-path argument to sign development manifest for requested code signing key'\n );\n }\n\n const codeSigningMetadata = exp.updates?.codeSigningMetadata;\n if (!codeSigningMetadata) {\n throw new CommandError(\n 'Must specify \"codeSigningMetadata\" under the \"updates\" field of your app config file to use EAS code signing'\n );\n }\n\n const { alg, keyid } = codeSigningMetadata;\n if (!alg || !keyid) {\n throw new CommandError(\n 'Must specify \"keyid\" and \"alg\" in the \"codeSigningMetadata\" field under the \"updates\" field of your app config file to use EAS code signing'\n );\n }\n\n if (expectedKeyId !== keyid) {\n throw new CommandError(`keyid mismatch: client=${expectedKeyId}, project=${keyid}`);\n }\n\n if (expectedAlg && expectedAlg !== alg) {\n throw new CommandError(`\"alg\" field mismatch (client=${expectedAlg}, project=${alg})`);\n }\n\n const { privateKeyPEM, certificatePEM } =\n await getProjectPrivateKeyAndCertificateFromFilePathsAsync({\n codeSigningCertificatePath,\n privateKeyPath,\n });\n\n return {\n keyId: keyid,\n privateKey: privateKeyPEM,\n certificateForPrivateKey: certificatePEM,\n certificateChainForResponse: [],\n scopeKey: null,\n };\n}\n\nasync function readFileWithErrorAsync(path: string, errorMessage: string): Promise<string> {\n try {\n return await fs.readFile(path, 'utf8');\n } catch {\n throw new CommandError(errorMessage);\n }\n}\n\nasync function getProjectPrivateKeyAndCertificateFromFilePathsAsync({\n codeSigningCertificatePath,\n privateKeyPath,\n}: {\n codeSigningCertificatePath: string;\n privateKeyPath: string;\n}): Promise<{ privateKeyPEM: string; certificatePEM: string }> {\n const [codeSigningCertificatePEM, privateKeyPEM] = await Promise.all([\n readFileWithErrorAsync(\n codeSigningCertificatePath,\n `Code signing certificate cannot be read from path: ${codeSigningCertificatePath}`\n ),\n readFileWithErrorAsync(\n privateKeyPath,\n `Code signing private key cannot be read from path: ${privateKeyPath}`\n ),\n ]);\n\n const privateKey = convertPrivateKeyPEMToPrivateKey(privateKeyPEM);\n const certificate = convertCertificatePEMToCertificate(codeSigningCertificatePEM);\n validateSelfSignedCertificate(certificate, {\n publicKey: certificate.publicKey as PKI.rsa.PublicKey,\n privateKey,\n });\n\n return { privateKeyPEM, certificatePEM: codeSigningCertificatePEM };\n}\n\n/**\n * Validate that the cached code signing info is still valid for the current project and\n * that it hasn't expired. If invalid, return null.\n */\nfunction validateStoredDevelopmentExpoRootCertificateCodeSigningInfo(\n codeSigningInfo: StoredDevelopmentExpoRootCodeSigningInfo,\n easProjectId: string\n): CodeSigningInfo | null {\n if (codeSigningInfo.easProjectId !== easProjectId) {\n return null;\n }\n\n const {\n privateKey: privateKeyPEM,\n certificateChain: certificatePEMs,\n scopeKey,\n } = codeSigningInfo;\n if (!privateKeyPEM || !certificatePEMs) {\n return null;\n }\n\n const certificateChain = certificatePEMs.map((certificatePEM) =>\n convertCertificatePEMToCertificate(certificatePEM)\n );\n\n // TODO(wschurman): maybe move to @expo/code-signing-certificates\n\n // ensure all intermediate certificates are valid\n for (const certificate of certificateChain) {\n const now = new Date();\n if (certificate.validity.notBefore > now || certificate.validity.notAfter < now) {\n return null;\n }\n }\n\n // TODO(wschurman): maybe do more validation, like validation of projectID and scopeKey within eas certificate extension\n\n return {\n keyId: 'expo-go',\n certificateChainForResponse: certificatePEMs,\n certificateForPrivateKey: certificatePEMs[0],\n privateKey: privateKeyPEM,\n scopeKey,\n };\n}\n\nfunction actorCanGetProjectDevelopmentCertificate(actor: Actor, app: App) {\n const owningAccountId = app.ownerAccount.id;\n\n const owningAccountIsActorPrimaryAccount =\n actor.__typename === 'User' || actor.__typename === 'SSOUser'\n ? actor.primaryAccount.id === owningAccountId\n : false;\n const userHasPublishPermissionForOwningAccount = !!actor.accounts\n .find((account) => account.id === owningAccountId)\n ?.users?.find((userPermission) => userPermission.actor.id === actor.id)\n ?.permissions?.includes('PUBLISH');\n return owningAccountIsActorPrimaryAccount || userHasPublishPermissionForOwningAccount;\n}\n\nasync function fetchAndCacheNewDevelopmentCodeSigningInfoAsync(\n easProjectId: string\n): Promise<CodeSigningInfo | null> {\n const actor = await tryGetUserAsync();\n\n if (!actor) {\n return null;\n }\n\n let app: App;\n try {\n app = await AppQuery.byIdAsync(easProjectId);\n } catch (e) {\n if (e instanceof UnexpectedServerError || e instanceof UnexpectedServerData) {\n return null;\n }\n throw e;\n }\n if (!actorCanGetProjectDevelopmentCertificate(actor, app)) {\n return null;\n }\n\n const keyPair = generateKeyPair();\n const keyPairPEM = convertKeyPairToPEM(keyPair);\n const csr = generateCSR(keyPair, `Development Certificate for ${easProjectId}`);\n const csrPEM = convertCSRToCSRPEM(csr);\n const [developmentSigningCertificate, expoGoIntermediateCertificate] = await Promise.all([\n getProjectDevelopmentCertificateAsync(easProjectId, csrPEM),\n getExpoGoIntermediateCertificateAsync(easProjectId),\n ]);\n\n await DevelopmentCodeSigningInfoFile.setAsync(easProjectId, {\n easProjectId,\n scopeKey: app.scopeKey,\n privateKey: keyPairPEM.privateKeyPEM,\n certificateChain: [developmentSigningCertificate, expoGoIntermediateCertificate],\n });\n\n return {\n keyId: 'expo-go',\n certificateChainForResponse: [developmentSigningCertificate, expoGoIntermediateCertificate],\n certificateForPrivateKey: developmentSigningCertificate,\n privateKey: keyPairPEM.privateKeyPEM,\n scopeKey: app.scopeKey,\n };\n}\n/**\n * Generate the `expo-signature` header for a manifest and code signing info.\n */\nexport function signManifestString(\n stringifiedManifest: string,\n codeSigningInfo: CodeSigningInfo\n): string {\n const privateKey = convertPrivateKeyPEMToPrivateKey(codeSigningInfo.privateKey);\n const certificate = convertCertificatePEMToCertificate(codeSigningInfo.certificateForPrivateKey);\n return signBufferRSASHA256AndVerify(\n privateKey,\n certificate,\n Buffer.from(stringifiedManifest, 'utf8')\n );\n}\n"],"names":["DevelopmentCodeSigningInfoFile","getCodeSigningInfoAsync","getDevelopmentCodeSigningDirectory","signManifestString","debug","require","DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME","path","join","getExpoHomeDirectory","getProjectDevelopmentCodeSigningInfoFile","defaults","getFile","easProjectId","filePath","JsonFile","readAsync","projectSettings","writeAsync","ensureDir","setAsync","json","mergeAsync","cantReadFileDefault","scopeKey","privateKey","certificateChain","exp","expectSignatureHeader","privateKeyPath","parsedExpectSignature","parseDictionary","CommandError","expectedKeyIdOuter","get","expectedKeyId","expectedAlg","expectedAlgOuter","expectedAlgTemp","getExpoRootDevelopmentCodeSigningInfoAsync","getProjectCodeSigningCertificateAsync","extra","eas","projectId","learnMore","developmentCodeSigningInfoFromFile","validatedCodeSigningInfo","validateStoredDevelopmentExpoRootCertificateCodeSigningInfo","env","EXPO_OFFLINE","newCodeSigningInfo","fetchAndCacheNewDevelopmentCodeSigningInfoAsync","Log","warn","e","codeSigningCertificatePath","updates","codeSigningCertificate","codeSigningMetadata","alg","keyid","privateKeyPEM","certificatePEM","getProjectPrivateKeyAndCertificateFromFilePathsAsync","keyId","certificateForPrivateKey","certificateChainForResponse","readFileWithErrorAsync","errorMessage","fs","readFile","codeSigningCertificatePEM","Promise","all","convertPrivateKeyPEMToPrivateKey","certificate","convertCertificatePEMToCertificate","validateSelfSignedCertificate","publicKey","codeSigningInfo","certificatePEMs","map","now","Date","validity","notBefore","notAfter","actorCanGetProjectDevelopmentCertificate","actor","app","owningAccountId","ownerAccount","id","owningAccountIsActorPrimaryAccount","__typename","primaryAccount","userHasPublishPermissionForOwningAccount","accounts","find","account","users","userPermission","permissions","includes","tryGetUserAsync","AppQuery","byIdAsync","UnexpectedServerError","UnexpectedServerData","keyPair","generateKeyPair","keyPairPEM","convertKeyPairToPEM","csr","generateCSR","csrPEM","convertCSRToCSRPEM","developmentSigningCertificate","expoGoIntermediateCertificate","getProjectDevelopmentCertificateAsync","getExpoGoIntermediateCertificateAsync","stringifiedManifest","signBufferRSASHA256AndVerify","Buffer","from"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/codesigning.ts"],"sourcesContent":["import {\n convertCertificatePEMToCertificate,\n convertKeyPairToPEM,\n convertCSRToCSRPEM,\n generateKeyPair,\n generateCSR,\n convertPrivateKeyPEMToPrivateKey,\n validateSelfSignedCertificate,\n signBufferRSASHA256AndVerify,\n} from '@expo/code-signing-certificates';\nimport { ExpoConfig } from '@expo/config';\nimport JsonFile, { JSONObject } from '@expo/json-file';\nimport { promises as fs } from 'fs';\nimport { pki as PKI } from 'node-forge';\nimport path from 'path';\nimport { Dictionary, parseDictionary } from 'structured-headers';\n\nimport { env } from './env';\nimport { CommandError } from './errors';\nimport { getExpoGoIntermediateCertificateAsync } from '../api/getExpoGoIntermediateCertificate';\nimport { getProjectDevelopmentCertificateAsync } from '../api/getProjectDevelopmentCertificate';\nimport { UnexpectedServerError, UnexpectedServerData } from '../api/graphql/client';\nimport { AppQuery, type App } from '../api/graphql/queries/AppQuery';\nimport { getExpoHomeDirectory } from '../api/user/UserSettings';\nimport { tryGetUserAsync } from '../api/user/actions';\nimport { Actor } from '../api/user/user';\nimport * as Log from '../log';\nimport { learnMore } from '../utils/link';\n\nconst debug = require('debug')('expo:codesigning') as typeof console.log;\n\nexport type CodeSigningInfo = {\n keyId: string;\n privateKey: string;\n certificateForPrivateKey: string;\n /**\n * Chain of certificates to serve in the manifest multipart body \"certificate_chain\" part.\n * The leaf certificate must be the 0th element of the array, followed by any intermediate certificates\n * necessary to evaluate the chain of trust ending in the implicitly trusted root certificate embedded in\n * the client.\n *\n * An empty array indicates that there is no need to serve the certificate chain in the multipart response.\n */\n certificateChainForResponse: string[];\n /**\n * Scope key cached for the project when certificate is development Expo Go code signing.\n * For project-specific code signing (keyId == the project's generated keyId) this is undefined.\n */\n scopeKey: string | null;\n};\n\ntype StoredDevelopmentExpoRootCodeSigningInfo = {\n easProjectId: string | null;\n scopeKey: string | null;\n privateKey: string | null;\n certificateChain: string[] | null;\n};\nconst DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME = 'development-code-signing-settings-2.json';\n\nexport function getDevelopmentCodeSigningDirectory(): string {\n return path.join(getExpoHomeDirectory(), 'codesigning');\n}\n\nfunction assertBasenameValue(input: string): void {\n if (!input || input === '.' || input === '..' || input !== path.basename(input)) {\n throw new CommandError('Invalid EAS project ID for development code signing cache');\n }\n}\n\nfunction getProjectDevelopmentCodeSigningInfoFile<T extends JSONObject>(defaults: T) {\n function getFile(easProjectId: string): JsonFile<T> {\n assertBasenameValue(easProjectId);\n const filePath = path.join(\n getDevelopmentCodeSigningDirectory(),\n easProjectId,\n DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME\n );\n return new JsonFile<T>(filePath);\n }\n\n async function readAsync(easProjectId: string): Promise<T> {\n let projectSettings;\n try {\n projectSettings = await getFile(easProjectId).readAsync();\n } catch {\n projectSettings = await getFile(easProjectId).writeAsync(defaults, { ensureDir: true });\n }\n // Set defaults for any missing fields\n return { ...defaults, ...projectSettings };\n }\n\n async function setAsync(easProjectId: string, json: Partial<T>): Promise<T> {\n try {\n return await getFile(easProjectId).mergeAsync(json, {\n cantReadFileDefault: defaults,\n });\n } catch {\n return await getFile(easProjectId).writeAsync(\n {\n ...defaults,\n ...json,\n },\n { ensureDir: true }\n );\n }\n }\n\n return {\n getFile,\n readAsync,\n setAsync,\n };\n}\n\nexport const DevelopmentCodeSigningInfoFile =\n getProjectDevelopmentCodeSigningInfoFile<StoredDevelopmentExpoRootCodeSigningInfo>({\n easProjectId: null,\n scopeKey: null,\n privateKey: null,\n certificateChain: null,\n });\n\n/**\n * Get info necessary to generate a response `expo-signature` header given a project and incoming request `expo-expect-signature` header.\n * This only knows how to serve two code signing keyids:\n * - `expo-root` indicates that it should use a development certificate in the `expo-root` chain. See {@link getExpoRootDevelopmentCodeSigningInfoAsync}\n * - <developer's expo-updates keyid> indicates that it should sign with the configured certificate. See {@link getProjectCodeSigningCertificateAsync}\n */\nexport async function getCodeSigningInfoAsync(\n exp: ExpoConfig,\n expectSignatureHeader: string | null,\n privateKeyPath: string | undefined\n): Promise<CodeSigningInfo | null> {\n if (!expectSignatureHeader) {\n return null;\n }\n\n let parsedExpectSignature: Dictionary;\n try {\n parsedExpectSignature = parseDictionary(expectSignatureHeader);\n } catch {\n throw new CommandError('Invalid value for expo-expect-signature header');\n }\n\n const expectedKeyIdOuter = parsedExpectSignature.get('keyid');\n if (!expectedKeyIdOuter) {\n throw new CommandError('keyid not present in expo-expect-signature header');\n }\n\n const expectedKeyId = expectedKeyIdOuter[0];\n if (typeof expectedKeyId !== 'string') {\n throw new CommandError(\n `Invalid value for keyid in expo-expect-signature header: ${expectedKeyId}`\n );\n }\n\n let expectedAlg: string | null = null;\n const expectedAlgOuter = parsedExpectSignature.get('alg');\n if (expectedAlgOuter) {\n const expectedAlgTemp = expectedAlgOuter[0];\n if (typeof expectedAlgTemp !== 'string') {\n throw new CommandError('Invalid value for alg in expo-expect-signature header');\n }\n expectedAlg = expectedAlgTemp;\n }\n\n if (expectedKeyId === 'expo-root') {\n return await getExpoRootDevelopmentCodeSigningInfoAsync(exp);\n } else if (expectedKeyId === 'expo-go') {\n throw new CommandError(\n 'Invalid certificate requested: cannot sign with embedded keyid=expo-go key'\n );\n } else {\n return await getProjectCodeSigningCertificateAsync(\n exp,\n privateKeyPath,\n expectedKeyId,\n expectedAlg\n );\n }\n}\n\n/**\n * Get a development code signing certificate for the expo-root -> expo-go -> (development certificate) certificate chain.\n * This requires the user be logged in and online, otherwise try to use the cached development certificate.\n */\nasync function getExpoRootDevelopmentCodeSigningInfoAsync(\n exp: ExpoConfig\n): Promise<CodeSigningInfo | null> {\n const easProjectId = exp.extra?.eas?.projectId;\n // can't check for scope key validity since scope key is derived on the server from projectId and we may be offline.\n // we rely upon the client certificate check to validate the scope key\n if (!easProjectId) {\n debug(\n `WARN: Expo Application Services (EAS) is not configured for your project. Configuring EAS enables a more secure development experience amongst many other benefits. ${learnMore(\n 'https://docs.expo.dev/eas/'\n )}`\n );\n return null;\n }\n\n const developmentCodeSigningInfoFromFile =\n await DevelopmentCodeSigningInfoFile.readAsync(easProjectId);\n const validatedCodeSigningInfo = validateStoredDevelopmentExpoRootCertificateCodeSigningInfo(\n developmentCodeSigningInfoFromFile,\n easProjectId\n );\n\n // 1. If online, ensure logged in, generate key pair and CSR, fetch and cache certificate chain for projectId\n // (overwriting existing dev cert in case projectId changed or it has expired)\n if (!env.EXPO_OFFLINE) {\n try {\n const newCodeSigningInfo =\n await fetchAndCacheNewDevelopmentCodeSigningInfoAsync(easProjectId);\n\n if (newCodeSigningInfo) {\n return newCodeSigningInfo;\n // fall back to cached certificate if we couldn't fetch a new one\n } else if (validatedCodeSigningInfo) {\n Log.warn(\n 'Could not fetch new Expo development certificate, falling back to cached certificate'\n );\n return validatedCodeSigningInfo;\n } else {\n return null;\n }\n } catch (e: any) {\n if (validatedCodeSigningInfo) {\n Log.warn(\n 'There was an error fetching the Expo development certificate, falling back to cached certificate'\n );\n return validatedCodeSigningInfo;\n } else {\n // need to return null here and say a message\n throw e;\n }\n }\n }\n\n // 2. check for cached cert/private key matching projectId and scopeKey of project, if found and valid return private key and cert chain including expo-go cert\n if (validatedCodeSigningInfo) {\n return validatedCodeSigningInfo;\n }\n\n // 3. if offline, return null\n Log.warn('Offline and no cached development certificate found, unable to sign manifest');\n return null;\n}\n\n/**\n * Get the certificate configured for expo-updates for this project.\n */\nasync function getProjectCodeSigningCertificateAsync(\n exp: ExpoConfig,\n privateKeyPath: string | undefined,\n expectedKeyId: string,\n expectedAlg: string | null\n): Promise<CodeSigningInfo | null> {\n const codeSigningCertificatePath = exp.updates?.codeSigningCertificate;\n if (!codeSigningCertificatePath) {\n return null;\n }\n\n if (!privateKeyPath) {\n throw new CommandError(\n 'Must specify --private-key-path argument to sign development manifest for requested code signing key'\n );\n }\n\n const codeSigningMetadata = exp.updates?.codeSigningMetadata;\n if (!codeSigningMetadata) {\n throw new CommandError(\n 'Must specify \"codeSigningMetadata\" under the \"updates\" field of your app config file to use EAS code signing'\n );\n }\n\n const { alg, keyid } = codeSigningMetadata;\n if (!alg || !keyid) {\n throw new CommandError(\n 'Must specify \"keyid\" and \"alg\" in the \"codeSigningMetadata\" field under the \"updates\" field of your app config file to use EAS code signing'\n );\n }\n\n if (expectedKeyId !== keyid) {\n throw new CommandError(`keyid mismatch: client=${expectedKeyId}, project=${keyid}`);\n }\n\n if (expectedAlg && expectedAlg !== alg) {\n throw new CommandError(`\"alg\" field mismatch (client=${expectedAlg}, project=${alg})`);\n }\n\n const { privateKeyPEM, certificatePEM } =\n await getProjectPrivateKeyAndCertificateFromFilePathsAsync({\n codeSigningCertificatePath,\n privateKeyPath,\n });\n\n return {\n keyId: keyid,\n privateKey: privateKeyPEM,\n certificateForPrivateKey: certificatePEM,\n certificateChainForResponse: [],\n scopeKey: null,\n };\n}\n\nasync function readFileWithErrorAsync(path: string, errorMessage: string): Promise<string> {\n try {\n return await fs.readFile(path, 'utf8');\n } catch {\n throw new CommandError(errorMessage);\n }\n}\n\nasync function getProjectPrivateKeyAndCertificateFromFilePathsAsync({\n codeSigningCertificatePath,\n privateKeyPath,\n}: {\n codeSigningCertificatePath: string;\n privateKeyPath: string;\n}): Promise<{ privateKeyPEM: string; certificatePEM: string }> {\n const [codeSigningCertificatePEM, privateKeyPEM] = await Promise.all([\n readFileWithErrorAsync(\n codeSigningCertificatePath,\n `Code signing certificate cannot be read from path: ${codeSigningCertificatePath}`\n ),\n readFileWithErrorAsync(\n privateKeyPath,\n `Code signing private key cannot be read from path: ${privateKeyPath}`\n ),\n ]);\n\n const privateKey = convertPrivateKeyPEMToPrivateKey(privateKeyPEM);\n const certificate = convertCertificatePEMToCertificate(codeSigningCertificatePEM);\n validateSelfSignedCertificate(certificate, {\n publicKey: certificate.publicKey as PKI.rsa.PublicKey,\n privateKey,\n });\n\n return { privateKeyPEM, certificatePEM: codeSigningCertificatePEM };\n}\n\n/**\n * Validate that the cached code signing info is still valid for the current project and\n * that it hasn't expired. If invalid, return null.\n */\nfunction validateStoredDevelopmentExpoRootCertificateCodeSigningInfo(\n codeSigningInfo: StoredDevelopmentExpoRootCodeSigningInfo,\n easProjectId: string\n): CodeSigningInfo | null {\n if (codeSigningInfo.easProjectId !== easProjectId) {\n return null;\n }\n\n const {\n privateKey: privateKeyPEM,\n certificateChain: certificatePEMs,\n scopeKey,\n } = codeSigningInfo;\n if (!privateKeyPEM || !certificatePEMs) {\n return null;\n }\n\n const certificateChain = certificatePEMs.map((certificatePEM) =>\n convertCertificatePEMToCertificate(certificatePEM)\n );\n\n // TODO(wschurman): maybe move to @expo/code-signing-certificates\n\n // ensure all intermediate certificates are valid\n for (const certificate of certificateChain) {\n const now = new Date();\n if (certificate.validity.notBefore > now || certificate.validity.notAfter < now) {\n return null;\n }\n }\n\n // TODO(wschurman): maybe do more validation, like validation of projectID and scopeKey within eas certificate extension\n\n return {\n keyId: 'expo-go',\n certificateChainForResponse: certificatePEMs,\n certificateForPrivateKey: certificatePEMs[0],\n privateKey: privateKeyPEM,\n scopeKey,\n };\n}\n\nfunction actorCanGetProjectDevelopmentCertificate(actor: Actor, app: App) {\n const owningAccountId = app.ownerAccount.id;\n\n const owningAccountIsActorPrimaryAccount =\n actor.__typename === 'User' || actor.__typename === 'SSOUser'\n ? actor.primaryAccount.id === owningAccountId\n : false;\n const userHasPublishPermissionForOwningAccount = !!actor.accounts\n .find((account) => account.id === owningAccountId)\n ?.users?.find((userPermission) => userPermission.actor.id === actor.id)\n ?.permissions?.includes('PUBLISH');\n return owningAccountIsActorPrimaryAccount || userHasPublishPermissionForOwningAccount;\n}\n\nasync function fetchAndCacheNewDevelopmentCodeSigningInfoAsync(\n easProjectId: string\n): Promise<CodeSigningInfo | null> {\n const actor = await tryGetUserAsync();\n\n if (!actor) {\n return null;\n }\n\n let app: App;\n try {\n app = await AppQuery.byIdAsync(easProjectId);\n } catch (e) {\n if (e instanceof UnexpectedServerError || e instanceof UnexpectedServerData) {\n return null;\n }\n throw e;\n }\n if (!actorCanGetProjectDevelopmentCertificate(actor, app)) {\n return null;\n }\n\n const keyPair = generateKeyPair();\n const keyPairPEM = convertKeyPairToPEM(keyPair);\n const csr = generateCSR(keyPair, `Development Certificate for ${easProjectId}`);\n const csrPEM = convertCSRToCSRPEM(csr);\n const [developmentSigningCertificate, expoGoIntermediateCertificate] = await Promise.all([\n getProjectDevelopmentCertificateAsync(easProjectId, csrPEM),\n getExpoGoIntermediateCertificateAsync(easProjectId),\n ]);\n\n await DevelopmentCodeSigningInfoFile.setAsync(easProjectId, {\n easProjectId,\n scopeKey: app.scopeKey,\n privateKey: keyPairPEM.privateKeyPEM,\n certificateChain: [developmentSigningCertificate, expoGoIntermediateCertificate],\n });\n\n return {\n keyId: 'expo-go',\n certificateChainForResponse: [developmentSigningCertificate, expoGoIntermediateCertificate],\n certificateForPrivateKey: developmentSigningCertificate,\n privateKey: keyPairPEM.privateKeyPEM,\n scopeKey: app.scopeKey,\n };\n}\n/**\n * Generate the `expo-signature` header for a manifest and code signing info.\n */\nexport function signManifestString(\n stringifiedManifest: string,\n codeSigningInfo: CodeSigningInfo\n): string {\n const privateKey = convertPrivateKeyPEMToPrivateKey(codeSigningInfo.privateKey);\n const certificate = convertCertificatePEMToCertificate(codeSigningInfo.certificateForPrivateKey);\n return signBufferRSASHA256AndVerify(\n privateKey,\n certificate,\n Buffer.from(stringifiedManifest, 'utf8')\n );\n}\n"],"names":["DevelopmentCodeSigningInfoFile","getCodeSigningInfoAsync","getDevelopmentCodeSigningDirectory","signManifestString","debug","require","DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME","path","join","getExpoHomeDirectory","assertBasenameValue","input","basename","CommandError","getProjectDevelopmentCodeSigningInfoFile","defaults","getFile","easProjectId","filePath","JsonFile","readAsync","projectSettings","writeAsync","ensureDir","setAsync","json","mergeAsync","cantReadFileDefault","scopeKey","privateKey","certificateChain","exp","expectSignatureHeader","privateKeyPath","parsedExpectSignature","parseDictionary","expectedKeyIdOuter","get","expectedKeyId","expectedAlg","expectedAlgOuter","expectedAlgTemp","getExpoRootDevelopmentCodeSigningInfoAsync","getProjectCodeSigningCertificateAsync","extra","eas","projectId","learnMore","developmentCodeSigningInfoFromFile","validatedCodeSigningInfo","validateStoredDevelopmentExpoRootCertificateCodeSigningInfo","env","EXPO_OFFLINE","newCodeSigningInfo","fetchAndCacheNewDevelopmentCodeSigningInfoAsync","Log","warn","e","codeSigningCertificatePath","updates","codeSigningCertificate","codeSigningMetadata","alg","keyid","privateKeyPEM","certificatePEM","getProjectPrivateKeyAndCertificateFromFilePathsAsync","keyId","certificateForPrivateKey","certificateChainForResponse","readFileWithErrorAsync","errorMessage","fs","readFile","codeSigningCertificatePEM","Promise","all","convertPrivateKeyPEMToPrivateKey","certificate","convertCertificatePEMToCertificate","validateSelfSignedCertificate","publicKey","codeSigningInfo","certificatePEMs","map","now","Date","validity","notBefore","notAfter","actorCanGetProjectDevelopmentCertificate","actor","app","owningAccountId","ownerAccount","id","owningAccountIsActorPrimaryAccount","__typename","primaryAccount","userHasPublishPermissionForOwningAccount","accounts","find","account","users","userPermission","permissions","includes","tryGetUserAsync","AppQuery","byIdAsync","UnexpectedServerError","UnexpectedServerData","keyPair","generateKeyPair","keyPairPEM","convertKeyPairToPEM","csr","generateCSR","csrPEM","convertCSRToCSRPEM","developmentSigningCertificate","expoGoIntermediateCertificate","getProjectDevelopmentCertificateAsync","getExpoGoIntermediateCertificateAsync","stringifiedManifest","signBufferRSASHA256AndVerify","Buffer","from"],"mappings":"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"}
|
package/build/src/utils/net.js
CHANGED
|
@@ -14,6 +14,9 @@ _export(exports, {
|
|
|
14
14
|
},
|
|
15
15
|
isMatchingOrigin: function() {
|
|
16
16
|
return isMatchingOrigin;
|
|
17
|
+
},
|
|
18
|
+
shouldThrottleRemoteDevCall: function() {
|
|
19
|
+
return shouldThrottleRemoteDevCall;
|
|
17
20
|
}
|
|
18
21
|
});
|
|
19
22
|
const ipv6To4Prefix = '::ffff:';
|
|
@@ -39,5 +42,15 @@ const isMatchingOrigin = (request, serverBaseUrl)=>{
|
|
|
39
42
|
const expectedHost = new URL(serverBaseUrl).host;
|
|
40
43
|
return actualHost === expectedHost;
|
|
41
44
|
};
|
|
45
|
+
const DEV_CALL_THROTTLE_MS = 2000;
|
|
46
|
+
let lastRemoteDevCallAt = 0;
|
|
47
|
+
const shouldThrottleRemoteDevCall = ()=>{
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
if (now - lastRemoteDevCallAt < DEV_CALL_THROTTLE_MS) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
lastRemoteDevCallAt = now;
|
|
53
|
+
return false;
|
|
54
|
+
};
|
|
42
55
|
|
|
43
56
|
//# sourceMappingURL=net.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/net.ts"],"sourcesContent":["import type { IncomingHttpHeaders } from 'node:http';\nimport type { Socket } from 'node:net';\n\nconst ipv6To4Prefix = '::ffff:';\n\nexport const isLocalSocket = (socket: Socket): boolean => {\n let { localAddress, remoteAddress, remoteFamily } = socket;\n\n const isLoopbackRequest = localAddress && localAddress === remoteAddress;\n if (isLoopbackRequest) {\n return true;\n } else if (!remoteAddress || !remoteFamily) {\n return false;\n }\n\n if (remoteFamily === 'IPv6' && remoteAddress.startsWith(ipv6To4Prefix)) {\n remoteAddress = remoteAddress.slice(ipv6To4Prefix.length);\n }\n\n return remoteAddress === '::1' || remoteAddress.startsWith('127.');\n};\n\ninterface AbstractIncomingMessage {\n headers: IncomingHttpHeaders | Record<string, string | string[]>;\n}\n\nexport const isMatchingOrigin = (\n request: AbstractIncomingMessage,\n serverBaseUrl: string\n): boolean => {\n // NOTE(@kitten): The browser will always send an origin header for websocket upgrade connections\n if (!request.headers.origin) {\n return true;\n }\n const actualHost = new URL(`${request.headers.origin}`).host;\n const expectedHost = new URL(serverBaseUrl).host;\n return actualHost === expectedHost;\n};\n"],"names":["isLocalSocket","isMatchingOrigin","ipv6To4Prefix","socket","localAddress","remoteAddress","remoteFamily","isLoopbackRequest","startsWith","slice","length","request","serverBaseUrl","headers","origin","actualHost","URL","host","expectedHost"],"mappings":";;;;;;;;;;;IAKaA,aAAa;eAAbA;;IAqBAC,gBAAgB;eAAhBA;;;
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/net.ts"],"sourcesContent":["import type { IncomingHttpHeaders } from 'node:http';\nimport type { Socket } from 'node:net';\n\nconst ipv6To4Prefix = '::ffff:';\n\nexport const isLocalSocket = (socket: Socket): boolean => {\n let { localAddress, remoteAddress, remoteFamily } = socket;\n\n const isLoopbackRequest = localAddress && localAddress === remoteAddress;\n if (isLoopbackRequest) {\n return true;\n } else if (!remoteAddress || !remoteFamily) {\n return false;\n }\n\n if (remoteFamily === 'IPv6' && remoteAddress.startsWith(ipv6To4Prefix)) {\n remoteAddress = remoteAddress.slice(ipv6To4Prefix.length);\n }\n\n return remoteAddress === '::1' || remoteAddress.startsWith('127.');\n};\n\ninterface AbstractIncomingMessage {\n headers: IncomingHttpHeaders | Record<string, string | string[]>;\n}\n\nexport const isMatchingOrigin = (\n request: AbstractIncomingMessage,\n serverBaseUrl: string\n): boolean => {\n // NOTE(@kitten): The browser will always send an origin header for websocket upgrade connections\n if (!request.headers.origin) {\n return true;\n }\n const actualHost = new URL(`${request.headers.origin}`).host;\n const expectedHost = new URL(serverBaseUrl).host;\n return actualHost === expectedHost;\n};\n\nconst DEV_CALL_THROTTLE_MS = 2_000;\nlet lastRemoteDevCallAt = 0;\n\n/** Process-wide throttle. Returns `true` if another call fired within the cooldown window. */\nexport const shouldThrottleRemoteDevCall = (): boolean => {\n const now = Date.now();\n if (now - lastRemoteDevCallAt < DEV_CALL_THROTTLE_MS) {\n return true;\n }\n lastRemoteDevCallAt = now;\n return false;\n};\n"],"names":["isLocalSocket","isMatchingOrigin","shouldThrottleRemoteDevCall","ipv6To4Prefix","socket","localAddress","remoteAddress","remoteFamily","isLoopbackRequest","startsWith","slice","length","request","serverBaseUrl","headers","origin","actualHost","URL","host","expectedHost","DEV_CALL_THROTTLE_MS","lastRemoteDevCallAt","now","Date"],"mappings":";;;;;;;;;;;IAKaA,aAAa;eAAbA;;IAqBAC,gBAAgB;eAAhBA;;IAiBAC,2BAA2B;eAA3BA;;;AAxCb,MAAMC,gBAAgB;AAEf,MAAMH,gBAAgB,CAACI;IAC5B,IAAI,EAAEC,YAAY,EAAEC,aAAa,EAAEC,YAAY,EAAE,GAAGH;IAEpD,MAAMI,oBAAoBH,gBAAgBA,iBAAiBC;IAC3D,IAAIE,mBAAmB;QACrB,OAAO;IACT,OAAO,IAAI,CAACF,iBAAiB,CAACC,cAAc;QAC1C,OAAO;IACT;IAEA,IAAIA,iBAAiB,UAAUD,cAAcG,UAAU,CAACN,gBAAgB;QACtEG,gBAAgBA,cAAcI,KAAK,CAACP,cAAcQ,MAAM;IAC1D;IAEA,OAAOL,kBAAkB,SAASA,cAAcG,UAAU,CAAC;AAC7D;AAMO,MAAMR,mBAAmB,CAC9BW,SACAC;IAEA,iGAAiG;IACjG,IAAI,CAACD,QAAQE,OAAO,CAACC,MAAM,EAAE;QAC3B,OAAO;IACT;IACA,MAAMC,aAAa,IAAIC,IAAI,GAAGL,QAAQE,OAAO,CAACC,MAAM,EAAE,EAAEG,IAAI;IAC5D,MAAMC,eAAe,IAAIF,IAAIJ,eAAeK,IAAI;IAChD,OAAOF,eAAeG;AACxB;AAEA,MAAMC,uBAAuB;AAC7B,IAAIC,sBAAsB;AAGnB,MAAMnB,8BAA8B;IACzC,MAAMoB,MAAMC,KAAKD,GAAG;IACpB,IAAIA,MAAMD,sBAAsBD,sBAAsB;QACpD,OAAO;IACT;IACAC,sBAAsBC;IACtB,OAAO;AACT"}
|
|
@@ -26,7 +26,7 @@ class FetchClient {
|
|
|
26
26
|
this.headers = {
|
|
27
27
|
accept: 'application/json',
|
|
28
28
|
'content-type': 'application/json',
|
|
29
|
-
'user-agent': `expo-cli/${"55.0.
|
|
29
|
+
'user-agent': `expo-cli/${"55.0.32"}`,
|
|
30
30
|
authorization: 'Basic ' + _nodebuffer().Buffer.from(`${target}:`).toString('base64')
|
|
31
31
|
};
|
|
32
32
|
}
|
package/build/src/utils/url.js
CHANGED
|
@@ -12,9 +12,6 @@ _export(exports, {
|
|
|
12
12
|
isUrlAvailableAsync: function() {
|
|
13
13
|
return isUrlAvailableAsync;
|
|
14
14
|
},
|
|
15
|
-
isUrlOk: function() {
|
|
16
|
-
return isUrlOk;
|
|
17
|
-
},
|
|
18
15
|
stripExtension: function() {
|
|
19
16
|
return stripExtension;
|
|
20
17
|
},
|
|
@@ -39,7 +36,6 @@ function _url() {
|
|
|
39
36
|
};
|
|
40
37
|
return data;
|
|
41
38
|
}
|
|
42
|
-
const _client = require("../api/rest/client");
|
|
43
39
|
function _interop_require_default(obj) {
|
|
44
40
|
return obj && obj.__esModule ? obj : {
|
|
45
41
|
default: obj
|
|
@@ -52,14 +48,6 @@ function isUrlAvailableAsync(url) {
|
|
|
52
48
|
});
|
|
53
49
|
});
|
|
54
50
|
}
|
|
55
|
-
async function isUrlOk(url) {
|
|
56
|
-
try {
|
|
57
|
-
const res = await (0, _client.fetchAsync)(url);
|
|
58
|
-
return res.ok;
|
|
59
|
-
} catch {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
51
|
function validateUrl(urlString, { protocols, requireProtocol } = {}) {
|
|
64
52
|
try {
|
|
65
53
|
const results = new (_url()).URL(urlString);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/url.ts"],"sourcesContent":["import dns from 'dns';\nimport { URL } from 'url';\n\
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/url.ts"],"sourcesContent":["import dns from 'dns';\nimport { URL } from 'url';\n\n/** Check if a server is available based on the URL. */\nexport function isUrlAvailableAsync(url: string): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n dns.lookup(url, (err) => {\n resolve(!err);\n });\n });\n}\n\n/** Determine if a string is a valid URL, can optionally ensure certain protocols (like `https` or `exp`) are adhered to. */\nexport function validateUrl(\n urlString: string,\n {\n protocols,\n requireProtocol,\n }: {\n /** Set of allowed protocols for the string to adhere to. @example ['exp', 'https'] */\n protocols?: string[];\n /** Ensure the URL has a protocol component (prefix before `://`). */\n requireProtocol?: boolean;\n } = {}\n) {\n try {\n const results = new URL(urlString);\n if (!results.protocol && !requireProtocol) {\n return true;\n }\n return protocols\n ? results.protocol\n ? protocols.map((x) => `${x.toLowerCase()}:`).includes(results.protocol)\n : false\n : true;\n } catch {\n return false;\n }\n}\n\n/** Remove the port from a given `host` URL string. */\nexport function stripPort(host?: string): string | null {\n return coerceUrl(host)?.hostname ?? null;\n}\n\nfunction coerceUrl(urlString?: string): URL | null {\n if (!urlString) {\n return null;\n }\n try {\n return new URL('/', urlString);\n } catch {\n return new URL('/', `http://${urlString}`);\n }\n}\n\n/** Strip a given extension from a URL string. */\nexport function stripExtension(url: string, extension: string): string {\n return url.replace(new RegExp(`.${extension}$`), '');\n}\n"],"names":["isUrlAvailableAsync","stripExtension","stripPort","validateUrl","url","Promise","resolve","dns","lookup","err","urlString","protocols","requireProtocol","results","URL","protocol","map","x","toLowerCase","includes","host","coerceUrl","hostname","extension","replace","RegExp"],"mappings":";;;;;;;;;;;IAIgBA,mBAAmB;eAAnBA;;IAqDAC,cAAc;eAAdA;;IAhBAC,SAAS;eAATA;;IA5BAC,WAAW;eAAXA;;;;gEAbA;;;;;;;yBACI;;;;;;;;;;;AAGb,SAASH,oBAAoBI,GAAW;IAC7C,OAAO,IAAIC,QAAiB,CAACC;QAC3BC,cAAG,CAACC,MAAM,CAACJ,KAAK,CAACK;YACfH,QAAQ,CAACG;QACX;IACF;AACF;AAGO,SAASN,YACdO,SAAiB,EACjB,EACEC,SAAS,EACTC,eAAe,EAMhB,GAAG,CAAC,CAAC;IAEN,IAAI;QACF,MAAMC,UAAU,IAAIC,CAAAA,MAAE,KAAC,CAACJ;QACxB,IAAI,CAACG,QAAQE,QAAQ,IAAI,CAACH,iBAAiB;YACzC,OAAO;QACT;QACA,OAAOD,YACHE,QAAQE,QAAQ,GACdJ,UAAUK,GAAG,CAAC,CAACC,IAAM,GAAGA,EAAEC,WAAW,GAAG,CAAC,CAAC,EAAEC,QAAQ,CAACN,QAAQE,QAAQ,IACrE,QACF;IACN,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAGO,SAASb,UAAUkB,IAAa;QAC9BC;IAAP,OAAOA,EAAAA,aAAAA,UAAUD,0BAAVC,WAAiBC,QAAQ,KAAI;AACtC;AAEA,SAASD,UAAUX,SAAkB;IACnC,IAAI,CAACA,WAAW;QACd,OAAO;IACT;IACA,IAAI;QACF,OAAO,IAAII,CAAAA,MAAE,KAAC,CAAC,KAAKJ;IACtB,EAAE,OAAM;QACN,OAAO,IAAII,CAAAA,MAAE,KAAC,CAAC,KAAK,CAAC,OAAO,EAAEJ,WAAW;IAC3C;AACF;AAGO,SAAST,eAAeG,GAAW,EAAEmB,SAAiB;IAC3D,OAAOnB,IAAIoB,OAAO,CAAC,IAAIC,OAAO,CAAC,CAAC,EAAEF,UAAU,CAAC,CAAC,GAAG;AACnD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/cli",
|
|
3
|
-
"version": "55.0.
|
|
3
|
+
"version": "55.0.32",
|
|
4
4
|
"description": "The Expo CLI",
|
|
5
5
|
"main": "build/bin/cli",
|
|
6
6
|
"bin": {
|
|
@@ -42,20 +42,20 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@expo/code-signing-certificates": "^0.0.6",
|
|
44
44
|
"@expo/config": "~55.0.17",
|
|
45
|
-
"@expo/config-plugins": "~55.0.
|
|
45
|
+
"@expo/config-plugins": "~55.0.10",
|
|
46
46
|
"@expo/devcert": "^1.2.1",
|
|
47
47
|
"@expo/env": "~2.1.2",
|
|
48
48
|
"@expo/image-utils": "^0.8.14",
|
|
49
|
-
"@expo/json-file": "^10.0.
|
|
49
|
+
"@expo/json-file": "^10.0.15",
|
|
50
50
|
"@expo/log-box": "55.0.12",
|
|
51
51
|
"@expo/metro": "~55.1.1",
|
|
52
|
-
"@expo/metro-config": "~55.0.
|
|
53
|
-
"@expo/osascript": "^2.4.
|
|
52
|
+
"@expo/metro-config": "~55.0.23",
|
|
53
|
+
"@expo/osascript": "^2.4.4",
|
|
54
54
|
"@expo/package-manager": "^1.10.5",
|
|
55
|
-
"@expo/plist": "^0.5.
|
|
55
|
+
"@expo/plist": "^0.5.4",
|
|
56
56
|
"@expo/prebuild-config": "^55.0.18",
|
|
57
57
|
"@expo/require-utils": "^55.0.5",
|
|
58
|
-
"@expo/router-server": "^55.0.
|
|
58
|
+
"@expo/router-server": "^55.0.18",
|
|
59
59
|
"@expo/schema-utils": "^55.0.4",
|
|
60
60
|
"@expo/spawn-async": "^1.7.2",
|
|
61
61
|
"@expo/ws-tunnel": "^1.0.1",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"connect": "^3.7.0",
|
|
73
73
|
"debug": "^4.3.4",
|
|
74
74
|
"dnssd-advertise": "^1.1.4",
|
|
75
|
-
"expo-server": "^55.0.
|
|
75
|
+
"expo-server": "^55.0.11",
|
|
76
76
|
"fetch-nodeshim": "^0.4.10",
|
|
77
77
|
"getenv": "^2.0.0",
|
|
78
78
|
"glob": "^13.0.0",
|
|
@@ -158,5 +158,5 @@
|
|
|
158
158
|
"tree-kill": "^1.2.2",
|
|
159
159
|
"tsd": "^0.28.1"
|
|
160
160
|
},
|
|
161
|
-
"gitHead": "
|
|
161
|
+
"gitHead": "bb1d4bd298e5bcaff86b04aabca7c56659e57138"
|
|
162
162
|
}
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
<title>Expo Start | Loading Page</title>
|
|
206
206
|
</head>
|
|
207
207
|
<body>
|
|
208
|
-
<div id="alert"></div>
|
|
208
|
+
<div id="alert" data-scheme="{{ Scheme }}"></div>
|
|
209
209
|
<div class="logo-box">
|
|
210
210
|
<div class="logo-background-box">
|
|
211
211
|
|
|
@@ -263,12 +263,20 @@
|
|
|
263
263
|
<script>
|
|
264
264
|
const alertElement = document.getElementById("alert");
|
|
265
265
|
|
|
266
|
+
const escapeHtml = (value) => value
|
|
267
|
+
.replace(/&/g, '&')
|
|
268
|
+
.replace(/</g, '<')
|
|
269
|
+
.replace(/>/g, '>')
|
|
270
|
+
.replace(/"/g, '"')
|
|
271
|
+
.replace(/'/g, ''');
|
|
272
|
+
|
|
266
273
|
const showAlert = (isExpoGo) => {
|
|
267
274
|
if (isExpoGo) {
|
|
268
275
|
alertElement.innerHTML = 'Unable to open in Expo Go. Please install <b>Expo Go</b> to continue.' +
|
|
269
276
|
'<br><a href="https://expo.dev/client">Learn more.</a>';
|
|
270
277
|
} else {
|
|
271
|
-
|
|
278
|
+
const scheme = escapeHtml(alertElement.dataset.scheme || 'Unknown');
|
|
279
|
+
alertElement.innerHTML = 'Unable to open in Development Build with the <b>' + scheme + '</b> scheme. Please build and install a compatible <b>Development Build</b> to continue.' +
|
|
272
280
|
'<br><a href="https://docs.expo.dev/development/build/">Learn more.</a>';
|
|
273
281
|
}
|
|
274
282
|
|