@expo/cli 56.1.5 → 56.1.7

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.
Files changed (94) hide show
  1. package/build/bin/cli +1 -1
  2. package/build/src/api/rest/client.js +27 -12
  3. package/build/src/api/rest/client.js.map +1 -1
  4. package/build/src/api/user/UserSettings.js +17 -4
  5. package/build/src/api/user/UserSettings.js.map +1 -1
  6. package/build/src/events/index.js +1 -1
  7. package/build/src/export/embed/exportEmbedAsync.js +3 -3
  8. package/build/src/export/embed/exportEmbedAsync.js.map +1 -1
  9. package/build/src/export/embed/exportServer.js +1 -1
  10. package/build/src/export/embed/exportServer.js.map +1 -1
  11. package/build/src/export/exportApp.js +1 -1
  12. package/build/src/export/exportApp.js.map +1 -1
  13. package/build/src/export/publicFolder.js +19 -1
  14. package/build/src/export/publicFolder.js.map +1 -1
  15. package/build/src/install/utils/checkPackagesCompatibility.js +32 -16
  16. package/build/src/install/utils/checkPackagesCompatibility.js.map +1 -1
  17. package/build/src/login/index.js +25 -5
  18. package/build/src/login/index.js.map +1 -1
  19. package/build/src/prebuild/resolveTemplate.js +10 -5
  20. package/build/src/prebuild/resolveTemplate.js.map +1 -1
  21. package/build/src/run/android/resolveLaunchProps.js +4 -1
  22. package/build/src/run/android/resolveLaunchProps.js.map +1 -1
  23. package/build/src/start/doctor/dependencies/reactNativeTv.js +149 -0
  24. package/build/src/start/doctor/dependencies/reactNativeTv.js.map +1 -0
  25. package/build/src/start/doctor/dependencies/validateDependenciesVersions.js +28 -3
  26. package/build/src/start/doctor/dependencies/validateDependenciesVersions.js.map +1 -1
  27. package/build/src/start/platforms/AppIdResolver.js +4 -0
  28. package/build/src/start/platforms/AppIdResolver.js.map +1 -1
  29. package/build/src/start/platforms/android/adb.js +16 -15
  30. package/build/src/start/platforms/android/adb.js.map +1 -1
  31. package/build/src/start/server/DevToolsPlugin.js +26 -1
  32. package/build/src/start/server/DevToolsPlugin.js.map +1 -1
  33. package/build/src/start/server/DevToolsPluginCliExtensionExecutor.js +57 -22
  34. package/build/src/start/server/DevToolsPluginCliExtensionExecutor.js.map +1 -1
  35. package/build/src/start/server/DevToolsPluginCliExtensionResults.js +29 -0
  36. package/build/src/start/server/DevToolsPluginCliExtensionResults.js.map +1 -1
  37. package/build/src/start/server/MCPDevToolsPluginCLIExtensions.js +15 -5
  38. package/build/src/start/server/MCPDevToolsPluginCLIExtensions.js.map +1 -1
  39. package/build/src/start/server/UrlCreator.js +14 -1
  40. package/build/src/start/server/UrlCreator.js.map +1 -1
  41. package/build/src/start/server/createMCPDevToolsExtensionSchema.js +13 -1
  42. package/build/src/start/server/createMCPDevToolsExtensionSchema.js.map +1 -1
  43. package/build/src/start/server/getStaticRenderFunctions.js +2 -1
  44. package/build/src/start/server/getStaticRenderFunctions.js.map +1 -1
  45. package/build/src/start/server/metro/MetroBundlerDevServer.js +44 -0
  46. package/build/src/start/server/metro/MetroBundlerDevServer.js.map +1 -1
  47. package/build/src/start/server/metro/createServerComponentsMiddleware.js +13 -13
  48. package/build/src/start/server/metro/createServerComponentsMiddleware.js.map +1 -1
  49. package/build/src/start/server/metro/debugging/createDebugMiddleware.js +5 -4
  50. package/build/src/start/server/metro/debugging/createDebugMiddleware.js.map +1 -1
  51. package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js +17 -1
  52. package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js.map +1 -1
  53. package/build/src/start/server/metro/dev-server/createMessageSocket.js +13 -2
  54. package/build/src/start/server/metro/dev-server/createMessageSocket.js.map +1 -1
  55. package/build/src/start/server/metro/dev-server/createMetroMiddleware.js +9 -2
  56. package/build/src/start/server/metro/dev-server/createMetroMiddleware.js.map +1 -1
  57. package/build/src/start/server/metro/instantiateMetro.js +8 -5
  58. package/build/src/start/server/metro/instantiateMetro.js.map +1 -1
  59. package/build/src/start/server/metro/metroErrorInterface.js +5 -2
  60. package/build/src/start/server/metro/metroErrorInterface.js.map +1 -1
  61. package/build/src/start/server/metro/router.js +10 -1
  62. package/build/src/start/server/metro/router.js.map +1 -1
  63. package/build/src/start/server/metro/withMetroMultiPlatform.js +8 -0
  64. package/build/src/start/server/metro/withMetroMultiPlatform.js.map +1 -1
  65. package/build/src/start/server/middleware/InterstitialPageMiddleware.js +7 -4
  66. package/build/src/start/server/middleware/InterstitialPageMiddleware.js.map +1 -1
  67. package/build/src/start/server/middleware/OpenMiddleware.js +150 -0
  68. package/build/src/start/server/middleware/OpenMiddleware.js.map +1 -0
  69. package/build/src/start/server/middleware/RuntimeRedirectMiddleware.js +13 -4
  70. package/build/src/start/server/middleware/RuntimeRedirectMiddleware.js.map +1 -1
  71. package/build/src/start/server/middleware/ServeStaticMiddleware.js +2 -9
  72. package/build/src/start/server/middleware/ServeStaticMiddleware.js.map +1 -1
  73. package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js +14 -24
  74. package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js.map +1 -1
  75. package/build/src/start/server/middleware/openHandlers.js +157 -0
  76. package/build/src/start/server/middleware/openHandlers.js.map +1 -0
  77. package/build/src/start/server/webTemplate.js +3 -5
  78. package/build/src/start/server/webTemplate.js.map +1 -1
  79. package/build/src/utils/codesigning.js +6 -0
  80. package/build/src/utils/codesigning.js.map +1 -1
  81. package/build/src/utils/env.js +29 -6
  82. package/build/src/utils/env.js.map +1 -1
  83. package/build/src/utils/net.js +20 -1
  84. package/build/src/utils/net.js.map +1 -1
  85. package/build/src/utils/open.js +2 -5
  86. package/build/src/utils/open.js.map +1 -1
  87. package/build/src/utils/tar.js +2 -2
  88. package/build/src/utils/tar.js.map +1 -1
  89. package/build/src/utils/telemetry/clients/FetchClient.js +1 -1
  90. package/build/src/utils/telemetry/utils/context.js +1 -1
  91. package/build/src/utils/url.js +0 -12
  92. package/build/src/utils/url.js.map +1 -1
  93. package/package.json +22 -22
  94. package/static/loading-page/index.html +10 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/start/server/middleware/RuntimeRedirectMiddleware.ts"],"sourcesContent":["import { parse } from 'url';\n\nimport { disableResponseCache, ExpoMiddleware } from './ExpoMiddleware';\nimport type { RuntimePlatform } from './resolvePlatform';\nimport {\n assertMissingRuntimePlatform,\n assertRuntimePlatform,\n parsePlatformHeader,\n resolvePlatformFromUserAgentHeader,\n} from './resolvePlatform';\nimport type { ServerRequest, ServerResponse } from './server.types';\nimport * as Log from '../../../log';\n\nconst debug = require('debug')(\n 'expo:start:server:middleware:runtimeRedirect'\n) as typeof console.log;\n\n/** Runtime to target: expo = Expo Go, custom = Dev Client. */\ntype RuntimeTarget = 'expo' | 'custom';\n\nexport type DeepLinkHandler = (props: {\n runtime: RuntimeTarget;\n platform: RuntimePlatform;\n}) => void | Promise<void>;\n\nexport class RuntimeRedirectMiddleware extends ExpoMiddleware {\n constructor(\n protected projectRoot: string,\n protected options: {\n onDeepLink?: DeepLinkHandler;\n getLocation: (props: { runtime: RuntimeTarget }) => string | null | undefined;\n }\n ) {\n super(projectRoot, ['/_expo/link']);\n }\n\n async handleRequestAsync(req: ServerRequest, res: ServerResponse): Promise<void> {\n const { query } = parse(req.url!, true);\n const isDevClient = query['choice'] === 'expo-dev-client';\n const platform = parsePlatformHeader(req) ?? resolvePlatformFromUserAgentHeader(req);\n assertMissingRuntimePlatform(platform);\n assertRuntimePlatform(platform);\n const runtime = isDevClient ? 'custom' : 'expo';\n\n debug(`props:`, { platform, runtime });\n\n this.options.onDeepLink?.({ runtime, platform });\n\n const redirect = this.options.getLocation({ runtime });\n if (!redirect) {\n Log.warn(\n `[redirect middleware]: Unable to determine redirect location for runtime '${runtime}' and platform '${platform}'`\n );\n res.statusCode = 404;\n res.end();\n return;\n }\n debug('Redirect ->', redirect);\n res.setHeader('Location', redirect);\n\n // Disable caching\n disableResponseCache(res);\n\n // 'Temporary Redirect'\n res.statusCode = 307;\n res.end();\n }\n}\n"],"names":["RuntimeRedirectMiddleware","debug","require","ExpoMiddleware","projectRoot","options","handleRequestAsync","req","res","query","parse","url","isDevClient","platform","parsePlatformHeader","resolvePlatformFromUserAgentHeader","assertMissingRuntimePlatform","assertRuntimePlatform","runtime","onDeepLink","redirect","getLocation","Log","warn","statusCode","end","setHeader","disableResponseCache"],"mappings":";;;;+BAyBaA;;;eAAAA;;;;yBAzBS;;;;;;gCAE+B;iCAO9C;6DAEc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAErB,MAAMC,QAAQC,QAAQ,SACpB;AAWK,MAAMF,kCAAkCG,8BAAc;IAC3D,YACE,AAAUC,WAAmB,EAC7B,AAAUC,OAGT,CACD;QACA,KAAK,CAACD,aAAa;YAAC;SAAc,QANxBA,cAAAA,kBACAC,UAAAA;IAMZ;IAEA,MAAMC,mBAAmBC,GAAkB,EAAEC,GAAmB,EAAiB;QAC/E,MAAM,EAAEC,KAAK,EAAE,GAAGC,IAAAA,YAAK,EAACH,IAAII,GAAG,EAAG;QAClC,MAAMC,cAAcH,KAAK,CAAC,SAAS,KAAK;QACxC,MAAMI,WAAWC,IAAAA,oCAAmB,EAACP,QAAQQ,IAAAA,mDAAkC,EAACR;QAChFS,IAAAA,6CAA4B,EAACH;QAC7BI,IAAAA,sCAAqB,EAACJ;QACtB,MAAMK,UAAUN,cAAc,WAAW;QAEzCX,MAAM,CAAC,MAAM,CAAC,EAAE;YAAEY;YAAUK;QAAQ;QAEpC,IAAI,CAACb,OAAO,CAACc,UAAU,oBAAvB,IAAI,CAACd,OAAO,CAACc,UAAU,MAAvB,IAAI,CAACd,OAAO,EAAc;YAAEa;YAASL;QAAS;QAE9C,MAAMO,WAAW,IAAI,CAACf,OAAO,CAACgB,WAAW,CAAC;YAAEH;QAAQ;QACpD,IAAI,CAACE,UAAU;YACbE,KAAIC,IAAI,CACN,CAAC,0EAA0E,EAAEL,QAAQ,gBAAgB,EAAEL,SAAS,CAAC,CAAC;YAEpHL,IAAIgB,UAAU,GAAG;YACjBhB,IAAIiB,GAAG;YACP;QACF;QACAxB,MAAM,eAAemB;QACrBZ,IAAIkB,SAAS,CAAC,YAAYN;QAE1B,kBAAkB;QAClBO,IAAAA,oCAAoB,EAACnB;QAErB,uBAAuB;QACvBA,IAAIgB,UAAU,GAAG;QACjBhB,IAAIiB,GAAG;IACT;AACF"}
1
+ {"version":3,"sources":["../../../../../src/start/server/middleware/RuntimeRedirectMiddleware.ts"],"sourcesContent":["import { parse } from 'url';\n\nimport { disableResponseCache, ExpoMiddleware } from './ExpoMiddleware';\nimport type { RuntimePlatform } from './resolvePlatform';\nimport {\n assertMissingRuntimePlatform,\n assertRuntimePlatform,\n parsePlatformHeader,\n resolvePlatformFromUserAgentHeader,\n} from './resolvePlatform';\nimport type { ServerRequest, ServerResponse } from './server.types';\nimport * as Log from '../../../log';\n\nconst debug = require('debug')(\n 'expo:start:server:middleware:runtimeRedirect'\n) as typeof console.log;\n\nexport const LinkEndpoint = '/_expo/link';\n\n/** Runtime to target: expo = Expo Go, custom = Dev Client. */\ntype RuntimeTarget = 'expo' | 'custom';\n\nexport type DeepLinkHandler = (props: {\n runtime: RuntimeTarget;\n platform: RuntimePlatform;\n}) => void | Promise<void>;\n\nexport class RuntimeRedirectMiddleware extends ExpoMiddleware {\n constructor(\n protected projectRoot: string,\n protected options: {\n onDeepLink?: DeepLinkHandler;\n getLocation: (props: { runtime: RuntimeTarget }) => string | null | undefined;\n }\n ) {\n super(projectRoot, [LinkEndpoint]);\n }\n\n async handleRequestAsync(req: ServerRequest, res: ServerResponse): Promise<void> {\n const { query } = parse(req.url!, true);\n const isDevClient = query['choice'] === 'expo-dev-client';\n const platform = parsePlatformHeader(req) ?? resolvePlatformFromUserAgentHeader(req);\n assertMissingRuntimePlatform(platform);\n assertRuntimePlatform(platform);\n const runtime = isDevClient ? 'custom' : 'expo';\n\n debug(`props:`, { platform, runtime });\n\n this.options.onDeepLink?.({ runtime, platform });\n\n const redirect = this.options.getLocation({ runtime });\n if (!redirect) {\n Log.warn(\n `[redirect middleware]: Unable to determine redirect location for runtime '${runtime}' and platform '${platform}'`\n );\n res.statusCode = 404;\n res.end();\n return;\n }\n debug('Redirect ->', redirect);\n res.setHeader('Location', redirect);\n\n // Disable caching\n disableResponseCache(res);\n\n // 'Temporary Redirect'\n res.statusCode = 307;\n res.end();\n }\n}\n"],"names":["LinkEndpoint","RuntimeRedirectMiddleware","debug","require","ExpoMiddleware","projectRoot","options","handleRequestAsync","req","res","query","parse","url","isDevClient","platform","parsePlatformHeader","resolvePlatformFromUserAgentHeader","assertMissingRuntimePlatform","assertRuntimePlatform","runtime","onDeepLink","redirect","getLocation","Log","warn","statusCode","end","setHeader","disableResponseCache"],"mappings":";;;;;;;;;;;QAiBaA;eAAAA;;QAUAC;eAAAA;;;;yBA3BS;;;;;;gCAE+B;iCAO9C;6DAEc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAErB,MAAMC,QAAQC,QAAQ,SACpB;AAGK,MAAMH,eAAe;AAUrB,MAAMC,kCAAkCG,8BAAc;IAC3D,YACE,AAAUC,WAAmB,EAC7B,AAAUC,OAGT,CACD;QACA,KAAK,CAACD,aAAa;YAACL;SAAa,QANvBK,cAAAA,kBACAC,UAAAA;IAMZ;IAEA,MAAMC,mBAAmBC,GAAkB,EAAEC,GAAmB,EAAiB;QAC/E,MAAM,EAAEC,KAAK,EAAE,GAAGC,IAAAA,YAAK,EAACH,IAAII,GAAG,EAAG;QAClC,MAAMC,cAAcH,KAAK,CAAC,SAAS,KAAK;QACxC,MAAMI,WAAWC,IAAAA,oCAAmB,EAACP,QAAQQ,IAAAA,mDAAkC,EAACR;QAChFS,IAAAA,6CAA4B,EAACH;QAC7BI,IAAAA,sCAAqB,EAACJ;QACtB,MAAMK,UAAUN,cAAc,WAAW;QAEzCX,MAAM,CAAC,MAAM,CAAC,EAAE;YAAEY;YAAUK;QAAQ;QAEpC,IAAI,CAACb,OAAO,CAACc,UAAU,oBAAvB,IAAI,CAACd,OAAO,CAACc,UAAU,MAAvB,IAAI,CAACd,OAAO,EAAc;YAAEa;YAASL;QAAS;QAE9C,MAAMO,WAAW,IAAI,CAACf,OAAO,CAACgB,WAAW,CAAC;YAAEH;QAAQ;QACpD,IAAI,CAACE,UAAU;YACbE,KAAIC,IAAI,CACN,CAAC,0EAA0E,EAAEL,QAAQ,gBAAgB,EAAEL,SAAS,CAAC,CAAC;YAEpHL,IAAIgB,UAAU,GAAG;YACjBhB,IAAIiB,GAAG;YACP;QACF;QACAxB,MAAM,eAAemB;QACrBZ,IAAIkB,SAAS,CAAC,YAAYN;QAE1B,kBAAkB;QAClBO,IAAAA,oCAAoB,EAACnB;QAErB,uBAAuB;QACvBA,IAAIgB,UAAU,GAAG;QACjBhB,IAAIiB,GAAG;IACT;AACF"}
@@ -8,13 +8,6 @@ Object.defineProperty(exports, "ServeStaticMiddleware", {
8
8
  return ServeStaticMiddleware;
9
9
  }
10
10
  });
11
- function _path() {
12
- const data = /*#__PURE__*/ _interop_require_default(require("path"));
13
- _path = function() {
14
- return data;
15
- };
16
- return data;
17
- }
18
11
  function _send() {
19
12
  const data = /*#__PURE__*/ _interop_require_default(require("send"));
20
13
  _send = function() {
@@ -30,7 +23,7 @@ function _url() {
30
23
  return data;
31
24
  }
32
25
  const _resolvePlatform = require("./resolvePlatform");
33
- const _env = require("../../../utils/env");
26
+ const _publicFolder = require("../../../export/publicFolder");
34
27
  function _interop_require_default(obj) {
35
28
  return obj && obj.__esModule ? obj : {
36
29
  default: obj
@@ -42,7 +35,7 @@ class ServeStaticMiddleware {
42
35
  this.projectRoot = projectRoot;
43
36
  }
44
37
  getHandler() {
45
- const publicPath = _path().default.join(this.projectRoot, _env.env.EXPO_PUBLIC_FOLDER);
38
+ const publicPath = (0, _publicFolder.getPublicFolderPath)(this.projectRoot);
46
39
  debug(`Serving static files from:`, publicPath);
47
40
  const opts = {
48
41
  root: publicPath
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/start/server/middleware/ServeStaticMiddleware.ts"],"sourcesContent":["import path from 'path';\nimport send from 'send';\nimport { parse } from 'url';\n\nimport { parsePlatformHeader } from './resolvePlatform';\nimport type { ServerRequest, ServerResponse } from './server.types';\nimport { env } from '../../../utils/env';\n\nconst debug = require('debug')('expo:start:server:middleware:serveStatic') as typeof console.log;\n\n/**\n * Adds support for serving the files in the static `public/` folder to web apps.\n */\nexport class ServeStaticMiddleware {\n constructor(private projectRoot: string) {}\n getHandler() {\n const publicPath = path.join(this.projectRoot, env.EXPO_PUBLIC_FOLDER);\n\n debug(`Serving static files from:`, publicPath);\n const opts = {\n root: publicPath,\n };\n return (req: ServerRequest, res: ServerResponse, next: any) => {\n if (!req?.url || (req.method !== 'GET' && req.method !== 'HEAD')) {\n return next();\n }\n\n const platform = parsePlatformHeader(req);\n // Currently this is web-only\n if (platform && platform !== 'web') {\n return next();\n }\n\n const pathname = parse(req.url).pathname;\n if (!pathname) {\n return next();\n }\n\n debug(`Maybe serve static:`, pathname);\n const stream = send(req, pathname, opts);\n\n // add file listener for fallthrough\n let forwardError = false;\n stream.on('file', function onFile() {\n // once file is determined, always forward error\n forwardError = true;\n });\n\n // forward errors\n stream.on('error', function error(err: any) {\n if (forwardError || !(err.statusCode < 500)) {\n next(err);\n return;\n }\n\n next();\n });\n\n // pipe\n stream.pipe(res);\n };\n }\n}\n"],"names":["ServeStaticMiddleware","debug","require","projectRoot","getHandler","publicPath","path","join","env","EXPO_PUBLIC_FOLDER","opts","root","req","res","next","url","method","platform","parsePlatformHeader","pathname","parse","stream","send","forwardError","on","onFile","error","err","statusCode","pipe"],"mappings":";;;;+BAaaA;;;eAAAA;;;;gEAbI;;;;;;;gEACA;;;;;;;yBACK;;;;;;iCAEc;qBAEhB;;;;;;AAEpB,MAAMC,QAAQC,QAAQ,SAAS;AAKxB,MAAMF;IACX,YAAY,AAAQG,WAAmB,CAAE;aAArBA,cAAAA;IAAsB;IAC1CC,aAAa;QACX,MAAMC,aAAaC,eAAI,CAACC,IAAI,CAAC,IAAI,CAACJ,WAAW,EAAEK,QAAG,CAACC,kBAAkB;QAErER,MAAM,CAAC,0BAA0B,CAAC,EAAEI;QACpC,MAAMK,OAAO;YACXC,MAAMN;QACR;QACA,OAAO,CAACO,KAAoBC,KAAqBC;YAC/C,IAAI,EAACF,uBAAAA,IAAKG,GAAG,KAAKH,IAAII,MAAM,KAAK,SAASJ,IAAII,MAAM,KAAK,QAAS;gBAChE,OAAOF;YACT;YAEA,MAAMG,WAAWC,IAAAA,oCAAmB,EAACN;YACrC,6BAA6B;YAC7B,IAAIK,YAAYA,aAAa,OAAO;gBAClC,OAAOH;YACT;YAEA,MAAMK,WAAWC,IAAAA,YAAK,EAACR,IAAIG,GAAG,EAAEI,QAAQ;YACxC,IAAI,CAACA,UAAU;gBACb,OAAOL;YACT;YAEAb,MAAM,CAAC,mBAAmB,CAAC,EAAEkB;YAC7B,MAAME,SAASC,IAAAA,eAAI,EAACV,KAAKO,UAAUT;YAEnC,oCAAoC;YACpC,IAAIa,eAAe;YACnBF,OAAOG,EAAE,CAAC,QAAQ,SAASC;gBACzB,gDAAgD;gBAChDF,eAAe;YACjB;YAEA,iBAAiB;YACjBF,OAAOG,EAAE,CAAC,SAAS,SAASE,MAAMC,GAAQ;gBACxC,IAAIJ,gBAAgB,CAAEI,CAAAA,IAAIC,UAAU,GAAG,GAAE,GAAI;oBAC3Cd,KAAKa;oBACL;gBACF;gBAEAb;YACF;YAEA,OAAO;YACPO,OAAOQ,IAAI,CAAChB;QACd;IACF;AACF"}
1
+ {"version":3,"sources":["../../../../../src/start/server/middleware/ServeStaticMiddleware.ts"],"sourcesContent":["import send from 'send';\nimport { parse } from 'url';\n\nimport { parsePlatformHeader } from './resolvePlatform';\nimport type { ServerRequest, ServerResponse } from './server.types';\nimport { getPublicFolderPath } from '../../../export/publicFolder';\n\nconst debug = require('debug')('expo:start:server:middleware:serveStatic') as typeof console.log;\n\n/**\n * Adds support for serving the files in the static `public/` folder to web apps.\n */\nexport class ServeStaticMiddleware {\n constructor(private projectRoot: string) {}\n getHandler() {\n const publicPath = getPublicFolderPath(this.projectRoot);\n\n debug(`Serving static files from:`, publicPath);\n const opts = {\n root: publicPath,\n };\n return (req: ServerRequest, res: ServerResponse, next: any) => {\n if (!req?.url || (req.method !== 'GET' && req.method !== 'HEAD')) {\n return next();\n }\n\n const platform = parsePlatformHeader(req);\n // Currently this is web-only\n if (platform && platform !== 'web') {\n return next();\n }\n\n const pathname = parse(req.url).pathname;\n if (!pathname) {\n return next();\n }\n\n debug(`Maybe serve static:`, pathname);\n const stream = send(req, pathname, opts);\n\n // add file listener for fallthrough\n let forwardError = false;\n stream.on('file', function onFile() {\n // once file is determined, always forward error\n forwardError = true;\n });\n\n // forward errors\n stream.on('error', function error(err: any) {\n if (forwardError || !(err.statusCode < 500)) {\n next(err);\n return;\n }\n\n next();\n });\n\n // pipe\n stream.pipe(res);\n };\n }\n}\n"],"names":["ServeStaticMiddleware","debug","require","projectRoot","getHandler","publicPath","getPublicFolderPath","opts","root","req","res","next","url","method","platform","parsePlatformHeader","pathname","parse","stream","send","forwardError","on","onFile","error","err","statusCode","pipe"],"mappings":";;;;+BAYaA;;;eAAAA;;;;gEAZI;;;;;;;yBACK;;;;;;iCAEc;8BAEA;;;;;;AAEpC,MAAMC,QAAQC,QAAQ,SAAS;AAKxB,MAAMF;IACX,YAAY,AAAQG,WAAmB,CAAE;aAArBA,cAAAA;IAAsB;IAC1CC,aAAa;QACX,MAAMC,aAAaC,IAAAA,iCAAmB,EAAC,IAAI,CAACH,WAAW;QAEvDF,MAAM,CAAC,0BAA0B,CAAC,EAAEI;QACpC,MAAME,OAAO;YACXC,MAAMH;QACR;QACA,OAAO,CAACI,KAAoBC,KAAqBC;YAC/C,IAAI,EAACF,uBAAAA,IAAKG,GAAG,KAAKH,IAAII,MAAM,KAAK,SAASJ,IAAII,MAAM,KAAK,QAAS;gBAChE,OAAOF;YACT;YAEA,MAAMG,WAAWC,IAAAA,oCAAmB,EAACN;YACrC,6BAA6B;YAC7B,IAAIK,YAAYA,aAAa,OAAO;gBAClC,OAAOH;YACT;YAEA,MAAMK,WAAWC,IAAAA,YAAK,EAACR,IAAIG,GAAG,EAAEI,QAAQ;YACxC,IAAI,CAACA,UAAU;gBACb,OAAOL;YACT;YAEAV,MAAM,CAAC,mBAAmB,CAAC,EAAEe;YAC7B,MAAME,SAASC,IAAAA,eAAI,EAACV,KAAKO,UAAUT;YAEnC,oCAAoC;YACpC,IAAIa,eAAe;YACnBF,OAAOG,EAAE,CAAC,QAAQ,SAASC;gBACzB,gDAAgD;gBAChDF,eAAe;YACjB;YAEA,iBAAiB;YACjBF,OAAOG,EAAE,CAAC,SAAS,SAASE,MAAMC,GAAQ;gBACxC,IAAIJ,gBAAgB,CAAEI,CAAAA,IAAIC,UAAU,GAAG,GAAE,GAAI;oBAC3Cd,KAAKa;oBACL;gBACF;gBAEAb;YACF;YAEA,OAAO;YACPO,OAAOQ,IAAI,CAAChB;QACd;IACF;AACF"}
@@ -15,20 +15,6 @@ function _chalk() {
15
15
  };
16
16
  return data;
17
17
  }
18
- function _net() {
19
- const data = /*#__PURE__*/ _interop_require_default(require("net"));
20
- _net = function() {
21
- return data;
22
- };
23
- return data;
24
- }
25
- function _tls() {
26
- const data = require("tls");
27
- _tls = function() {
28
- return data;
29
- };
30
- return data;
31
- }
32
18
  function _url() {
33
19
  const data = require("url");
34
20
  _url = function() {
@@ -37,20 +23,26 @@ function _url() {
37
23
  return data;
38
24
  }
39
25
  const _JsInspector = require("./JsInspector");
26
+ const _net = require("../../../../utils/net");
40
27
  function _interop_require_default(obj) {
41
28
  return obj && obj.__esModule ? obj : {
42
29
  default: obj
43
30
  };
44
31
  }
45
- function createJsInspectorMiddleware() {
32
+ function createJsInspectorMiddleware({ serverBaseUrl }) {
46
33
  return async function(req, res, next) {
47
- const { origin, searchParams } = new (_url()).URL(req.url ?? '/', getServerBase(req));
34
+ if (!(0, _net.isMatchingOrigin)(req, serverBaseUrl)) {
35
+ res.writeHead(403).end();
36
+ return;
37
+ }
38
+ // `req.url` may be absolute-form (HTTP/1.1) — only take search params from it.
39
+ const { searchParams } = new (_url()).URL(req.url ?? '/', serverBaseUrl);
48
40
  const appId = searchParams.get('appId') || searchParams.get('applicationId');
49
41
  if (!appId) {
50
42
  res.writeHead(400).end('Missing application identifier ("?appId=...")');
51
43
  return;
52
44
  }
53
- const app = await (0, _JsInspector.queryInspectorAppAsync)(origin, appId);
45
+ const app = await (0, _JsInspector.queryInspectorAppAsync)(serverBaseUrl, appId);
54
46
  if (!app) {
55
47
  res.writeHead(404).end('Unable to find inspector target from @react-native/dev-middleware');
56
48
  console.warn(_chalk().default.yellow('No compatible apps connected. JavaScript Debugging can only be used with the Hermes engine.'));
@@ -65,8 +57,12 @@ function createJsInspectorMiddleware() {
65
57
  });
66
58
  res.end(data);
67
59
  } else if (req.method === 'POST' || req.method === 'PUT') {
60
+ if ((0, _net.shouldThrottleRemoteDevCall)()) {
61
+ res.writeHead(429).end();
62
+ return;
63
+ }
68
64
  try {
69
- await (0, _JsInspector.openJsInspector)(origin, app);
65
+ await (0, _JsInspector.openJsInspector)(serverBaseUrl, app);
70
66
  } catch (error) {
71
67
  // abort(Error: Command failed: osascript -e POSIX path of (path to application "google chrome")
72
68
  // 15:50: execution error: Google Chrome got an error: Application isn’t running. (-600)
@@ -81,11 +77,5 @@ function createJsInspectorMiddleware() {
81
77
  }
82
78
  };
83
79
  }
84
- function getServerBase(req) {
85
- const scheme = req.socket instanceof _tls().TLSSocket && req.socket.encrypted === true ? 'https' : 'http';
86
- const { localAddress, localPort } = req.socket;
87
- const address = localAddress && _net().default.isIPv6(localAddress) ? `[${localAddress}]` : localAddress;
88
- return `${scheme}:${address}:${localPort}`;
89
- }
90
80
 
91
81
  //# sourceMappingURL=createJsInspectorMiddleware.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../src/start/server/middleware/inspector/createJsInspectorMiddleware.ts"],"sourcesContent":["import chalk from 'chalk';\nimport type { NextHandleFunction } from 'connect';\nimport type { IncomingMessage, ServerResponse } from 'http';\nimport net from 'net';\nimport { TLSSocket } from 'tls';\nimport { URL } from 'url';\n\nimport { openJsInspector, queryInspectorAppAsync } from './JsInspector';\n\n/**\n * Create a middleware that handles new requests to open the debugger from the dev menu.\n * @todo(cedric): delete this middleware once we fully swap over to the new React Native JS Inspector.\n */\nexport function createJsInspectorMiddleware(): NextHandleFunction {\n return async function (req: IncomingMessage, res: ServerResponse, next: (err?: Error) => void) {\n const { origin, searchParams } = new URL(req.url ?? '/', getServerBase(req));\n const appId = searchParams.get('appId') || searchParams.get('applicationId');\n if (!appId) {\n res.writeHead(400).end('Missing application identifier (\"?appId=...\")');\n return;\n }\n\n const app = await queryInspectorAppAsync(origin, appId);\n if (!app) {\n res.writeHead(404).end('Unable to find inspector target from @react-native/dev-middleware');\n console.warn(\n chalk.yellow(\n 'No compatible apps connected. JavaScript Debugging can only be used with the Hermes engine.'\n )\n );\n return;\n }\n\n if (req.method === 'GET') {\n const data = JSON.stringify(app);\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=UTF-8',\n 'Cache-Control': 'no-cache',\n 'Content-Length': data.length.toString(),\n });\n res.end(data);\n } else if (req.method === 'POST' || req.method === 'PUT') {\n try {\n await openJsInspector(origin, app);\n } catch (error: any) {\n // abort(Error: Command failed: osascript -e POSIX path of (path to application \"google chrome\")\n // 15:50: execution error: Google Chrome got an error: Application isn’t running. (-600)\n\n console.error(\n chalk.red('Error launching JS inspector: ' + (error?.message ?? 'Unknown error occurred'))\n );\n res.writeHead(500);\n res.end();\n return;\n }\n res.end();\n } else {\n res.writeHead(405);\n }\n };\n}\n\nfunction getServerBase(req: IncomingMessage): string {\n const scheme =\n req.socket instanceof TLSSocket && req.socket.encrypted === true ? 'https' : 'http';\n const { localAddress, localPort } = req.socket;\n const address = localAddress && net.isIPv6(localAddress) ? `[${localAddress}]` : localAddress;\n return `${scheme}:${address}:${localPort}`;\n}\n"],"names":["createJsInspectorMiddleware","req","res","next","origin","searchParams","URL","url","getServerBase","appId","get","writeHead","end","app","queryInspectorAppAsync","console","warn","chalk","yellow","method","data","JSON","stringify","length","toString","openJsInspector","error","red","message","scheme","socket","TLSSocket","encrypted","localAddress","localPort","address","net","isIPv6"],"mappings":";;;;+BAagBA;;;eAAAA;;;;gEAbE;;;;;;;gEAGF;;;;;;;yBACU;;;;;;;yBACN;;;;;;6BAEoC;;;;;;AAMjD,SAASA;IACd,OAAO,eAAgBC,GAAoB,EAAEC,GAAmB,EAAEC,IAA2B;QAC3F,MAAM,EAAEC,MAAM,EAAEC,YAAY,EAAE,GAAG,IAAIC,CAAAA,MAAE,KAAC,CAACL,IAAIM,GAAG,IAAI,KAAKC,cAAcP;QACvE,MAAMQ,QAAQJ,aAAaK,GAAG,CAAC,YAAYL,aAAaK,GAAG,CAAC;QAC5D,IAAI,CAACD,OAAO;YACVP,IAAIS,SAAS,CAAC,KAAKC,GAAG,CAAC;YACvB;QACF;QAEA,MAAMC,MAAM,MAAMC,IAAAA,mCAAsB,EAACV,QAAQK;QACjD,IAAI,CAACI,KAAK;YACRX,IAAIS,SAAS,CAAC,KAAKC,GAAG,CAAC;YACvBG,QAAQC,IAAI,CACVC,gBAAK,CAACC,MAAM,CACV;YAGJ;QACF;QAEA,IAAIjB,IAAIkB,MAAM,KAAK,OAAO;YACxB,MAAMC,OAAOC,KAAKC,SAAS,CAACT;YAC5BX,IAAIS,SAAS,CAAC,KAAK;gBACjB,gBAAgB;gBAChB,iBAAiB;gBACjB,kBAAkBS,KAAKG,MAAM,CAACC,QAAQ;YACxC;YACAtB,IAAIU,GAAG,CAACQ;QACV,OAAO,IAAInB,IAAIkB,MAAM,KAAK,UAAUlB,IAAIkB,MAAM,KAAK,OAAO;YACxD,IAAI;gBACF,MAAMM,IAAAA,4BAAe,EAACrB,QAAQS;YAChC,EAAE,OAAOa,OAAY;gBACnB,gGAAgG;gBAChG,wFAAwF;gBAExFX,QAAQW,KAAK,CACXT,gBAAK,CAACU,GAAG,CAAC,mCAAoCD,CAAAA,CAAAA,yBAAAA,MAAOE,OAAO,KAAI,wBAAuB;gBAEzF1B,IAAIS,SAAS,CAAC;gBACdT,IAAIU,GAAG;gBACP;YACF;YACAV,IAAIU,GAAG;QACT,OAAO;YACLV,IAAIS,SAAS,CAAC;QAChB;IACF;AACF;AAEA,SAASH,cAAcP,GAAoB;IACzC,MAAM4B,SACJ5B,IAAI6B,MAAM,YAAYC,gBAAS,IAAI9B,IAAI6B,MAAM,CAACE,SAAS,KAAK,OAAO,UAAU;IAC/E,MAAM,EAAEC,YAAY,EAAEC,SAAS,EAAE,GAAGjC,IAAI6B,MAAM;IAC9C,MAAMK,UAAUF,gBAAgBG,cAAG,CAACC,MAAM,CAACJ,gBAAgB,CAAC,CAAC,EAAEA,aAAa,CAAC,CAAC,GAAGA;IACjF,OAAO,GAAGJ,OAAO,CAAC,EAAEM,QAAQ,CAAC,EAAED,WAAW;AAC5C"}
1
+ {"version":3,"sources":["../../../../../../src/start/server/middleware/inspector/createJsInspectorMiddleware.ts"],"sourcesContent":["import chalk from 'chalk';\nimport type { NextHandleFunction } from 'connect';\nimport type { IncomingMessage, ServerResponse } from 'http';\nimport { URL } from 'url';\n\nimport { openJsInspector, queryInspectorAppAsync } from './JsInspector';\nimport { isMatchingOrigin, shouldThrottleRemoteDevCall } from '../../../../utils/net';\n\ninterface JsInspectorMiddlewareOptions {\n serverBaseUrl: string;\n}\n\n/**\n * Create a middleware that handles new requests to open the debugger from the dev menu.\n * @todo(cedric): delete this middleware once we fully swap over to the new React Native JS Inspector.\n */\nexport function createJsInspectorMiddleware({\n serverBaseUrl,\n}: JsInspectorMiddlewareOptions): NextHandleFunction {\n return async function (req: IncomingMessage, res: ServerResponse, next: (err?: Error) => void) {\n if (!isMatchingOrigin(req, serverBaseUrl)) {\n res.writeHead(403).end();\n return;\n }\n\n // `req.url` may be absolute-form (HTTP/1.1) — only take search params from it.\n const { searchParams } = new URL(req.url ?? '/', serverBaseUrl);\n const appId = searchParams.get('appId') || searchParams.get('applicationId');\n if (!appId) {\n res.writeHead(400).end('Missing application identifier (\"?appId=...\")');\n return;\n }\n\n const app = await queryInspectorAppAsync(serverBaseUrl, appId);\n if (!app) {\n res.writeHead(404).end('Unable to find inspector target from @react-native/dev-middleware');\n console.warn(\n chalk.yellow(\n 'No compatible apps connected. JavaScript Debugging can only be used with the Hermes engine.'\n )\n );\n return;\n }\n\n if (req.method === 'GET') {\n const data = JSON.stringify(app);\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=UTF-8',\n 'Cache-Control': 'no-cache',\n 'Content-Length': data.length.toString(),\n });\n res.end(data);\n } else if (req.method === 'POST' || req.method === 'PUT') {\n if (shouldThrottleRemoteDevCall()) {\n res.writeHead(429).end();\n return;\n }\n try {\n await openJsInspector(serverBaseUrl, app);\n } catch (error: any) {\n // abort(Error: Command failed: osascript -e POSIX path of (path to application \"google chrome\")\n // 15:50: execution error: Google Chrome got an error: Application isn’t running. (-600)\n\n console.error(\n chalk.red('Error launching JS inspector: ' + (error?.message ?? 'Unknown error occurred'))\n );\n res.writeHead(500);\n res.end();\n return;\n }\n res.end();\n } else {\n res.writeHead(405);\n }\n };\n}\n"],"names":["createJsInspectorMiddleware","serverBaseUrl","req","res","next","isMatchingOrigin","writeHead","end","searchParams","URL","url","appId","get","app","queryInspectorAppAsync","console","warn","chalk","yellow","method","data","JSON","stringify","length","toString","shouldThrottleRemoteDevCall","openJsInspector","error","red","message"],"mappings":";;;;+BAgBgBA;;;eAAAA;;;;gEAhBE;;;;;;;yBAGE;;;;;;6BAEoC;qBACM;;;;;;AAUvD,SAASA,4BAA4B,EAC1CC,aAAa,EACgB;IAC7B,OAAO,eAAgBC,GAAoB,EAAEC,GAAmB,EAAEC,IAA2B;QAC3F,IAAI,CAACC,IAAAA,qBAAgB,EAACH,KAAKD,gBAAgB;YACzCE,IAAIG,SAAS,CAAC,KAAKC,GAAG;YACtB;QACF;QAEA,+EAA+E;QAC/E,MAAM,EAAEC,YAAY,EAAE,GAAG,IAAIC,CAAAA,MAAE,KAAC,CAACP,IAAIQ,GAAG,IAAI,KAAKT;QACjD,MAAMU,QAAQH,aAAaI,GAAG,CAAC,YAAYJ,aAAaI,GAAG,CAAC;QAC5D,IAAI,CAACD,OAAO;YACVR,IAAIG,SAAS,CAAC,KAAKC,GAAG,CAAC;YACvB;QACF;QAEA,MAAMM,MAAM,MAAMC,IAAAA,mCAAsB,EAACb,eAAeU;QACxD,IAAI,CAACE,KAAK;YACRV,IAAIG,SAAS,CAAC,KAAKC,GAAG,CAAC;YACvBQ,QAAQC,IAAI,CACVC,gBAAK,CAACC,MAAM,CACV;YAGJ;QACF;QAEA,IAAIhB,IAAIiB,MAAM,KAAK,OAAO;YACxB,MAAMC,OAAOC,KAAKC,SAAS,CAACT;YAC5BV,IAAIG,SAAS,CAAC,KAAK;gBACjB,gBAAgB;gBAChB,iBAAiB;gBACjB,kBAAkBc,KAAKG,MAAM,CAACC,QAAQ;YACxC;YACArB,IAAII,GAAG,CAACa;QACV,OAAO,IAAIlB,IAAIiB,MAAM,KAAK,UAAUjB,IAAIiB,MAAM,KAAK,OAAO;YACxD,IAAIM,IAAAA,gCAA2B,KAAI;gBACjCtB,IAAIG,SAAS,CAAC,KAAKC,GAAG;gBACtB;YACF;YACA,IAAI;gBACF,MAAMmB,IAAAA,4BAAe,EAACzB,eAAeY;YACvC,EAAE,OAAOc,OAAY;gBACnB,gGAAgG;gBAChG,wFAAwF;gBAExFZ,QAAQY,KAAK,CACXV,gBAAK,CAACW,GAAG,CAAC,mCAAoCD,CAAAA,CAAAA,yBAAAA,MAAOE,OAAO,KAAI,wBAAuB;gBAEzF1B,IAAIG,SAAS,CAAC;gBACdH,IAAII,GAAG;gBACP;YACF;YACAJ,IAAII,GAAG;QACT,OAAO;YACLJ,IAAIG,SAAS,CAAC;QAChB;IACF;AACF"}
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get createInfoHandler () {
13
+ return createInfoHandler;
14
+ },
15
+ get createOpen () {
16
+ return createOpen;
17
+ },
18
+ get resolveOpenInfo () {
19
+ return resolveOpenInfo;
20
+ }
21
+ });
22
+ function createInfoHandler(deps) {
23
+ return ({ platform, runtime })=>resolveOpenInfo({
24
+ platform,
25
+ runtime
26
+ }, deps);
27
+ }
28
+ async function resolveOpenInfo({ platform, runtime }, deps) {
29
+ // Snapshot the live state once per request so the response is internally consistent even if a
30
+ // toggle happens between sub-resolutions.
31
+ const scheme = deps.urlCreator.getScheme();
32
+ const isDevClient = deps.getIsDevClient();
33
+ const isRedirectPageEnabled = deps.getIsRedirectPageEnabled();
34
+ const availableRuntimes = isDevClient ? [
35
+ 'custom'
36
+ ] : isRedirectPageEnabled ? [
37
+ 'expo',
38
+ 'custom'
39
+ ] : [
40
+ 'expo'
41
+ ];
42
+ if (platform) {
43
+ return {
44
+ scheme,
45
+ availableRuntimes,
46
+ ...await resolvePlatformInfo(platform, runtime, deps, {
47
+ isDevClient,
48
+ isRedirectPageEnabled
49
+ })
50
+ };
51
+ }
52
+ const [ios, android, web] = await Promise.all([
53
+ resolvePlatformInfo('ios', runtime, deps, {
54
+ isDevClient,
55
+ isRedirectPageEnabled
56
+ }),
57
+ resolvePlatformInfo('android', runtime, deps, {
58
+ isDevClient,
59
+ isRedirectPageEnabled
60
+ }),
61
+ resolvePlatformInfo('web', runtime, deps, {
62
+ isDevClient,
63
+ isRedirectPageEnabled
64
+ })
65
+ ]);
66
+ return {
67
+ scheme,
68
+ availableRuntimes,
69
+ platforms: {
70
+ ios,
71
+ android,
72
+ web
73
+ }
74
+ };
75
+ }
76
+ async function resolvePlatformInfo(platform, runtime, deps, state) {
77
+ const { urlCreator, getAppId } = deps;
78
+ const { isDevClient, isRedirectPageEnabled } = state;
79
+ const appId = await getAppId(platform);
80
+ if (platform === 'web') {
81
+ // constructUrl inherits the tunnel host from `defaults.hostType` when --tunnel is active,
82
+ // so this returns the ngrok URL instead of localhost in that case.
83
+ return {
84
+ runtime: 'web',
85
+ url: urlCreator.constructUrl({
86
+ scheme: 'http'
87
+ }),
88
+ appId
89
+ };
90
+ }
91
+ // Caller explicitly wants the disambiguation page — useful when they want the device (not the
92
+ // dev server) to pick between Expo Go and the dev build. No `runtime` field on the response
93
+ // since the actual runtime depends on the device's choice.
94
+ if (runtime === 'unknown') {
95
+ return {
96
+ url: urlCreator.constructLoadingUrl({}, platform),
97
+ appId
98
+ };
99
+ }
100
+ // `runtime: 'default'` mirrors what pressing `i` / `a` does in the terminal:
101
+ // --dev-client server → open the dev client directly.
102
+ // project has both → hand off to the disambiguation interstitial so the
103
+ // device resolves between Expo Go and the dev build.
104
+ // else → open Expo Go directly.
105
+ if (runtime === 'default') {
106
+ if (isDevClient) {
107
+ return {
108
+ runtime: 'custom',
109
+ url: urlCreator.constructDevClientUrl(),
110
+ appId
111
+ };
112
+ }
113
+ if (isRedirectPageEnabled) {
114
+ return {
115
+ url: urlCreator.constructLoadingUrl({}, platform),
116
+ appId
117
+ };
118
+ }
119
+ return {
120
+ runtime: 'expo',
121
+ url: urlCreator.constructUrl({
122
+ scheme: 'exp'
123
+ }),
124
+ appId
125
+ };
126
+ }
127
+ return {
128
+ runtime,
129
+ url: runtime === 'custom' ? urlCreator.constructDevClientUrl() : urlCreator.constructUrl({
130
+ scheme: 'exp'
131
+ }),
132
+ appId
133
+ };
134
+ }
135
+ function createOpen(deps) {
136
+ return async ({ platform })=>{
137
+ if (platform === 'web') {
138
+ const result = await deps.openPlatformAsync('desktop');
139
+ return {
140
+ platform,
141
+ runtime: 'web',
142
+ url: result.url ?? ''
143
+ };
144
+ }
145
+ const launchTarget = platform === 'ios' ? 'simulator' : 'emulator';
146
+ const result = await deps.openPlatformAsync(launchTarget, {
147
+ shouldPrompt: false
148
+ });
149
+ return {
150
+ platform,
151
+ runtime: deps.getIsDevClient() ? 'custom' : 'expo',
152
+ url: result.url ?? ''
153
+ };
154
+ };
155
+ }
156
+
157
+ //# sourceMappingURL=openHandlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/start/server/middleware/openHandlers.ts"],"sourcesContent":["import type {\n OpenActionResult,\n OpenInfoResult,\n OpenMiddlewareOptions,\n OpenNativeRuntime,\n OpenPlatform,\n OpenPlatformInfo,\n OpenRequestedRuntime,\n} from './OpenMiddleware';\nimport type { UrlCreator } from '../UrlCreator';\n\ninterface InfoHandlerDeps {\n /** Stable UrlCreator instance — its `defaults` mutate when `toggleRuntimeMode` runs, so the same instance keeps producing fresh URLs and reflects the current scheme. */\n urlCreator: UrlCreator;\n /**\n * Read live values every call. The dev server's runtime mode can flip mid-run via the `s` key\n * in the terminal, and `expo-dev-client` can be installed while the server is running — both\n * change `isDevClient` and `isRedirectPageEnabled`, and the endpoint should reflect the\n * current state on every request.\n */\n getIsDevClient: () => boolean;\n /** Live mirror of `BundlerDevServer.isRedirectPageEnabled()`. */\n getIsRedirectPageEnabled: () => boolean;\n /**\n * Resolve the native application identifier for a platform (iOS bundle id / Android package\n * name). Implementations should return `null` instead of throwing when the project has no\n * configured identifier; the endpoint surfaces `null` so distributed preview systems can detect\n * that the build can't be matched by app id and either bail out or prompt the user.\n */\n getAppId: (platform: OpenPlatform) => Promise<string | null>;\n}\n\n/**\n * Build the GET handler for `/_expo/open`. Resolves dry-run info for a single platform, or for\n * every platform in discovery mode. Extracted so it can be exercised with a real\n * {@link UrlCreator} in tests (covers tunnel routing in particular).\n */\nexport function createInfoHandler(deps: InfoHandlerDeps): OpenMiddlewareOptions['getInfo'] {\n return ({ platform, runtime }) => resolveOpenInfo({ platform, runtime }, deps);\n}\n\nexport async function resolveOpenInfo(\n { platform, runtime }: { platform: OpenPlatform | null; runtime: OpenRequestedRuntime },\n deps: InfoHandlerDeps\n): Promise<OpenInfoResult> {\n // Snapshot the live state once per request so the response is internally consistent even if a\n // toggle happens between sub-resolutions.\n const scheme = deps.urlCreator.getScheme();\n const isDevClient = deps.getIsDevClient();\n const isRedirectPageEnabled = deps.getIsRedirectPageEnabled();\n const availableRuntimes: OpenNativeRuntime[] = isDevClient\n ? ['custom']\n : isRedirectPageEnabled\n ? ['expo', 'custom']\n : ['expo'];\n\n if (platform) {\n return {\n scheme,\n availableRuntimes,\n ...(await resolvePlatformInfo(platform, runtime, deps, {\n isDevClient,\n isRedirectPageEnabled,\n })),\n };\n }\n\n const [ios, android, web] = await Promise.all([\n resolvePlatformInfo('ios', runtime, deps, { isDevClient, isRedirectPageEnabled }),\n resolvePlatformInfo('android', runtime, deps, { isDevClient, isRedirectPageEnabled }),\n resolvePlatformInfo('web', runtime, deps, { isDevClient, isRedirectPageEnabled }),\n ]);\n return { scheme, availableRuntimes, platforms: { ios, android, web } };\n}\n\nasync function resolvePlatformInfo(\n platform: OpenPlatform,\n runtime: OpenRequestedRuntime,\n deps: InfoHandlerDeps,\n state: { isDevClient: boolean; isRedirectPageEnabled: boolean }\n): Promise<OpenPlatformInfo> {\n const { urlCreator, getAppId } = deps;\n const { isDevClient, isRedirectPageEnabled } = state;\n const appId = await getAppId(platform);\n\n if (platform === 'web') {\n // constructUrl inherits the tunnel host from `defaults.hostType` when --tunnel is active,\n // so this returns the ngrok URL instead of localhost in that case.\n return { runtime: 'web', url: urlCreator.constructUrl({ scheme: 'http' }), appId };\n }\n\n // Caller explicitly wants the disambiguation page — useful when they want the device (not the\n // dev server) to pick between Expo Go and the dev build. No `runtime` field on the response\n // since the actual runtime depends on the device's choice.\n if (runtime === 'unknown') {\n return { url: urlCreator.constructLoadingUrl({}, platform), appId };\n }\n\n // `runtime: 'default'` mirrors what pressing `i` / `a` does in the terminal:\n // --dev-client server → open the dev client directly.\n // project has both → hand off to the disambiguation interstitial so the\n // device resolves between Expo Go and the dev build.\n // else → open Expo Go directly.\n if (runtime === 'default') {\n if (isDevClient) {\n return { runtime: 'custom', url: urlCreator.constructDevClientUrl(), appId };\n }\n if (isRedirectPageEnabled) {\n return { url: urlCreator.constructLoadingUrl({}, platform), appId };\n }\n return { runtime: 'expo', url: urlCreator.constructUrl({ scheme: 'exp' }), appId };\n }\n\n return {\n runtime,\n url:\n runtime === 'custom'\n ? urlCreator.constructDevClientUrl()\n : urlCreator.constructUrl({ scheme: 'exp' }),\n appId,\n };\n}\n\ninterface OpenHandlerDeps {\n /** Live `BundlerDevServer.isDevClient` — `s` in the terminal can flip this between dispatch and response. */\n getIsDevClient: () => boolean;\n /** Same shape as `BundlerDevServer.openPlatformAsync`. */\n openPlatformAsync: (\n launchTarget: 'simulator' | 'emulator' | 'desktop',\n resolver?: { shouldPrompt?: boolean }\n ) => Promise<{ url: string | null }>;\n}\n\n/** Build the POST handler for `/_expo/open` — dispatches to the dev server's platform launcher. */\nexport function createOpen(deps: OpenHandlerDeps): OpenMiddlewareOptions['open'] {\n return async ({ platform }): Promise<OpenActionResult> => {\n if (platform === 'web') {\n const result = await deps.openPlatformAsync('desktop');\n return { platform, runtime: 'web', url: result.url ?? '' };\n }\n const launchTarget = platform === 'ios' ? 'simulator' : 'emulator';\n const result = await deps.openPlatformAsync(launchTarget, { shouldPrompt: false });\n return {\n platform,\n runtime: deps.getIsDevClient() ? 'custom' : 'expo',\n url: result.url ?? '',\n };\n };\n}\n"],"names":["createInfoHandler","createOpen","resolveOpenInfo","deps","platform","runtime","scheme","urlCreator","getScheme","isDevClient","getIsDevClient","isRedirectPageEnabled","getIsRedirectPageEnabled","availableRuntimes","resolvePlatformInfo","ios","android","web","Promise","all","platforms","state","getAppId","appId","url","constructUrl","constructLoadingUrl","constructDevClientUrl","result","openPlatformAsync","launchTarget","shouldPrompt"],"mappings":"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"}
@@ -39,7 +39,7 @@ function _path() {
39
39
  }
40
40
  const _templates = require("../../customize/templates");
41
41
  const _html = require("../../export/html");
42
- const _env = require("../../utils/env");
42
+ const _publicFolder = require("../../export/publicFolder");
43
43
  function _interop_require_default(obj) {
44
44
  return obj && obj.__esModule ? obj : {
45
45
  default: obj
@@ -58,8 +58,8 @@ async function createTemplateHtmlFromExpoConfigAsync(projectRoot, { scripts, css
58
58
  themeColor: (_exp_web2 = exp.web) == null ? void 0 : _exp_web2.themeColor
59
59
  });
60
60
  }
61
- function getFileFromLocalPublicFolder(projectRoot, { publicFolder, filePath }) {
62
- const localFilePath = _path().default.resolve(projectRoot, publicFolder, filePath);
61
+ function getFileFromLocalPublicFolder(projectRoot, { filePath }) {
62
+ const localFilePath = _path().default.resolve((0, _publicFolder.getPublicFolderPath)(projectRoot), filePath);
63
63
  if (!_fs().default.existsSync(localFilePath)) {
64
64
  return null;
65
65
  }
@@ -67,8 +67,6 @@ function getFileFromLocalPublicFolder(projectRoot, { publicFolder, filePath }) {
67
67
  }
68
68
  /** Attempt to read the `index.html` from the local project before falling back on the template `index.html`. */ async function getTemplateIndexHtmlAsync(projectRoot) {
69
69
  let filePath = getFileFromLocalPublicFolder(projectRoot, {
70
- // TODO: Maybe use the app.json override.
71
- publicFolder: _env.env.EXPO_PUBLIC_FOLDER,
72
70
  filePath: 'index.html'
73
71
  });
74
72
  if (!filePath) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/start/server/webTemplate.ts"],"sourcesContent":["import type { ExpoConfig } from '@expo/config';\nimport { getConfig, getNameFromConfig } from '@expo/config';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { TEMPLATES } from '../../customize/templates';\nimport { appendLinkToHtml, appendScriptsToHtml } from '../../export/html';\nimport { env } from '../../utils/env';\n\n/**\n * Create a static HTML for SPA styled websites.\n * This method attempts to reuse the same patterns as `@expo/webpack-config`.\n */\nexport async function createTemplateHtmlFromExpoConfigAsync(\n projectRoot: string,\n {\n scripts,\n cssLinks,\n exp = getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp,\n }: {\n scripts: string[];\n cssLinks?: string[];\n exp?: ExpoConfig;\n }\n) {\n return createTemplateHtmlAsync(projectRoot, {\n langIsoCode: exp.web?.lang ?? 'en',\n scripts,\n cssLinks,\n title: getNameFromConfig(exp).webName ?? 'Expo App',\n description: exp.web?.description,\n themeColor: exp.web?.themeColor,\n });\n}\n\nfunction getFileFromLocalPublicFolder(\n projectRoot: string,\n { publicFolder, filePath }: { publicFolder: string; filePath: string }\n): string | null {\n const localFilePath = path.resolve(projectRoot, publicFolder, filePath);\n if (!fs.existsSync(localFilePath)) {\n return null;\n }\n return localFilePath;\n}\n\n/** Attempt to read the `index.html` from the local project before falling back on the template `index.html`. */\nasync function getTemplateIndexHtmlAsync(projectRoot: string): Promise<string> {\n let filePath = getFileFromLocalPublicFolder(projectRoot, {\n // TODO: Maybe use the app.json override.\n publicFolder: env.EXPO_PUBLIC_FOLDER,\n filePath: 'index.html',\n });\n if (!filePath) {\n filePath = TEMPLATES.find((value) => value.id === 'index.html')!.file(projectRoot);\n }\n return fs.promises.readFile(filePath, 'utf8');\n}\n\n/** Return an `index.html` string with template values added. */\nexport async function createTemplateHtmlAsync(\n projectRoot: string,\n {\n scripts,\n cssLinks,\n description,\n langIsoCode,\n title,\n themeColor,\n }: {\n scripts: string[];\n cssLinks?: string[];\n description?: string;\n langIsoCode: string;\n title: string;\n themeColor?: string;\n }\n): Promise<string> {\n // Resolve the best possible index.html template file.\n let contents = await getTemplateIndexHtmlAsync(projectRoot);\n\n contents = contents.replace('%LANG_ISO_CODE%', langIsoCode);\n contents = contents.replace('%WEB_TITLE%', title);\n\n contents = appendScriptsToHtml(contents, scripts);\n\n if (cssLinks) {\n contents = appendLinkToHtml(\n contents,\n cssLinks\n .map((href) => [\n // NOTE: We probably don't have to preload the CSS files for SPA-styled websites.\n {\n as: 'style',\n rel: 'preload',\n href,\n },\n {\n rel: 'stylesheet',\n href,\n },\n ])\n .flat()\n );\n }\n\n if (themeColor) {\n contents = addMeta(contents, `name=\"theme-color\" content=\"${themeColor}\"`);\n }\n\n if (description) {\n contents = addMeta(contents, `name=\"description\" content=\"${description}\"`);\n }\n\n return contents;\n}\n\n/** Add a `<meta />` tag to the `<head />` element. */\nfunction addMeta(contents: string, meta: string): string {\n return contents.replace('</head>', `<meta ${meta}>\\n</head>`);\n}\n"],"names":["createTemplateHtmlAsync","createTemplateHtmlFromExpoConfigAsync","projectRoot","scripts","cssLinks","exp","getConfig","skipSDKVersionRequirement","langIsoCode","web","lang","title","getNameFromConfig","webName","description","themeColor","getFileFromLocalPublicFolder","publicFolder","filePath","localFilePath","path","resolve","fs","existsSync","getTemplateIndexHtmlAsync","env","EXPO_PUBLIC_FOLDER","TEMPLATES","find","value","id","file","promises","readFile","contents","replace","appendScriptsToHtml","appendLinkToHtml","map","href","as","rel","flat","addMeta","meta"],"mappings":";;;;;;;;;;;QA4DsBA;eAAAA;;QA/CAC;eAAAA;;;;yBAZuB;;;;;;;gEAC9B;;;;;;;gEACE;;;;;;2BAES;sBAC4B;qBAClC;;;;;;AAMb,eAAeA,sCACpBC,WAAmB,EACnB,EACEC,OAAO,EACPC,QAAQ,EACRC,MAAMC,IAAAA,mBAAS,EAACJ,aAAa;IAAEK,2BAA2B;AAAK,GAAGF,GAAG,EAKtE;QAGcA,UAIAA,WACDA;IANd,OAAOL,wBAAwBE,aAAa;QAC1CM,aAAaH,EAAAA,WAAAA,IAAII,GAAG,qBAAPJ,SAASK,IAAI,KAAI;QAC9BP;QACAC;QACAO,OAAOC,IAAAA,2BAAiB,EAACP,KAAKQ,OAAO,IAAI;QACzCC,WAAW,GAAET,YAAAA,IAAII,GAAG,qBAAPJ,UAASS,WAAW;QACjCC,UAAU,GAAEV,YAAAA,IAAII,GAAG,qBAAPJ,UAASU,UAAU;IACjC;AACF;AAEA,SAASC,6BACPd,WAAmB,EACnB,EAAEe,YAAY,EAAEC,QAAQ,EAA8C;IAEtE,MAAMC,gBAAgBC,eAAI,CAACC,OAAO,CAACnB,aAAae,cAAcC;IAC9D,IAAI,CAACI,aAAE,CAACC,UAAU,CAACJ,gBAAgB;QACjC,OAAO;IACT;IACA,OAAOA;AACT;AAEA,8GAA8G,GAC9G,eAAeK,0BAA0BtB,WAAmB;IAC1D,IAAIgB,WAAWF,6BAA6Bd,aAAa;QACvD,yCAAyC;QACzCe,cAAcQ,QAAG,CAACC,kBAAkB;QACpCR,UAAU;IACZ;IACA,IAAI,CAACA,UAAU;QACbA,WAAWS,oBAAS,CAACC,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAK,cAAeC,IAAI,CAAC7B;IACxE;IACA,OAAOoB,aAAE,CAACU,QAAQ,CAACC,QAAQ,CAACf,UAAU;AACxC;AAGO,eAAelB,wBACpBE,WAAmB,EACnB,EACEC,OAAO,EACPC,QAAQ,EACRU,WAAW,EACXN,WAAW,EACXG,KAAK,EACLI,UAAU,EAQX;IAED,sDAAsD;IACtD,IAAImB,WAAW,MAAMV,0BAA0BtB;IAE/CgC,WAAWA,SAASC,OAAO,CAAC,mBAAmB3B;IAC/C0B,WAAWA,SAASC,OAAO,CAAC,eAAexB;IAE3CuB,WAAWE,IAAAA,yBAAmB,EAACF,UAAU/B;IAEzC,IAAIC,UAAU;QACZ8B,WAAWG,IAAAA,sBAAgB,EACzBH,UACA9B,SACGkC,GAAG,CAAC,CAACC,OAAS;gBACb,iFAAiF;gBACjF;oBACEC,IAAI;oBACJC,KAAK;oBACLF;gBACF;gBACA;oBACEE,KAAK;oBACLF;gBACF;aACD,EACAG,IAAI;IAEX;IAEA,IAAI3B,YAAY;QACdmB,WAAWS,QAAQT,UAAU,CAAC,4BAA4B,EAAEnB,WAAW,CAAC,CAAC;IAC3E;IAEA,IAAID,aAAa;QACfoB,WAAWS,QAAQT,UAAU,CAAC,4BAA4B,EAAEpB,YAAY,CAAC,CAAC;IAC5E;IAEA,OAAOoB;AACT;AAEA,oDAAoD,GACpD,SAASS,QAAQT,QAAgB,EAAEU,IAAY;IAC7C,OAAOV,SAASC,OAAO,CAAC,WAAW,CAAC,MAAM,EAAES,KAAK,UAAU,CAAC;AAC9D"}
1
+ {"version":3,"sources":["../../../../src/start/server/webTemplate.ts"],"sourcesContent":["import type { ExpoConfig } from '@expo/config';\nimport { getConfig, getNameFromConfig } from '@expo/config';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { TEMPLATES } from '../../customize/templates';\nimport { appendLinkToHtml, appendScriptsToHtml } from '../../export/html';\nimport { getPublicFolderPath } from '../../export/publicFolder';\n\n/**\n * Create a static HTML for SPA styled websites.\n * This method attempts to reuse the same patterns as `@expo/webpack-config`.\n */\nexport async function createTemplateHtmlFromExpoConfigAsync(\n projectRoot: string,\n {\n scripts,\n cssLinks,\n exp = getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp,\n }: {\n scripts: string[];\n cssLinks?: string[];\n exp?: ExpoConfig;\n }\n) {\n return createTemplateHtmlAsync(projectRoot, {\n langIsoCode: exp.web?.lang ?? 'en',\n scripts,\n cssLinks,\n title: getNameFromConfig(exp).webName ?? 'Expo App',\n description: exp.web?.description,\n themeColor: exp.web?.themeColor,\n });\n}\n\nfunction getFileFromLocalPublicFolder(\n projectRoot: string,\n { filePath }: { filePath: string }\n): string | null {\n const localFilePath = path.resolve(getPublicFolderPath(projectRoot), filePath);\n if (!fs.existsSync(localFilePath)) {\n return null;\n }\n return localFilePath;\n}\n\n/** Attempt to read the `index.html` from the local project before falling back on the template `index.html`. */\nasync function getTemplateIndexHtmlAsync(projectRoot: string): Promise<string> {\n let filePath = getFileFromLocalPublicFolder(projectRoot, {\n filePath: 'index.html',\n });\n if (!filePath) {\n filePath = TEMPLATES.find((value) => value.id === 'index.html')!.file(projectRoot);\n }\n return fs.promises.readFile(filePath, 'utf8');\n}\n\n/** Return an `index.html` string with template values added. */\nexport async function createTemplateHtmlAsync(\n projectRoot: string,\n {\n scripts,\n cssLinks,\n description,\n langIsoCode,\n title,\n themeColor,\n }: {\n scripts: string[];\n cssLinks?: string[];\n description?: string;\n langIsoCode: string;\n title: string;\n themeColor?: string;\n }\n): Promise<string> {\n // Resolve the best possible index.html template file.\n let contents = await getTemplateIndexHtmlAsync(projectRoot);\n\n contents = contents.replace('%LANG_ISO_CODE%', langIsoCode);\n contents = contents.replace('%WEB_TITLE%', title);\n\n contents = appendScriptsToHtml(contents, scripts);\n\n if (cssLinks) {\n contents = appendLinkToHtml(\n contents,\n cssLinks\n .map((href) => [\n // NOTE: We probably don't have to preload the CSS files for SPA-styled websites.\n {\n as: 'style',\n rel: 'preload',\n href,\n },\n {\n rel: 'stylesheet',\n href,\n },\n ])\n .flat()\n );\n }\n\n if (themeColor) {\n contents = addMeta(contents, `name=\"theme-color\" content=\"${themeColor}\"`);\n }\n\n if (description) {\n contents = addMeta(contents, `name=\"description\" content=\"${description}\"`);\n }\n\n return contents;\n}\n\n/** Add a `<meta />` tag to the `<head />` element. */\nfunction addMeta(contents: string, meta: string): string {\n return contents.replace('</head>', `<meta ${meta}>\\n</head>`);\n}\n"],"names":["createTemplateHtmlAsync","createTemplateHtmlFromExpoConfigAsync","projectRoot","scripts","cssLinks","exp","getConfig","skipSDKVersionRequirement","langIsoCode","web","lang","title","getNameFromConfig","webName","description","themeColor","getFileFromLocalPublicFolder","filePath","localFilePath","path","resolve","getPublicFolderPath","fs","existsSync","getTemplateIndexHtmlAsync","TEMPLATES","find","value","id","file","promises","readFile","contents","replace","appendScriptsToHtml","appendLinkToHtml","map","href","as","rel","flat","addMeta","meta"],"mappings":";;;;;;;;;;;QA0DsBA;eAAAA;;QA7CAC;eAAAA;;;;yBAZuB;;;;;;;gEAC9B;;;;;;;gEACE;;;;;;2BAES;sBAC4B;8BAClB;;;;;;AAM7B,eAAeA,sCACpBC,WAAmB,EACnB,EACEC,OAAO,EACPC,QAAQ,EACRC,MAAMC,IAAAA,mBAAS,EAACJ,aAAa;IAAEK,2BAA2B;AAAK,GAAGF,GAAG,EAKtE;QAGcA,UAIAA,WACDA;IANd,OAAOL,wBAAwBE,aAAa;QAC1CM,aAAaH,EAAAA,WAAAA,IAAII,GAAG,qBAAPJ,SAASK,IAAI,KAAI;QAC9BP;QACAC;QACAO,OAAOC,IAAAA,2BAAiB,EAACP,KAAKQ,OAAO,IAAI;QACzCC,WAAW,GAAET,YAAAA,IAAII,GAAG,qBAAPJ,UAASS,WAAW;QACjCC,UAAU,GAAEV,YAAAA,IAAII,GAAG,qBAAPJ,UAASU,UAAU;IACjC;AACF;AAEA,SAASC,6BACPd,WAAmB,EACnB,EAAEe,QAAQ,EAAwB;IAElC,MAAMC,gBAAgBC,eAAI,CAACC,OAAO,CAACC,IAAAA,iCAAmB,EAACnB,cAAce;IACrE,IAAI,CAACK,aAAE,CAACC,UAAU,CAACL,gBAAgB;QACjC,OAAO;IACT;IACA,OAAOA;AACT;AAEA,8GAA8G,GAC9G,eAAeM,0BAA0BtB,WAAmB;IAC1D,IAAIe,WAAWD,6BAA6Bd,aAAa;QACvDe,UAAU;IACZ;IACA,IAAI,CAACA,UAAU;QACbA,WAAWQ,oBAAS,CAACC,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAK,cAAeC,IAAI,CAAC3B;IACxE;IACA,OAAOoB,aAAE,CAACQ,QAAQ,CAACC,QAAQ,CAACd,UAAU;AACxC;AAGO,eAAejB,wBACpBE,WAAmB,EACnB,EACEC,OAAO,EACPC,QAAQ,EACRU,WAAW,EACXN,WAAW,EACXG,KAAK,EACLI,UAAU,EAQX;IAED,sDAAsD;IACtD,IAAIiB,WAAW,MAAMR,0BAA0BtB;IAE/C8B,WAAWA,SAASC,OAAO,CAAC,mBAAmBzB;IAC/CwB,WAAWA,SAASC,OAAO,CAAC,eAAetB;IAE3CqB,WAAWE,IAAAA,yBAAmB,EAACF,UAAU7B;IAEzC,IAAIC,UAAU;QACZ4B,WAAWG,IAAAA,sBAAgB,EACzBH,UACA5B,SACGgC,GAAG,CAAC,CAACC,OAAS;gBACb,iFAAiF;gBACjF;oBACEC,IAAI;oBACJC,KAAK;oBACLF;gBACF;gBACA;oBACEE,KAAK;oBACLF;gBACF;aACD,EACAG,IAAI;IAEX;IAEA,IAAIzB,YAAY;QACdiB,WAAWS,QAAQT,UAAU,CAAC,4BAA4B,EAAEjB,WAAW,CAAC,CAAC;IAC3E;IAEA,IAAID,aAAa;QACfkB,WAAWS,QAAQT,UAAU,CAAC,4BAA4B,EAAElB,YAAY,CAAC,CAAC;IAC5E;IAEA,OAAOkB;AACT;AAEA,oDAAoD,GACpD,SAASS,QAAQT,QAAgB,EAAEU,IAAY;IAC7C,OAAOV,SAASC,OAAO,CAAC,WAAW,CAAC,MAAM,EAAES,KAAK,UAAU,CAAC;AAC9D"}
@@ -118,8 +118,14 @@ const DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME = 'development-code-signing-se
118
118
  function getDevelopmentCodeSigningDirectory() {
119
119
  return _path().default.join((0, _UserSettings.getExpoHomeDirectory)(), 'codesigning');
120
120
  }
121
+ function assertBasenameValue(input) {
122
+ if (!input || input === '.' || input === '..' || input !== _path().default.basename(input)) {
123
+ throw new _errors.CommandError('Invalid EAS project ID for development code signing cache');
124
+ }
125
+ }
121
126
  function getProjectDevelopmentCodeSigningInfoFile(defaults) {
122
127
  function getFile(easProjectId) {
128
+ assertBasenameValue(easProjectId);
123
129
  const filePath = _path().default.join(getDevelopmentCodeSigningDirectory(), easProjectId, DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME);
124
130
  return new (_jsonfile()).default(filePath);
125
131
  }
@@ -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 type { ExpoConfig } from '@expo/config';\nimport type { JSONObject } from '@expo/json-file';\nimport JsonFile from '@expo/json-file';\nimport { promises as fs } from 'fs';\nimport type { pki as PKI } from 'node-forge';\nimport path from 'path';\nimport type { Dictionary } from 'structured-headers';\nimport { 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 type { 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?.length) {\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","length","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":";;;;;;;;;;;QA6GaA;eAAAA;;QAcSC;eAAAA;;QA9DNC;eAAAA;;QAiYAC;eAAAA;;;;yBArbT;;;;;;;gEAGc;;;;;;;yBACU;;;;;;;gEAEd;;;;;;;yBAEe;;;;;;qBAEZ;wBACS;kDACyB;kDACA;wBACM;0BACzB;8BACE;yBACL;6DAEX;sBACK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE1B,MAAMC,QAAQC,QAAQ,SAAS;AA4B/B,MAAMC,8CAA8C;AAE7C,SAASJ;IACd,OAAOK,eAAI,CAACC,IAAI,CAACC,IAAAA,kCAAoB,KAAI;AAC3C;AAEA,SAASC,yCAA+DC,QAAW;IACjF,SAASC,QAAQC,YAAoB;QACnC,MAAMC,WAAWP,eAAI,CAACC,IAAI,CACxBN,sCACAW,cACAP;QAEF,OAAO,IAAIS,CAAAA,WAAO,SAAC,CAAID;IACzB;IAEA,eAAeE,UAAUH,YAAoB;QAC3C,IAAII;QACJ,IAAI;YACFA,kBAAkB,MAAML,QAAQC,cAAcG,SAAS;QACzD,EAAE,OAAM;YACNC,kBAAkB,MAAML,QAAQC,cAAcK,UAAU,CAACP,UAAU;gBAAEQ,WAAW;YAAK;QACvF;QACA,sCAAsC;QACtC,OAAO;YAAE,GAAGR,QAAQ;YAAE,GAAGM,eAAe;QAAC;IAC3C;IAEA,eAAeG,SAASP,YAAoB,EAAEQ,IAAgB;QAC5D,IAAI;YACF,OAAO,MAAMT,QAAQC,cAAcS,UAAU,CAACD,MAAM;gBAClDE,qBAAqBZ;YACvB;QACF,EAAE,OAAM;YACN,OAAO,MAAMC,QAAQC,cAAcK,UAAU,CAC3C;gBACE,GAAGP,QAAQ;gBACX,GAAGU,IAAI;YACT,GACA;gBAAEF,WAAW;YAAK;QAEtB;IACF;IAEA,OAAO;QACLP;QACAI;QACAI;IACF;AACF;AAEO,MAAMpB,iCACXU,yCAAmF;IACjFG,cAAc;IACdW,UAAU;IACVC,YAAY;IACZC,kBAAkB;AACpB;AAQK,eAAezB,wBACpB0B,GAAe,EACfC,qBAAoC,EACpCC,cAAkC;IAElC,IAAI,CAACD,uBAAuB;QAC1B,OAAO;IACT;IAEA,IAAIE;IACJ,IAAI;QACFA,wBAAwBC,IAAAA,oCAAe,EAACH;IAC1C,EAAE,OAAM;QACN,MAAM,IAAII,oBAAY,CAAC;IACzB;IAEA,MAAMC,qBAAqBH,sBAAsBI,GAAG,CAAC;IACrD,IAAI,CAACD,oBAAoB;QACvB,MAAM,IAAID,oBAAY,CAAC;IACzB;IAEA,MAAMG,gBAAgBF,kBAAkB,CAAC,EAAE;IAC3C,IAAI,OAAOE,kBAAkB,UAAU;QACrC,MAAM,IAAIH,oBAAY,CACpB,CAAC,yDAAyD,EAAEG,eAAe;IAE/E;IAEA,IAAIC,cAA6B;IACjC,MAAMC,mBAAmBP,sBAAsBI,GAAG,CAAC;IACnD,IAAIG,kBAAkB;QACpB,MAAMC,kBAAkBD,gBAAgB,CAAC,EAAE;QAC3C,IAAI,OAAOC,oBAAoB,UAAU;YACvC,MAAM,IAAIN,oBAAY,CAAC;QACzB;QACAI,cAAcE;IAChB;IAEA,IAAIH,kBAAkB,aAAa;QACjC,OAAO,MAAMI,2CAA2CZ;IAC1D,OAAO,IAAIQ,kBAAkB,WAAW;QACtC,MAAM,IAAIH,oBAAY,CACpB;IAEJ,OAAO;QACL,OAAO,MAAMQ,sCACXb,KACAE,gBACAM,eACAC;IAEJ;AACF;AAEA;;;CAGC,GACD,eAAeG,2CACbZ,GAAe;QAEMA,gBAAAA;IAArB,MAAMd,gBAAec,aAAAA,IAAIc,KAAK,sBAATd,iBAAAA,WAAWe,GAAG,qBAAdf,eAAgBgB,SAAS;IAC9C,oHAAoH;IACpH,sEAAsE;IACtE,IAAI,CAAC9B,cAAc;QACjBT,MACE,CAAC,oKAAoK,EAAEwC,IAAAA,eAAS,EAC9K,+BACC;QAEL,OAAO;IACT;IAEA,MAAMC,qCACJ,MAAM7C,+BAA+BgB,SAAS,CAACH;IACjD,MAAMiC,2BAA2BC,4DAC/BF,oCACAhC;IAGF,6GAA6G;IAC7G,iFAAiF;IACjF,IAAI,CAACmC,QAAG,CAACC,YAAY,EAAE;QACrB,IAAI;YACF,MAAMC,qBACJ,MAAMC,gDAAgDtC;YAExD,IAAIqC,oBAAoB;gBACtB,OAAOA;YACP,iEAAiE;YACnE,OAAO,IAAIJ,0BAA0B;gBACnCM,KAAIC,IAAI,CACN;gBAEF,OAAOP;YACT,OAAO;gBACL,OAAO;YACT;QACF,EAAE,OAAOQ,GAAQ;YACf,IAAIR,0BAA0B;gBAC5BM,KAAIC,IAAI,CACN;gBAEF,OAAOP;YACT,OAAO;gBACL,6CAA6C;gBAC7C,MAAMQ;YACR;QACF;IACF;IAEA,+JAA+J;IAC/J,IAAIR,0BAA0B;QAC5B,OAAOA;IACT;IAEA,6BAA6B;IAC7BM,KAAIC,IAAI,CAAC;IACT,OAAO;AACT;AAEA;;CAEC,GACD,eAAeb,sCACbb,GAAe,EACfE,cAAkC,EAClCM,aAAqB,EACrBC,WAA0B;QAEST,cAWPA;IAX5B,MAAM4B,8BAA6B5B,eAAAA,IAAI6B,OAAO,qBAAX7B,aAAa8B,sBAAsB;IACtE,IAAI,CAACF,4BAA4B;QAC/B,OAAO;IACT;IAEA,IAAI,CAAC1B,gBAAgB;QACnB,MAAM,IAAIG,oBAAY,CACpB;IAEJ;IAEA,MAAM0B,uBAAsB/B,gBAAAA,IAAI6B,OAAO,qBAAX7B,cAAa+B,mBAAmB;IAC5D,IAAI,CAACA,qBAAqB;QACxB,MAAM,IAAI1B,oBAAY,CACpB;IAEJ;IAEA,MAAM,EAAE2B,GAAG,EAAEC,KAAK,EAAE,GAAGF;IACvB,IAAI,CAACC,OAAO,CAACC,OAAO;QAClB,MAAM,IAAI5B,oBAAY,CACpB;IAEJ;IAEA,IAAIG,kBAAkByB,OAAO;QAC3B,MAAM,IAAI5B,oBAAY,CAAC,CAAC,uBAAuB,EAAEG,cAAc,UAAU,EAAEyB,OAAO;IACpF;IAEA,IAAIxB,eAAeA,gBAAgBuB,KAAK;QACtC,MAAM,IAAI3B,oBAAY,CAAC,CAAC,6BAA6B,EAAEI,YAAY,UAAU,EAAEuB,IAAI,CAAC,CAAC;IACvF;IAEA,MAAM,EAAEE,aAAa,EAAEC,cAAc,EAAE,GACrC,MAAMC,qDAAqD;QACzDR;QACA1B;IACF;IAEF,OAAO;QACLmC,OAAOJ;QACPnC,YAAYoC;QACZI,0BAA0BH;QAC1BI,6BAA6B,EAAE;QAC/B1C,UAAU;IACZ;AACF;AAEA,eAAe2C,uBAAuB5D,IAAY,EAAE6D,YAAoB;IACtE,IAAI;QACF,OAAO,MAAMC,cAAE,CAACC,QAAQ,CAAC/D,MAAM;IACjC,EAAE,OAAM;QACN,MAAM,IAAIyB,oBAAY,CAACoC;IACzB;AACF;AAEA,eAAeL,qDAAqD,EAClER,0BAA0B,EAC1B1B,cAAc,EAIf;IACC,MAAM,CAAC0C,2BAA2BV,cAAc,GAAG,MAAMW,QAAQC,GAAG,CAAC;QACnEN,uBACEZ,4BACA,CAAC,mDAAmD,EAAEA,4BAA4B;QAEpFY,uBACEtC,gBACA,CAAC,mDAAmD,EAAEA,gBAAgB;KAEzE;IAED,MAAMJ,aAAaiD,IAAAA,2DAAgC,EAACb;IACpD,MAAMc,cAAcC,IAAAA,6DAAkC,EAACL;IACvDM,IAAAA,wDAA6B,EAACF,aAAa;QACzCG,WAAWH,YAAYG,SAAS;QAChCrD;IACF;IAEA,OAAO;QAAEoC;QAAeC,gBAAgBS;IAA0B;AACpE;AAEA;;;CAGC,GACD,SAASxB,4DACPgC,eAAyD,EACzDlE,YAAoB;IAEpB,IAAIkE,gBAAgBlE,YAAY,KAAKA,cAAc;QACjD,OAAO;IACT;IAEA,MAAM,EACJY,YAAYoC,aAAa,EACzBnC,kBAAkBsD,eAAe,EACjCxD,QAAQ,EACT,GAAGuD;IACJ,IAAI,CAAClB,iBAAiB,EAACmB,mCAAAA,gBAAiBC,MAAM,GAAE;QAC9C,OAAO;IACT;IAEA,MAAMvD,mBAAmBsD,gBAAgBE,GAAG,CAAC,CAACpB,iBAC5Cc,IAAAA,6DAAkC,EAACd;IAGrC,iEAAiE;IAEjE,iDAAiD;IACjD,KAAK,MAAMa,eAAejD,iBAAkB;QAC1C,MAAMyD,MAAM,IAAIC;QAChB,IAAIT,YAAYU,QAAQ,CAACC,SAAS,GAAGH,OAAOR,YAAYU,QAAQ,CAACE,QAAQ,GAAGJ,KAAK;YAC/E,OAAO;QACT;IACF;IAEA,wHAAwH;IAExH,OAAO;QACLnB,OAAO;QACPE,6BAA6Bc;QAC7Bf,0BAA0Be,eAAe,CAAC,EAAE;QAC5CvD,YAAYoC;QACZrC;IACF;AACF;AAEA,SAASgE,yCAAyCC,KAAY,EAAEC,GAAQ;QAOnBD,6CAAAA,iCAAAA,4BAAAA;IANnD,MAAME,kBAAkBD,IAAIE,YAAY,CAACC,EAAE;IAE3C,MAAMC,qCACJL,MAAMM,UAAU,KAAK,UAAUN,MAAMM,UAAU,KAAK,YAChDN,MAAMO,cAAc,CAACH,EAAE,KAAKF,kBAC5B;IACN,MAAMM,2CAA2C,CAAC,GAACR,uBAAAA,MAAMS,QAAQ,CAC9DC,IAAI,CAAC,CAACC,UAAYA,QAAQP,EAAE,KAAKF,sCADeF,6BAAAA,qBAE/CY,KAAK,sBAF0CZ,kCAAAA,2BAExCU,IAAI,CAAC,CAACG,iBAAmBA,eAAeb,KAAK,CAACI,EAAE,KAAKJ,MAAMI,EAAE,uBAFrBJ,8CAAAA,gCAG/Cc,WAAW,qBAHoCd,4CAGlCe,QAAQ,CAAC;IAC1B,OAAOV,sCAAsCG;AAC/C;AAEA,eAAe9C,gDACbtC,YAAoB;IAEpB,MAAM4E,QAAQ,MAAMgB,IAAAA,wBAAe;IAEnC,IAAI,CAAChB,OAAO;QACV,OAAO;IACT;IAEA,IAAIC;IACJ,IAAI;QACFA,MAAM,MAAMgB,kBAAQ,CAACC,SAAS,CAAC9F;IACjC,EAAE,OAAOyC,GAAG;QACV,IAAIA,aAAasD,6BAAqB,IAAItD,aAAauD,4BAAoB,EAAE;YAC3E,OAAO;QACT;QACA,MAAMvD;IACR;IACA,IAAI,CAACkC,yCAAyCC,OAAOC,MAAM;QACzD,OAAO;IACT;IAEA,MAAMoB,UAAUC,IAAAA,0CAAe;IAC/B,MAAMC,aAAaC,IAAAA,8CAAmB,EAACH;IACvC,MAAMI,MAAMC,IAAAA,sCAAW,EAACL,SAAS,CAAC,4BAA4B,EAAEjG,cAAc;IAC9E,MAAMuG,SAASC,IAAAA,6CAAkB,EAACH;IAClC,MAAM,CAACI,+BAA+BC,8BAA8B,GAAG,MAAM/C,QAAQC,GAAG,CAAC;QACvF+C,IAAAA,uEAAqC,EAAC3G,cAAcuG;QACpDK,IAAAA,uEAAqC,EAAC5G;KACvC;IAED,MAAMb,+BAA+BoB,QAAQ,CAACP,cAAc;QAC1DA;QACAW,UAAUkE,IAAIlE,QAAQ;QACtBC,YAAYuF,WAAWnD,aAAa;QACpCnC,kBAAkB;YAAC4F;YAA+BC;SAA8B;IAClF;IAEA,OAAO;QACLvD,OAAO;QACPE,6BAA6B;YAACoD;YAA+BC;SAA8B;QAC3FtD,0BAA0BqD;QAC1B7F,YAAYuF,WAAWnD,aAAa;QACpCrC,UAAUkE,IAAIlE,QAAQ;IACxB;AACF;AAIO,SAASrB,mBACduH,mBAA2B,EAC3B3C,eAAgC;IAEhC,MAAMtD,aAAaiD,IAAAA,2DAAgC,EAACK,gBAAgBtD,UAAU;IAC9E,MAAMkD,cAAcC,IAAAA,6DAAkC,EAACG,gBAAgBd,wBAAwB;IAC/F,OAAO0D,IAAAA,uDAA4B,EACjClG,YACAkD,aACAiD,OAAOC,IAAI,CAACH,qBAAqB;AAErC"}
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 type { ExpoConfig } from '@expo/config';\nimport type { JSONObject } from '@expo/json-file';\nimport JsonFile from '@expo/json-file';\nimport { promises as fs } from 'fs';\nimport type { pki as PKI } from 'node-forge';\nimport path from 'path';\nimport type { Dictionary } from 'structured-headers';\nimport { 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 type { 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?.length) {\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","length","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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"}