@expo/cli 56.1.13 → 56.1.15

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.
@@ -2,12 +2,27 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- Object.defineProperty(exports, "AsyncWsTunnel", {
6
- enumerable: true,
7
- get: function() {
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 AsyncWsTunnel () {
8
13
  return AsyncWsTunnel;
14
+ },
15
+ get getExpoAccountTunnelUrlAsync () {
16
+ return getExpoAccountTunnelUrlAsync;
9
17
  }
10
18
  });
19
+ function _config() {
20
+ const data = require("@expo/config");
21
+ _config = function() {
22
+ return data;
23
+ };
24
+ return data;
25
+ }
11
26
  function _wstunnel() {
12
27
  const data = /*#__PURE__*/ _interop_require_wildcard(require("@expo/ws-tunnel"));
13
28
  _wstunnel = function() {
@@ -43,6 +58,10 @@ function _nodepath() {
43
58
  };
44
59
  return data;
45
60
  }
61
+ const _TunnelMutation = require("../../api/graphql/mutations/TunnelMutation");
62
+ const _AppQuery = require("../../api/graphql/queries/AppQuery");
63
+ const _UserSettings = require("../../api/user/UserSettings");
64
+ const _user = require("../../api/user/user");
46
65
  const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../log"));
47
66
  const _env = require("../../utils/env");
48
67
  const _errors = require("../../utils/errors");
@@ -94,25 +113,44 @@ function _interop_require_wildcard(obj, nodeInterop) {
94
113
  }
95
114
  const debug = require('debug')('expo:start:server:ws-tunnel');
96
115
  class AsyncWsTunnel {
97
- constructor(_projectRoot, port){
98
- /** Info about the currently running instance of tunnel. */ this.serverUrl = null;
99
- if (port !== 8081) {
100
- throw new _errors.CommandError('WS_TUNNEL_PORT', `WS-tunnel only supports tunneling over port 8081, attempted to use port ${port}`);
101
- }
116
+ constructor(projectRoot, port, options = {}){
117
+ this.projectRoot = projectRoot;
118
+ this.port = port;
119
+ this.options = options;
120
+ this.serverUrl = null;
102
121
  }
103
122
  getActiveUrl() {
104
- return this.serverUrl;
123
+ var _this_serverUrl;
124
+ return ((_this_serverUrl = this.serverUrl) == null ? void 0 : _this_serverUrl.href) ?? null;
105
125
  }
106
126
  async startAsync() {
107
127
  this.serverUrl = await _wstunnel().startAsync({
108
- ...getTunnelOptions(),
128
+ ...await this.getTunnelOptionsAsync(),
109
129
  onStatusChange (status) {
110
130
  if (status === 'disconnected') {
111
131
  _log.error(_chalk().default.red('Tunnel connection has been closed. This is often related to intermittent connection problems with the ws proxy servers. Restart the dev server to try connecting again.') + _chalk().default.gray('\nCheck the Expo status page for outages: https://status.expo.dev/'));
112
132
  }
113
133
  }
114
134
  });
115
- debug('Tunnel URL:', this.serverUrl);
135
+ debug('Tunnel URL:', this.serverUrl.href);
136
+ }
137
+ async getTunnelOptionsAsync() {
138
+ if (this.options.useExpoAccount) {
139
+ return this.getExpoAccountTunnelOptionsAsync();
140
+ } else {
141
+ return getLegacyTunnelOptions(this.port);
142
+ }
143
+ }
144
+ async getExpoAccountTunnelOptionsAsync() {
145
+ const apiUrl = await getExpoAccountTunnelUrlAsync(this.projectRoot);
146
+ if (!apiUrl) {
147
+ const cause = (0, _UserSettings.hasCredentials)() ? `Your Expo account may not have access to it, or the tunnel service is unavailable.` : `You're not logged in to your Expo account — run 'npx expo login'.`;
148
+ throw new _errors.CommandError('WS_TUNNEL_SIGNED_URL', `Couldn't create a signed tunnel URL for this project. ${cause} ` + `Unset EXPO_UNSTABLE_TUNNEL_V2 to use an ngrok tunnel instead.`);
149
+ }
150
+ return {
151
+ apiUrl,
152
+ targetUrl: `http://localhost:${this.port}`
153
+ };
116
154
  }
117
155
  async stopAsync() {
118
156
  debug('Stopping Tunnel');
@@ -120,36 +158,83 @@ class AsyncWsTunnel {
120
158
  this.serverUrl = null;
121
159
  }
122
160
  }
123
- // Generate a base-36 string of 5 characters (from 32 bits of randomness)
124
- function randomStr() {
125
- return (Math.random().toString(36) + '00000').slice(2, 7);
126
- }
127
- function getTunnelSession() {
128
- let session = randomStr() + randomStr() + randomStr();
129
- if ((0, _env.envIsWebcontainer)()) {
130
- const leaseId = Buffer.from((0, _nodeos().hostname)()).toString('base64url');
131
- const leaseFile = _nodepath().join((0, _nodeos().tmpdir)(), `_ws_tunnel_lease_${leaseId}`);
132
- try {
133
- session = _nodefs().readFileSync(leaseFile, 'utf8').trim() || session;
134
- } catch {}
135
- try {
136
- _nodefs().writeFileSync(leaseFile, session, 'utf8');
137
- } catch {}
138
- }
139
- return session;
140
- }
141
- function getTunnelOptions() {
161
+ function getLegacyTunnelOptions(port) {
162
+ if (port !== 8081) {
163
+ throw new _errors.CommandError('WS_TUNNEL_PORT', `WS-tunnel only supports tunneling over port 8081, attempted to use port ${port}`);
164
+ }
165
+ function randomSessionId() {
166
+ const randomPart = ()=>(Math.random().toString(36) + '00000').slice(2, 7);
167
+ return randomPart() + randomPart() + randomPart();
168
+ }
169
+ function getWebcontainerSession() {
170
+ let session = randomSessionId();
171
+ if ((0, _env.envIsWebcontainer)()) {
172
+ const leaseId = Buffer.from((0, _nodeos().hostname)()).toString('base64url');
173
+ const leaseFile = _nodepath().join((0, _nodeos().tmpdir)(), `_ws_tunnel_lease_${leaseId}`);
174
+ try {
175
+ session = _nodefs().readFileSync(leaseFile, 'utf8').trim() || session;
176
+ } catch {}
177
+ try {
178
+ _nodefs().writeFileSync(leaseFile, session, 'utf8');
179
+ } catch {}
180
+ }
181
+ return session;
182
+ }
142
183
  const userDefinedSubdomain = _env.env.EXPO_TUNNEL_SUBDOMAIN;
143
184
  if (userDefinedSubdomain && typeof userDefinedSubdomain === 'string') {
144
185
  debug('Session:', userDefinedSubdomain);
145
186
  return {
146
187
  session: userDefinedSubdomain
147
188
  };
148
- } else {
149
- const session = getTunnelSession();
150
- return {
151
- session
152
- };
189
+ }
190
+ return {
191
+ session: getWebcontainerSession()
192
+ };
193
+ }
194
+ async function getExpoAccountTunnelUrlAsync(projectRoot) {
195
+ async function resolveExpoAccountIdAsync(projectRoot) {
196
+ var _exp_extra_eas, _exp_extra, _actor_accounts_;
197
+ const { exp } = (0, _config().getConfig)(projectRoot, {
198
+ skipSDKVersionRequirement: true
199
+ });
200
+ const easProjectId = (_exp_extra = exp.extra) == null ? void 0 : (_exp_extra_eas = _exp_extra.eas) == null ? void 0 : _exp_extra_eas.projectId;
201
+ if (easProjectId) {
202
+ try {
203
+ var _app_ownerAccount;
204
+ const app = await _AppQuery.AppQuery.byIdAsync(easProjectId);
205
+ const appOwnerAccountId = app == null ? void 0 : (_app_ownerAccount = app.ownerAccount) == null ? void 0 : _app_ownerAccount.id;
206
+ if (appOwnerAccountId) {
207
+ return appOwnerAccountId;
208
+ }
209
+ } catch (error) {
210
+ debug('Failed to resolve owning account from EAS project ID:', error);
211
+ }
212
+ }
213
+ const actor = await (0, _user.getUserAsync)();
214
+ if (!actor) {
215
+ return null;
216
+ } else if (actor.__typename === 'User' || actor.__typename === 'SSOUser') {
217
+ var _actor_primaryAccount;
218
+ const actorPrimaryAccountId = (_actor_primaryAccount = actor.primaryAccount) == null ? void 0 : _actor_primaryAccount.id;
219
+ if (actorPrimaryAccountId) {
220
+ return actorPrimaryAccountId;
221
+ }
222
+ }
223
+ // Robots have no primary account; fall back to their first available account.
224
+ return ((_actor_accounts_ = actor.accounts[0]) == null ? void 0 : _actor_accounts_.id) ?? null;
225
+ }
226
+ try {
227
+ const accountId = await resolveExpoAccountIdAsync(projectRoot);
228
+ if (!accountId) {
229
+ debug('No Expo account available to create a signed tunnel URL; user is likely logged out.');
230
+ return null;
231
+ }
232
+ const { url } = await _TunnelMutation.TunnelMutation.createSignedTunnelUrlAsync(accountId);
233
+ debug('Created signed tunnel URL for account:', accountId);
234
+ return url;
235
+ } catch (error) {
236
+ debug('Failed to create a signed tunnel URL:', error);
237
+ return null;
153
238
  }
154
239
  }
155
240
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/start/server/AsyncWsTunnel.ts"],"sourcesContent":["import * as tunnel from '@expo/ws-tunnel';\nimport chalk from 'chalk';\nimport * as fs from 'node:fs';\nimport { tmpdir, hostname } from 'node:os';\nimport * as path from 'node:path';\n\nimport * as Log from '../../log';\nimport { env, envIsWebcontainer } from '../../utils/env';\nimport { CommandError } from '../../utils/errors';\n\nconst debug = require('debug')('expo:start:server:ws-tunnel') as typeof console.log;\n\nexport class AsyncWsTunnel {\n /** Info about the currently running instance of tunnel. */\n private serverUrl: string | null = null;\n\n constructor(_projectRoot: string, port: number) {\n if (port !== 8081) {\n throw new CommandError(\n 'WS_TUNNEL_PORT',\n `WS-tunnel only supports tunneling over port 8081, attempted to use port ${port}`\n );\n }\n }\n\n public getActiveUrl(): string | null {\n return this.serverUrl;\n }\n\n async startAsync(): Promise<void> {\n this.serverUrl = await tunnel.startAsync({\n ...getTunnelOptions(),\n onStatusChange(status) {\n if (status === 'disconnected') {\n Log.error(\n chalk.red(\n 'Tunnel connection has been closed. This is often related to intermittent connection problems with the ws proxy servers. Restart the dev server to try connecting again.'\n ) + chalk.gray('\\nCheck the Expo status page for outages: https://status.expo.dev/')\n );\n }\n },\n });\n\n debug('Tunnel URL:', this.serverUrl);\n }\n\n async stopAsync(): Promise<void> {\n debug('Stopping Tunnel');\n await tunnel.stopAsync();\n this.serverUrl = null;\n }\n}\n\n// Generate a base-36 string of 5 characters (from 32 bits of randomness)\nfunction randomStr() {\n return (Math.random().toString(36) + '00000').slice(2, 7);\n}\n\nfunction getTunnelSession(): string {\n let session = randomStr() + randomStr() + randomStr();\n if (envIsWebcontainer()) {\n const leaseId = Buffer.from(hostname()).toString('base64url');\n const leaseFile = path.join(tmpdir(), `_ws_tunnel_lease_${leaseId}`);\n try {\n session = fs.readFileSync(leaseFile, 'utf8').trim() || session;\n } catch {}\n try {\n fs.writeFileSync(leaseFile, session, 'utf8');\n } catch {}\n }\n return session;\n}\n\nfunction getTunnelOptions() {\n const userDefinedSubdomain = env.EXPO_TUNNEL_SUBDOMAIN;\n if (userDefinedSubdomain && typeof userDefinedSubdomain === 'string') {\n debug('Session:', userDefinedSubdomain);\n return { session: userDefinedSubdomain };\n } else {\n const session = getTunnelSession();\n return { session };\n }\n}\n"],"names":["AsyncWsTunnel","debug","require","_projectRoot","port","serverUrl","CommandError","getActiveUrl","startAsync","tunnel","getTunnelOptions","onStatusChange","status","Log","error","chalk","red","gray","stopAsync","randomStr","Math","random","toString","slice","getTunnelSession","session","envIsWebcontainer","leaseId","Buffer","from","hostname","leaseFile","path","join","tmpdir","fs","readFileSync","trim","writeFileSync","userDefinedSubdomain","env","EXPO_TUNNEL_SUBDOMAIN"],"mappings":";;;;+BAYaA;;;eAAAA;;;;iEAZW;;;;;;;gEACN;;;;;;;iEACE;;;;;;;yBACa;;;;;;;iEACX;;;;;;6DAED;qBACkB;wBACV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE7B,MAAMC,QAAQC,QAAQ,SAAS;AAExB,MAAMF;IAIX,YAAYG,YAAoB,EAAEC,IAAY,CAAE;QAHhD,yDAAyD,QACjDC,YAA2B;QAGjC,IAAID,SAAS,MAAM;YACjB,MAAM,IAAIE,oBAAY,CACpB,kBACA,CAAC,wEAAwE,EAAEF,MAAM;QAErF;IACF;IAEOG,eAA8B;QACnC,OAAO,IAAI,CAACF,SAAS;IACvB;IAEA,MAAMG,aAA4B;QAChC,IAAI,CAACH,SAAS,GAAG,MAAMI,YAAOD,UAAU,CAAC;YACvC,GAAGE,kBAAkB;YACrBC,gBAAeC,MAAM;gBACnB,IAAIA,WAAW,gBAAgB;oBAC7BC,KAAIC,KAAK,CACPC,gBAAK,CAACC,GAAG,CACP,6KACED,gBAAK,CAACE,IAAI,CAAC;gBAEnB;YACF;QACF;QAEAhB,MAAM,eAAe,IAAI,CAACI,SAAS;IACrC;IAEA,MAAMa,YAA2B;QAC/BjB,MAAM;QACN,MAAMQ,YAAOS,SAAS;QACtB,IAAI,CAACb,SAAS,GAAG;IACnB;AACF;AAEA,yEAAyE;AACzE,SAASc;IACP,OAAO,AAACC,CAAAA,KAAKC,MAAM,GAAGC,QAAQ,CAAC,MAAM,OAAM,EAAGC,KAAK,CAAC,GAAG;AACzD;AAEA,SAASC;IACP,IAAIC,UAAUN,cAAcA,cAAcA;IAC1C,IAAIO,IAAAA,sBAAiB,KAAI;QACvB,MAAMC,UAAUC,OAAOC,IAAI,CAACC,IAAAA,kBAAQ,KAAIR,QAAQ,CAAC;QACjD,MAAMS,YAAYC,YAAKC,IAAI,CAACC,IAAAA,gBAAM,KAAI,CAAC,iBAAiB,EAAEP,SAAS;QACnE,IAAI;YACFF,UAAUU,UAAGC,YAAY,CAACL,WAAW,QAAQM,IAAI,MAAMZ;QACzD,EAAE,OAAM,CAAC;QACT,IAAI;YACFU,UAAGG,aAAa,CAACP,WAAWN,SAAS;QACvC,EAAE,OAAM,CAAC;IACX;IACA,OAAOA;AACT;AAEA,SAASf;IACP,MAAM6B,uBAAuBC,QAAG,CAACC,qBAAqB;IACtD,IAAIF,wBAAwB,OAAOA,yBAAyB,UAAU;QACpEtC,MAAM,YAAYsC;QAClB,OAAO;YAAEd,SAASc;QAAqB;IACzC,OAAO;QACL,MAAMd,UAAUD;QAChB,OAAO;YAAEC;QAAQ;IACnB;AACF"}
1
+ {"version":3,"sources":["../../../../src/start/server/AsyncWsTunnel.ts"],"sourcesContent":["import { getConfig } from '@expo/config';\nimport * as tunnel from '@expo/ws-tunnel';\nimport chalk from 'chalk';\nimport * as fs from 'node:fs';\nimport { tmpdir, hostname } from 'node:os';\nimport * as path from 'node:path';\n\nimport { TunnelMutation } from '../../api/graphql/mutations/TunnelMutation';\nimport { AppQuery } from '../../api/graphql/queries/AppQuery';\nimport { hasCredentials } from '../../api/user/UserSettings';\nimport { getUserAsync } from '../../api/user/user';\nimport * as Log from '../../log';\nimport { env, envIsWebcontainer } from '../../utils/env';\nimport { CommandError } from '../../utils/errors';\n\nconst debug = require('debug')('expo:start:server:ws-tunnel') as typeof console.log;\n\nexport interface AsyncWsTunnelOptions {\n useExpoAccount?: boolean;\n}\n\nexport class AsyncWsTunnel {\n private serverUrl: URL | null = null;\n\n constructor(\n private projectRoot: string,\n private port: number,\n private options: AsyncWsTunnelOptions = {}\n ) {}\n\n public getActiveUrl(): string | null {\n return this.serverUrl?.href ?? null;\n }\n\n async startAsync(): Promise<void> {\n this.serverUrl = await tunnel.startAsync({\n ...(await this.getTunnelOptionsAsync()),\n onStatusChange(status) {\n if (status === 'disconnected') {\n Log.error(\n chalk.red(\n 'Tunnel connection has been closed. This is often related to intermittent connection problems with the ws proxy servers. Restart the dev server to try connecting again.'\n ) + chalk.gray('\\nCheck the Expo status page for outages: https://status.expo.dev/')\n );\n }\n },\n });\n\n debug('Tunnel URL:', this.serverUrl.href);\n }\n\n private async getTunnelOptionsAsync(): Promise<tunnel.WsTunnelOptions> {\n if (this.options.useExpoAccount) {\n return this.getExpoAccountTunnelOptionsAsync();\n } else {\n return getLegacyTunnelOptions(this.port);\n }\n }\n\n private async getExpoAccountTunnelOptionsAsync(): Promise<tunnel.WsTunnelOptions> {\n const apiUrl = await getExpoAccountTunnelUrlAsync(this.projectRoot);\n if (!apiUrl) {\n const cause = hasCredentials()\n ? `Your Expo account may not have access to it, or the tunnel service is unavailable.`\n : `You're not logged in to your Expo account — run 'npx expo login'.`;\n throw new CommandError(\n 'WS_TUNNEL_SIGNED_URL',\n `Couldn't create a signed tunnel URL for this project. ${cause} ` +\n `Unset EXPO_UNSTABLE_TUNNEL_V2 to use an ngrok tunnel instead.`\n );\n }\n return { apiUrl, targetUrl: `http://localhost:${this.port}` };\n }\n\n async stopAsync(): Promise<void> {\n debug('Stopping Tunnel');\n await tunnel.stopAsync();\n this.serverUrl = null;\n }\n}\n\nfunction getLegacyTunnelOptions(port: number): tunnel.WsTunnelOptions {\n if (port !== 8081) {\n throw new CommandError(\n 'WS_TUNNEL_PORT',\n `WS-tunnel only supports tunneling over port 8081, attempted to use port ${port}`\n );\n }\n\n function randomSessionId(): string {\n const randomPart = () => (Math.random().toString(36) + '00000').slice(2, 7);\n return randomPart() + randomPart() + randomPart();\n }\n\n function getWebcontainerSession(): string {\n let session = randomSessionId();\n if (envIsWebcontainer()) {\n const leaseId = Buffer.from(hostname()).toString('base64url');\n const leaseFile = path.join(tmpdir(), `_ws_tunnel_lease_${leaseId}`);\n try {\n session = fs.readFileSync(leaseFile, 'utf8').trim() || session;\n } catch {}\n try {\n fs.writeFileSync(leaseFile, session, 'utf8');\n } catch {}\n }\n return session;\n }\n\n const userDefinedSubdomain = env.EXPO_TUNNEL_SUBDOMAIN;\n if (userDefinedSubdomain && typeof userDefinedSubdomain === 'string') {\n debug('Session:', userDefinedSubdomain);\n return { session: userDefinedSubdomain };\n }\n return { session: getWebcontainerSession() };\n}\n\nexport async function getExpoAccountTunnelUrlAsync(projectRoot: string): Promise<string | null> {\n async function resolveExpoAccountIdAsync(projectRoot: string): Promise<string | null> {\n const { exp } = getConfig(projectRoot, { skipSDKVersionRequirement: true });\n const easProjectId = exp.extra?.eas?.projectId as string | undefined | null;\n if (easProjectId) {\n try {\n const app = await AppQuery.byIdAsync(easProjectId);\n const appOwnerAccountId = app?.ownerAccount?.id;\n if (appOwnerAccountId) {\n return appOwnerAccountId;\n }\n } catch (error) {\n debug('Failed to resolve owning account from EAS project ID:', error);\n }\n }\n\n const actor = await getUserAsync();\n if (!actor) {\n return null;\n } else if (actor.__typename === 'User' || actor.__typename === 'SSOUser') {\n const actorPrimaryAccountId = actor.primaryAccount?.id;\n if (actorPrimaryAccountId) {\n return actorPrimaryAccountId;\n }\n }\n\n // Robots have no primary account; fall back to their first available account.\n return actor.accounts[0]?.id ?? null;\n }\n\n try {\n const accountId = await resolveExpoAccountIdAsync(projectRoot);\n if (!accountId) {\n debug('No Expo account available to create a signed tunnel URL; user is likely logged out.');\n return null;\n }\n const { url } = await TunnelMutation.createSignedTunnelUrlAsync(accountId);\n debug('Created signed tunnel URL for account:', accountId);\n return url;\n } catch (error) {\n debug('Failed to create a signed tunnel URL:', error);\n return null;\n }\n}\n"],"names":["AsyncWsTunnel","getExpoAccountTunnelUrlAsync","debug","require","projectRoot","port","options","serverUrl","getActiveUrl","href","startAsync","tunnel","getTunnelOptionsAsync","onStatusChange","status","Log","error","chalk","red","gray","useExpoAccount","getExpoAccountTunnelOptionsAsync","getLegacyTunnelOptions","apiUrl","cause","hasCredentials","CommandError","targetUrl","stopAsync","randomSessionId","randomPart","Math","random","toString","slice","getWebcontainerSession","session","envIsWebcontainer","leaseId","Buffer","from","hostname","leaseFile","path","join","tmpdir","fs","readFileSync","trim","writeFileSync","userDefinedSubdomain","env","EXPO_TUNNEL_SUBDOMAIN","resolveExpoAccountIdAsync","exp","actor","getConfig","skipSDKVersionRequirement","easProjectId","extra","eas","projectId","app","AppQuery","byIdAsync","appOwnerAccountId","ownerAccount","id","getUserAsync","__typename","actorPrimaryAccountId","primaryAccount","accounts","accountId","url","TunnelMutation","createSignedTunnelUrlAsync"],"mappings":"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"}
@@ -189,15 +189,25 @@ class BundlerDevServer {
189
189
  this.notifier = new _FileNotifier.FileNotifier(this.projectRoot, this.getConfigModuleIds());
190
190
  this.notifier.startObserving();
191
191
  }
192
- /** Create ngrok instance and start the tunnel server. Exposed for testing. */ async _startTunnelAsync() {
192
+ /** Create the tunnel instance and start the tunnel server. Exposed for testing. */ async _startTunnelAsync() {
193
193
  var _this_getInstance;
194
194
  const port = (_this_getInstance = this.getInstance()) == null ? void 0 : _this_getInstance.location.port;
195
195
  if (!port) return null;
196
196
  debug('[tunnel] connect to port: ' + port);
197
- this.tunnel = (0, _env.envIsWebcontainer)() ? new _AsyncWsTunnel.AsyncWsTunnel(this.projectRoot, port) : new _AsyncNgrok.AsyncNgrok(this.projectRoot, port);
197
+ this.tunnel = this._createTunnel(port);
198
198
  await this.tunnel.startAsync();
199
199
  return this.tunnel;
200
200
  }
201
+ /** Resolve which tunnel implementation to use, without starting it. */ _createTunnel(port) {
202
+ const useV2Tunnel = _env.env.EXPO_UNSTABLE_TUNNEL_V2 || (0, _env.envIsWebcontainer)();
203
+ if (useV2Tunnel) {
204
+ const useExpoAccount = !!_env.env.EXPO_UNSTABLE_TUNNEL_V2;
205
+ return new _AsyncWsTunnel.AsyncWsTunnel(this.projectRoot, port, {
206
+ useExpoAccount
207
+ });
208
+ }
209
+ return new _AsyncNgrok.AsyncNgrok(this.projectRoot, port);
210
+ }
201
211
  async startDevSessionAsync() {
202
212
  // This is used to make Expo Go open the project in either Expo Go, or the web browser.
203
213
  // Must come after ngrok (`startTunnelAsync`) setup.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/start/server/BundlerDevServer.ts"],"sourcesContent":["import assert from 'assert';\nimport resolveFrom from 'resolve-from';\n\nimport { AsyncNgrok } from './AsyncNgrok';\nimport { AsyncWsTunnel } from './AsyncWsTunnel';\nimport { Bonjour } from './Bonjour';\nimport DevToolsPluginManager from './DevToolsPluginManager';\nimport { DevelopmentSession } from './DevelopmentSession';\nimport type { CreateURLOptions } from './UrlCreator';\nimport { UrlCreator } from './UrlCreator';\nimport type { PlatformBundlers } from './platformBundlers';\nimport * as Log from '../../log';\nimport { FileNotifier } from '../../utils/FileNotifier';\nimport { resolveWithTimeout } from '../../utils/delay';\nimport { env, envIsWebcontainer } from '../../utils/env';\nimport { CommandError } from '../../utils/errors';\nimport { isInteractive } from '../../utils/interactive';\nimport { openBrowserAsync } from '../../utils/open';\nimport type { BaseResolveDeviceProps, PlatformManager } from '../platforms/PlatformManager';\n\nconst debug = require('debug')('expo:start:server:devServer') as typeof console.log;\n\nexport type MessageSocket = {\n broadcast: (method: string, params?: Record<string, any> | undefined) => void;\n};\n\nexport type ServerLike = {\n close(callback?: (err?: Error) => void): void;\n addListener?(event: string, listener: (...args: any[]) => void): unknown;\n};\n\nexport type DevServerInstance = {\n /** Bundler dev server instance. */\n server: ServerLike;\n /** Dev server URL location properties. */\n location: {\n url: string;\n port: number;\n protocol: 'http' | 'https';\n host?: string;\n };\n /** Additional middleware that's attached to the `server`. */\n middleware: any;\n /** Message socket for communicating with the runtime. */\n messageSocket: MessageSocket;\n};\n\nexport interface BundlerStartOptions {\n /** Should the dev server use `https` protocol. */\n https?: boolean;\n /** Should start the dev servers in development mode (minify). */\n mode?: 'development' | 'production';\n /** Is dev client enabled. */\n devClient?: boolean;\n /** Should run dev servers with clean caches. */\n resetDevServer?: boolean;\n /** Code signing private key path (defaults to same directory as certificate) */\n privateKeyPath?: string;\n\n /** Max amount of workers (threads) to use with Metro bundler, defaults to undefined for max workers. */\n maxWorkers?: number;\n /** Port to start the dev server on. */\n port?: number;\n\n /** Should start a headless dev server e.g. mock representation to approximate info from a server running in a different process. */\n headless?: boolean;\n /** Should instruct the bundler to create minified bundles. */\n minify?: boolean;\n\n /** Will the bundler be used for exporting. NOTE: This is an odd option to pass to the dev server. */\n isExporting?: boolean;\n\n // Webpack options\n /** Should modify and create PWA icons. */\n isImageEditingEnabled?: boolean;\n\n location: CreateURLOptions;\n}\n\nconst PLATFORM_MANAGERS = {\n simulator: () =>\n require('../platforms/ios/ApplePlatformManager')\n .ApplePlatformManager as typeof import('../platforms/ios/ApplePlatformManager').ApplePlatformManager,\n emulator: () =>\n require('../platforms/android/AndroidPlatformManager')\n .AndroidPlatformManager as typeof import('../platforms/android/AndroidPlatformManager').AndroidPlatformManager,\n};\n\ntype PlatformManagers = {\n [K in keyof typeof PLATFORM_MANAGERS]: InstanceType<ReturnType<(typeof PLATFORM_MANAGERS)[K]>>;\n};\n\ntype PlatformDevice<Platform extends keyof PlatformManagers> =\n PlatformManagers[Platform] extends PlatformManager<infer Device, any> ? Device : never;\n\ntype PlatformLaunchProps<Platform extends keyof PlatformManagers> =\n PlatformManagers[Platform] extends PlatformManager<any, infer LaunchProps> ? LaunchProps : never;\n\nexport abstract class BundlerDevServer {\n /** Name of the bundler. */\n abstract get name(): string;\n\n /** Tunnel instance for managing tunnel connections. */\n protected tunnel: AsyncNgrok | AsyncWsTunnel | null = null;\n /** Interfaces with the Expo 'Development Session' API. */\n protected devSession: DevelopmentSession | null = null;\n /** Announces dev server via Bonjour */\n protected bonjour: Bonjour | null = null;\n /** Http server and related info. */\n protected instance: DevServerInstance | null = null;\n /** Native platform interfaces for opening projects. */\n private platformManagers: { [K in keyof PlatformManagers]?: PlatformManagers[K] | undefined } =\n {};\n /** Manages the creation of dev server URLs. */\n protected urlCreator?: UrlCreator | null = null;\n\n private notifier: FileNotifier | null = null;\n protected readonly devToolsPluginManager: DevToolsPluginManager;\n public isDevClient: boolean;\n\n constructor(\n /** Project root folder. */\n public projectRoot: string,\n /** A mapping of bundlers to platforms. */\n public platformBundlers: PlatformBundlers,\n /** Advanced options */\n options?: {\n /**\n * The instance of DevToolsPluginManager\n * @default new DevToolsPluginManager(projectRoot)\n */\n devToolsPluginManager?: DevToolsPluginManager;\n // TODO: Replace with custom scheme maybe...\n isDevClient?: boolean;\n }\n ) {\n this.devToolsPluginManager =\n options?.devToolsPluginManager ?? new DevToolsPluginManager(projectRoot);\n this.isDevClient = options?.isDevClient ?? false;\n }\n\n protected setInstance(instance: DevServerInstance) {\n this.instance = instance;\n }\n\n /** Get the manifest middleware function. */\n protected async getManifestMiddlewareAsync(\n options: Pick<BundlerStartOptions, 'minify' | 'mode' | 'privateKeyPath'> = {}\n ) {\n const Middleware = require('./middleware/ExpoGoManifestHandlerMiddleware')\n .ExpoGoManifestHandlerMiddleware as typeof import('./middleware/ExpoGoManifestHandlerMiddleware').ExpoGoManifestHandlerMiddleware;\n\n const urlCreator = this.getUrlCreator();\n const middleware = new Middleware(this.projectRoot, {\n constructUrl: urlCreator.constructUrl.bind(urlCreator),\n mode: options.mode,\n minify: options.minify,\n isNativeWebpack: this.name === 'webpack' && this.isTargetingNative(),\n privateKeyPath: options.privateKeyPath,\n });\n return middleware;\n }\n\n /** Start the dev server using settings defined in the start command. */\n public async startAsync(options: BundlerStartOptions): Promise<DevServerInstance> {\n await this.stopAsync();\n\n let instance: DevServerInstance;\n if (options.headless) {\n instance = await this.startHeadlessAsync(options);\n } else {\n instance = await this.startImplementationAsync(options);\n }\n\n this.setInstance(instance);\n await this.postStartAsync(options);\n return instance;\n }\n\n protected abstract startImplementationAsync(\n options: BundlerStartOptions\n ): Promise<DevServerInstance>;\n\n public async waitForTypeScriptAsync(): Promise<boolean> {\n return false;\n }\n\n public abstract startTypeScriptServices(): Promise<void>;\n\n public async watchEnvironmentVariables(): Promise<void> {\n // noop -- We've only implemented this functionality in Metro.\n }\n\n /**\n * Creates a mock server representation that can be used to estimate URLs for a server started in another process.\n * This is used for the run commands where you can reuse the server from a previous run.\n */\n private async startHeadlessAsync(options: BundlerStartOptions): Promise<DevServerInstance> {\n if (!options.port)\n throw new CommandError('HEADLESS_SERVER', 'headless dev server requires a port option');\n await this.initUrlCreator(options);\n return {\n // Create a mock server\n server: {\n close: (callback: () => void) => {\n this.instance = null;\n callback?.();\n },\n addListener() {},\n },\n location: {\n // The port is the main thing we want to send back.\n port: options.port,\n // localhost isn't always correct.\n host: 'localhost',\n // http is the only supported protocol on native.\n url: `http://localhost:${options.port}`,\n protocol: 'http',\n },\n middleware: {},\n messageSocket: {\n broadcast: () => {\n throw new CommandError('HEADLESS_SERVER', 'Cannot broadcast messages to headless server');\n },\n },\n };\n }\n\n /**\n * Runs after the `startAsync` function, performing any additional common operations.\n * You can assume the dev server is started by the time this function is called.\n */\n protected async postStartAsync(options: BundlerStartOptions) {\n if (\n options.location.hostType === 'tunnel' &&\n !env.EXPO_OFFLINE &&\n // This is a hack to prevent using tunnel on web since we block it upstream for some reason.\n this.isTargetingNative()\n ) {\n await this._startTunnelAsync();\n } else if (envIsWebcontainer()) {\n await this._startTunnelAsync();\n }\n\n if (!options.isExporting) {\n await Promise.all([this.startDevSessionAsync(), this.startBonjourAsync()]);\n this.watchConfig();\n }\n }\n\n protected abstract getConfigModuleIds(): string[];\n\n protected watchConfig() {\n this.notifier?.stopObserving();\n this.notifier = new FileNotifier(this.projectRoot, this.getConfigModuleIds());\n this.notifier.startObserving();\n }\n\n /** Create ngrok instance and start the tunnel server. Exposed for testing. */\n public async _startTunnelAsync(): Promise<AsyncNgrok | AsyncWsTunnel | null> {\n const port = this.getInstance()?.location.port;\n if (!port) return null;\n debug('[tunnel] connect to port: ' + port);\n this.tunnel = envIsWebcontainer()\n ? new AsyncWsTunnel(this.projectRoot, port)\n : new AsyncNgrok(this.projectRoot, port);\n await this.tunnel.startAsync();\n return this.tunnel;\n }\n\n protected async startDevSessionAsync() {\n // This is used to make Expo Go open the project in either Expo Go, or the web browser.\n // Must come after ngrok (`startTunnelAsync`) setup.\n this.devSession = new DevelopmentSession(\n this.projectRoot,\n // This URL will be used on external devices so the computer IP won't be relevant.\n this.isTargetingNative()\n ? this.getNativeRuntimeUrl()\n : this.getDevServerUrl({ hostType: 'localhost' })\n );\n\n await this.devSession.startAsync({\n runtime: this.isTargetingNative() ? 'native' : 'web',\n });\n }\n\n protected async startBonjourAsync() {\n // This is used to make Expo Go open the project in either Expo Go, or the web browser.\n // Must come after ngrok (`startTunnelAsync`) setup.\n if (!this.bonjour) {\n this.bonjour = new Bonjour(this.projectRoot, this.getInstance()?.location.port);\n }\n\n await this.bonjour.announceAsync({});\n }\n\n public isTargetingNative() {\n // Temporary hack while we implement multi-bundler dev server proxy.\n return true;\n }\n\n public isTargetingWeb() {\n return this.platformBundlers.web === this.name;\n }\n\n /**\n * Sends a message over web sockets to any connected device,\n * does nothing when the dev server is not running.\n *\n * @param method name of the command. In RN projects `reload`, and `devMenu` are available. In Expo Go, `sendDevCommand` is available.\n * @param params\n */\n public broadcastMessage(\n method: 'reload' | 'devMenu' | 'sendDevCommand',\n params?: Record<string, any>\n ) {\n this.getInstance()?.messageSocket.broadcast(method, params);\n }\n\n /** Get the running dev server instance. */\n public getInstance() {\n return this.instance;\n }\n\n /** Stop the running dev server instance. */\n async stopAsync() {\n // Reset url creator\n this.urlCreator = undefined;\n\n // Stop file watching.\n this.notifier?.stopObserving();\n\n await Promise.all([\n // Stop the bonjour advertiser\n this.bonjour?.closeAsync(),\n // Stop the dev session timer and tell Expo API to remove dev session.\n this.devSession?.closeAsync(),\n ]);\n\n // Stop tunnel if running.\n await this.tunnel?.stopAsync().catch((e) => {\n Log.error(`Error stopping tunnel:`);\n Log.exception(e);\n });\n\n return resolveWithTimeout(\n () =>\n new Promise<void>((resolve, reject) => {\n // Close the server.\n debug(`Stopping dev server (bundler: ${this.name})`);\n\n if (this.instance?.server) {\n // Check if server is even running.\n this.instance.server.close((error) => {\n debug(`Stopped dev server (bundler: ${this.name})`);\n this.instance = null;\n if (error) {\n if ('code' in error && error.code === 'ERR_SERVER_NOT_RUNNING') {\n resolve();\n } else {\n reject(error);\n }\n } else {\n resolve();\n }\n });\n } else {\n debug(`Stopped dev server (bundler: ${this.name})`);\n this.instance = null;\n resolve();\n }\n }),\n {\n // NOTE(Bacon): Metro dev server doesn't seem to be closing in time.\n timeout: 1000,\n errorMessage: `Timeout waiting for '${this.name}' dev server to close`,\n }\n );\n }\n\n // TODO(@kitten): This should be created top-down rather than bottom up from implementors\n protected async initUrlCreator(\n options: Partial<Pick<BundlerStartOptions, 'port' | 'location'>> = {}\n ) {\n assert(options?.port, 'Dev server instance not found');\n assert(!this.urlCreator, 'Dev server is already initialized');\n const urlCreator = await UrlCreator.init(options.location, {\n port: options.port,\n getTunnelUrl: this.getTunnelUrl.bind(this),\n });\n this.urlCreator = urlCreator;\n return urlCreator;\n }\n\n public getUrlCreator() {\n assert(this.urlCreator, 'Dev server is uninitialized');\n return this.urlCreator;\n }\n\n public getNativeRuntimeUrl(opts: Partial<CreateURLOptions> = {}) {\n return this.isDevClient\n ? (this.getUrlCreator().constructDevClientUrl(opts) ?? this.getDevServerUrl())\n : this.getUrlCreator().constructUrl({ ...opts, scheme: 'exp' });\n }\n\n /** Get the URL for the running instance of the dev server. */\n public getDevServerUrl(options: { hostType?: 'localhost' } = {}): string | null {\n const instance = this.getInstance();\n if (!instance?.location) {\n return null;\n }\n\n // If we have an active WS tunnel instance, we always need to return the tunnel location.\n if (this.tunnel && this.tunnel instanceof AsyncWsTunnel) {\n return this.getUrlCreator().constructUrl();\n }\n\n const { location } = instance;\n if (options.hostType === 'localhost') {\n return `${location.protocol}://localhost:${location.port}`;\n }\n\n return location.url ?? null;\n }\n\n public getDevServerUrlOrAssert(options: { hostType?: 'localhost' } = {}): string {\n const instance = this.getDevServerUrl(options);\n if (!instance) {\n throw new CommandError(\n 'DEV_SERVER',\n `Cannot get the dev server URL before the server has started - bundler[${this.name}]`\n );\n }\n\n return instance;\n }\n\n /** Get the base URL for JS inspector */\n public getJsInspectorBaseUrl(): string {\n if (this.name !== 'metro') {\n throw new CommandError(\n 'DEV_SERVER',\n `Cannot get the JS inspector base url - bundler[${this.name}]`\n );\n }\n return this.getUrlCreator().constructUrl({ scheme: 'http' });\n }\n\n /** Get the tunnel URL from the tunnel. */\n public getTunnelUrl(): string | null {\n return this.tunnel?.getActiveUrl() ?? null;\n }\n\n /** Open the dev server in a runtime. */\n public async openPlatformAsync(\n launchTarget: keyof PlatformManagers | 'desktop',\n resolver: BaseResolveDeviceProps<any> = {}\n ) {\n if (launchTarget === 'desktop') {\n const serverUrl = this.getDevServerUrl({ hostType: 'localhost' });\n // Allow opening the tunnel URL when using Metro web.\n const url = this.name === 'metro' ? (this.getTunnelUrl() ?? serverUrl) : serverUrl;\n // Only launch the browser automatically if the process is interactive, otherwise we'll assume it's an agent.\n if (isInteractive()) {\n await openBrowserAsync(url!);\n }\n return { url };\n }\n\n const runtime = this.isTargetingNative() ? (this.isDevClient ? 'custom' : 'expo') : 'web';\n const manager = await this.getPlatformManagerAsync(launchTarget);\n return manager.openAsync({ runtime }, resolver);\n }\n\n /** Open the dev server in a runtime. */\n public async openCustomRuntimeAsync<Platform extends keyof PlatformManagers>(\n launchTarget: Platform,\n launchProps: Partial<PlatformLaunchProps<Platform>> = {},\n resolver: BaseResolveDeviceProps<PlatformDevice<Platform>> = {}\n ) {\n const runtime = this.isTargetingNative() ? (this.isDevClient ? 'custom' : 'expo') : 'web';\n if (runtime !== 'custom') {\n throw new CommandError(\n `dev server cannot open custom runtimes either because it does not target native platforms or because it is not targeting dev clients. (target: ${runtime})`\n );\n }\n\n const manager = await this.getPlatformManagerAsync(launchTarget);\n return manager.openAsync(\n { runtime: 'custom', props: launchProps },\n resolver as BaseResolveDeviceProps<any>\n );\n }\n\n /** Get the URL for opening in Expo Go. */\n protected getExpoGoUrl(): string {\n return this.getUrlCreator().constructUrl({ scheme: 'exp' });\n }\n\n /** Should use the interstitial page for selecting which runtime to use. */\n protected isRedirectPageEnabled(): boolean {\n return (\n !env.EXPO_NO_REDIRECT_PAGE &&\n // if user passed --dev-client flag, skip interstitial page\n !this.isDevClient &&\n // Checks if dev client is installed.\n !!resolveFrom.silent(this.projectRoot, 'expo-dev-client')\n );\n }\n\n /** Get the redirect URL when redirecting is enabled. */\n public getRedirectUrl(platform: keyof PlatformManagers | null = null): string | null {\n if (!this.isRedirectPageEnabled()) {\n debug('Redirect page is disabled');\n return null;\n }\n\n return (\n this.getUrlCreator().constructLoadingUrl(\n {},\n platform === 'emulator' ? 'android' : platform === 'simulator' ? 'ios' : null\n ) ?? null\n );\n }\n\n protected async getPlatformManagerAsync<Platform extends keyof PlatformManagers>(\n ofPlatform: Platform\n ): Promise<PlatformManagers[Platform]> {\n const platform: keyof PlatformManagers = ofPlatform;\n if (!this.platformManagers[platform]) {\n const port = this.getInstance()?.location.port;\n if (!port || !this.urlCreator) {\n throw new CommandError(\n 'DEV_SERVER',\n 'Cannot interact with native platforms until dev server has started'\n );\n }\n debug(`Creating platform manager (platform: ${platform}, port: ${port})`);\n const managerParams = {\n getCustomRuntimeUrl: this.urlCreator.constructDevClientUrl.bind(this.urlCreator),\n getExpoGoUrl: this.getExpoGoUrl.bind(this),\n getRedirectUrl: this.getRedirectUrl.bind(this, platform),\n getDevServerUrl: this.getDevServerUrl.bind(this, { hostType: 'localhost' }),\n };\n switch (platform) {\n case 'simulator': {\n const Manager = PLATFORM_MANAGERS[platform]();\n this.platformManagers[platform] = new Manager(this.projectRoot, port, managerParams);\n break;\n }\n case 'emulator': {\n const Manager = PLATFORM_MANAGERS[platform]();\n this.platformManagers[platform] = new Manager(this.projectRoot, port, managerParams);\n break;\n }\n }\n }\n return this.platformManagers[platform] as PlatformManagers[Platform];\n }\n}\n"],"names":["BundlerDevServer","debug","require","PLATFORM_MANAGERS","simulator","ApplePlatformManager","emulator","AndroidPlatformManager","projectRoot","platformBundlers","options","tunnel","devSession","bonjour","instance","platformManagers","urlCreator","notifier","devToolsPluginManager","DevToolsPluginManager","isDevClient","setInstance","getManifestMiddlewareAsync","Middleware","ExpoGoManifestHandlerMiddleware","getUrlCreator","middleware","constructUrl","bind","mode","minify","isNativeWebpack","name","isTargetingNative","privateKeyPath","startAsync","stopAsync","headless","startHeadlessAsync","startImplementationAsync","postStartAsync","waitForTypeScriptAsync","watchEnvironmentVariables","port","CommandError","initUrlCreator","server","close","callback","addListener","location","host","url","protocol","messageSocket","broadcast","hostType","env","EXPO_OFFLINE","_startTunnelAsync","envIsWebcontainer","isExporting","Promise","all","startDevSessionAsync","startBonjourAsync","watchConfig","stopObserving","FileNotifier","getConfigModuleIds","startObserving","getInstance","AsyncWsTunnel","AsyncNgrok","DevelopmentSession","getNativeRuntimeUrl","getDevServerUrl","runtime","Bonjour","announceAsync","isTargetingWeb","web","broadcastMessage","method","params","undefined","closeAsync","catch","e","Log","error","exception","resolveWithTimeout","resolve","reject","code","timeout","errorMessage","assert","UrlCreator","init","getTunnelUrl","opts","constructDevClientUrl","scheme","getDevServerUrlOrAssert","getJsInspectorBaseUrl","getActiveUrl","openPlatformAsync","launchTarget","resolver","serverUrl","isInteractive","openBrowserAsync","manager","getPlatformManagerAsync","openAsync","openCustomRuntimeAsync","launchProps","props","getExpoGoUrl","isRedirectPageEnabled","EXPO_NO_REDIRECT_PAGE","resolveFrom","silent","getRedirectUrl","platform","constructLoadingUrl","ofPlatform","managerParams","getCustomRuntimeUrl","Manager"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/start/server/BundlerDevServer.ts"],"sourcesContent":["import assert from 'assert';\nimport resolveFrom from 'resolve-from';\n\nimport { AsyncNgrok } from './AsyncNgrok';\nimport { AsyncWsTunnel } from './AsyncWsTunnel';\nimport { Bonjour } from './Bonjour';\nimport DevToolsPluginManager from './DevToolsPluginManager';\nimport { DevelopmentSession } from './DevelopmentSession';\nimport type { CreateURLOptions } from './UrlCreator';\nimport { UrlCreator } from './UrlCreator';\nimport type { PlatformBundlers } from './platformBundlers';\nimport * as Log from '../../log';\nimport { FileNotifier } from '../../utils/FileNotifier';\nimport { resolveWithTimeout } from '../../utils/delay';\nimport { env, envIsWebcontainer } from '../../utils/env';\nimport { CommandError } from '../../utils/errors';\nimport { isInteractive } from '../../utils/interactive';\nimport { openBrowserAsync } from '../../utils/open';\nimport type { BaseResolveDeviceProps, PlatformManager } from '../platforms/PlatformManager';\n\nconst debug = require('debug')('expo:start:server:devServer') as typeof console.log;\n\nexport type MessageSocket = {\n broadcast: (method: string, params?: Record<string, any> | undefined) => void;\n};\n\nexport type ServerLike = {\n close(callback?: (err?: Error) => void): void;\n addListener?(event: string, listener: (...args: any[]) => void): unknown;\n};\n\nexport type DevServerInstance = {\n /** Bundler dev server instance. */\n server: ServerLike;\n /** Dev server URL location properties. */\n location: {\n url: string;\n port: number;\n protocol: 'http' | 'https';\n host?: string;\n };\n /** Additional middleware that's attached to the `server`. */\n middleware: any;\n /** Message socket for communicating with the runtime. */\n messageSocket: MessageSocket;\n};\n\nexport interface BundlerStartOptions {\n /** Should the dev server use `https` protocol. */\n https?: boolean;\n /** Should start the dev servers in development mode (minify). */\n mode?: 'development' | 'production';\n /** Is dev client enabled. */\n devClient?: boolean;\n /** Should run dev servers with clean caches. */\n resetDevServer?: boolean;\n /** Code signing private key path (defaults to same directory as certificate) */\n privateKeyPath?: string;\n\n /** Max amount of workers (threads) to use with Metro bundler, defaults to undefined for max workers. */\n maxWorkers?: number;\n /** Port to start the dev server on. */\n port?: number;\n\n /** Should start a headless dev server e.g. mock representation to approximate info from a server running in a different process. */\n headless?: boolean;\n /** Should instruct the bundler to create minified bundles. */\n minify?: boolean;\n\n /** Will the bundler be used for exporting. NOTE: This is an odd option to pass to the dev server. */\n isExporting?: boolean;\n\n // Webpack options\n /** Should modify and create PWA icons. */\n isImageEditingEnabled?: boolean;\n\n location: CreateURLOptions;\n}\n\nconst PLATFORM_MANAGERS = {\n simulator: () =>\n require('../platforms/ios/ApplePlatformManager')\n .ApplePlatformManager as typeof import('../platforms/ios/ApplePlatformManager').ApplePlatformManager,\n emulator: () =>\n require('../platforms/android/AndroidPlatformManager')\n .AndroidPlatformManager as typeof import('../platforms/android/AndroidPlatformManager').AndroidPlatformManager,\n};\n\ntype PlatformManagers = {\n [K in keyof typeof PLATFORM_MANAGERS]: InstanceType<ReturnType<(typeof PLATFORM_MANAGERS)[K]>>;\n};\n\ntype PlatformDevice<Platform extends keyof PlatformManagers> =\n PlatformManagers[Platform] extends PlatformManager<infer Device, any> ? Device : never;\n\ntype PlatformLaunchProps<Platform extends keyof PlatformManagers> =\n PlatformManagers[Platform] extends PlatformManager<any, infer LaunchProps> ? LaunchProps : never;\n\nexport abstract class BundlerDevServer {\n /** Name of the bundler. */\n abstract get name(): string;\n\n /** Tunnel instance for managing tunnel connections. */\n protected tunnel: AsyncNgrok | AsyncWsTunnel | null = null;\n /** Interfaces with the Expo 'Development Session' API. */\n protected devSession: DevelopmentSession | null = null;\n /** Announces dev server via Bonjour */\n protected bonjour: Bonjour | null = null;\n /** Http server and related info. */\n protected instance: DevServerInstance | null = null;\n /** Native platform interfaces for opening projects. */\n private platformManagers: { [K in keyof PlatformManagers]?: PlatformManagers[K] | undefined } =\n {};\n /** Manages the creation of dev server URLs. */\n protected urlCreator?: UrlCreator | null = null;\n\n private notifier: FileNotifier | null = null;\n protected readonly devToolsPluginManager: DevToolsPluginManager;\n public isDevClient: boolean;\n\n constructor(\n /** Project root folder. */\n public projectRoot: string,\n /** A mapping of bundlers to platforms. */\n public platformBundlers: PlatformBundlers,\n /** Advanced options */\n options?: {\n /**\n * The instance of DevToolsPluginManager\n * @default new DevToolsPluginManager(projectRoot)\n */\n devToolsPluginManager?: DevToolsPluginManager;\n // TODO: Replace with custom scheme maybe...\n isDevClient?: boolean;\n }\n ) {\n this.devToolsPluginManager =\n options?.devToolsPluginManager ?? new DevToolsPluginManager(projectRoot);\n this.isDevClient = options?.isDevClient ?? false;\n }\n\n protected setInstance(instance: DevServerInstance) {\n this.instance = instance;\n }\n\n /** Get the manifest middleware function. */\n protected async getManifestMiddlewareAsync(\n options: Pick<BundlerStartOptions, 'minify' | 'mode' | 'privateKeyPath'> = {}\n ) {\n const Middleware = require('./middleware/ExpoGoManifestHandlerMiddleware')\n .ExpoGoManifestHandlerMiddleware as typeof import('./middleware/ExpoGoManifestHandlerMiddleware').ExpoGoManifestHandlerMiddleware;\n\n const urlCreator = this.getUrlCreator();\n const middleware = new Middleware(this.projectRoot, {\n constructUrl: urlCreator.constructUrl.bind(urlCreator),\n mode: options.mode,\n minify: options.minify,\n isNativeWebpack: this.name === 'webpack' && this.isTargetingNative(),\n privateKeyPath: options.privateKeyPath,\n });\n return middleware;\n }\n\n /** Start the dev server using settings defined in the start command. */\n public async startAsync(options: BundlerStartOptions): Promise<DevServerInstance> {\n await this.stopAsync();\n\n let instance: DevServerInstance;\n if (options.headless) {\n instance = await this.startHeadlessAsync(options);\n } else {\n instance = await this.startImplementationAsync(options);\n }\n\n this.setInstance(instance);\n await this.postStartAsync(options);\n return instance;\n }\n\n protected abstract startImplementationAsync(\n options: BundlerStartOptions\n ): Promise<DevServerInstance>;\n\n public async waitForTypeScriptAsync(): Promise<boolean> {\n return false;\n }\n\n public abstract startTypeScriptServices(): Promise<void>;\n\n public async watchEnvironmentVariables(): Promise<void> {\n // noop -- We've only implemented this functionality in Metro.\n }\n\n /**\n * Creates a mock server representation that can be used to estimate URLs for a server started in another process.\n * This is used for the run commands where you can reuse the server from a previous run.\n */\n private async startHeadlessAsync(options: BundlerStartOptions): Promise<DevServerInstance> {\n if (!options.port)\n throw new CommandError('HEADLESS_SERVER', 'headless dev server requires a port option');\n await this.initUrlCreator(options);\n return {\n // Create a mock server\n server: {\n close: (callback: () => void) => {\n this.instance = null;\n callback?.();\n },\n addListener() {},\n },\n location: {\n // The port is the main thing we want to send back.\n port: options.port,\n // localhost isn't always correct.\n host: 'localhost',\n // http is the only supported protocol on native.\n url: `http://localhost:${options.port}`,\n protocol: 'http',\n },\n middleware: {},\n messageSocket: {\n broadcast: () => {\n throw new CommandError('HEADLESS_SERVER', 'Cannot broadcast messages to headless server');\n },\n },\n };\n }\n\n /**\n * Runs after the `startAsync` function, performing any additional common operations.\n * You can assume the dev server is started by the time this function is called.\n */\n protected async postStartAsync(options: BundlerStartOptions) {\n if (\n options.location.hostType === 'tunnel' &&\n !env.EXPO_OFFLINE &&\n // This is a hack to prevent using tunnel on web since we block it upstream for some reason.\n this.isTargetingNative()\n ) {\n await this._startTunnelAsync();\n } else if (envIsWebcontainer()) {\n await this._startTunnelAsync();\n }\n\n if (!options.isExporting) {\n await Promise.all([this.startDevSessionAsync(), this.startBonjourAsync()]);\n this.watchConfig();\n }\n }\n\n protected abstract getConfigModuleIds(): string[];\n\n protected watchConfig() {\n this.notifier?.stopObserving();\n this.notifier = new FileNotifier(this.projectRoot, this.getConfigModuleIds());\n this.notifier.startObserving();\n }\n\n /** Create the tunnel instance and start the tunnel server. Exposed for testing. */\n public async _startTunnelAsync(): Promise<AsyncNgrok | AsyncWsTunnel | null> {\n const port = this.getInstance()?.location.port;\n if (!port) return null;\n debug('[tunnel] connect to port: ' + port);\n this.tunnel = this._createTunnel(port);\n await this.tunnel.startAsync();\n return this.tunnel;\n }\n\n /** Resolve which tunnel implementation to use, without starting it. */\n private _createTunnel(port: number): AsyncNgrok | AsyncWsTunnel {\n const useV2Tunnel = env.EXPO_UNSTABLE_TUNNEL_V2 || envIsWebcontainer();\n if (useV2Tunnel) {\n const useExpoAccount = !!env.EXPO_UNSTABLE_TUNNEL_V2;\n return new AsyncWsTunnel(this.projectRoot, port, { useExpoAccount });\n }\n\n return new AsyncNgrok(this.projectRoot, port);\n }\n\n protected async startDevSessionAsync() {\n // This is used to make Expo Go open the project in either Expo Go, or the web browser.\n // Must come after ngrok (`startTunnelAsync`) setup.\n this.devSession = new DevelopmentSession(\n this.projectRoot,\n // This URL will be used on external devices so the computer IP won't be relevant.\n this.isTargetingNative()\n ? this.getNativeRuntimeUrl()\n : this.getDevServerUrl({ hostType: 'localhost' })\n );\n\n await this.devSession.startAsync({\n runtime: this.isTargetingNative() ? 'native' : 'web',\n });\n }\n\n protected async startBonjourAsync() {\n // This is used to make Expo Go open the project in either Expo Go, or the web browser.\n // Must come after ngrok (`startTunnelAsync`) setup.\n if (!this.bonjour) {\n this.bonjour = new Bonjour(this.projectRoot, this.getInstance()?.location.port);\n }\n\n await this.bonjour.announceAsync({});\n }\n\n public isTargetingNative() {\n // Temporary hack while we implement multi-bundler dev server proxy.\n return true;\n }\n\n public isTargetingWeb() {\n return this.platformBundlers.web === this.name;\n }\n\n /**\n * Sends a message over web sockets to any connected device,\n * does nothing when the dev server is not running.\n *\n * @param method name of the command. In RN projects `reload`, and `devMenu` are available. In Expo Go, `sendDevCommand` is available.\n * @param params\n */\n public broadcastMessage(\n method: 'reload' | 'devMenu' | 'sendDevCommand',\n params?: Record<string, any>\n ) {\n this.getInstance()?.messageSocket.broadcast(method, params);\n }\n\n /** Get the running dev server instance. */\n public getInstance() {\n return this.instance;\n }\n\n /** Stop the running dev server instance. */\n async stopAsync() {\n // Reset url creator\n this.urlCreator = undefined;\n\n // Stop file watching.\n this.notifier?.stopObserving();\n\n await Promise.all([\n // Stop the bonjour advertiser\n this.bonjour?.closeAsync(),\n // Stop the dev session timer and tell Expo API to remove dev session.\n this.devSession?.closeAsync(),\n ]);\n\n // Stop tunnel if running.\n await this.tunnel?.stopAsync().catch((e) => {\n Log.error(`Error stopping tunnel:`);\n Log.exception(e);\n });\n\n return resolveWithTimeout(\n () =>\n new Promise<void>((resolve, reject) => {\n // Close the server.\n debug(`Stopping dev server (bundler: ${this.name})`);\n\n if (this.instance?.server) {\n // Check if server is even running.\n this.instance.server.close((error) => {\n debug(`Stopped dev server (bundler: ${this.name})`);\n this.instance = null;\n if (error) {\n if ('code' in error && error.code === 'ERR_SERVER_NOT_RUNNING') {\n resolve();\n } else {\n reject(error);\n }\n } else {\n resolve();\n }\n });\n } else {\n debug(`Stopped dev server (bundler: ${this.name})`);\n this.instance = null;\n resolve();\n }\n }),\n {\n // NOTE(Bacon): Metro dev server doesn't seem to be closing in time.\n timeout: 1000,\n errorMessage: `Timeout waiting for '${this.name}' dev server to close`,\n }\n );\n }\n\n // TODO(@kitten): This should be created top-down rather than bottom up from implementors\n protected async initUrlCreator(\n options: Partial<Pick<BundlerStartOptions, 'port' | 'location'>> = {}\n ) {\n assert(options?.port, 'Dev server instance not found');\n assert(!this.urlCreator, 'Dev server is already initialized');\n const urlCreator = await UrlCreator.init(options.location, {\n port: options.port,\n getTunnelUrl: this.getTunnelUrl.bind(this),\n });\n this.urlCreator = urlCreator;\n return urlCreator;\n }\n\n public getUrlCreator() {\n assert(this.urlCreator, 'Dev server is uninitialized');\n return this.urlCreator;\n }\n\n public getNativeRuntimeUrl(opts: Partial<CreateURLOptions> = {}) {\n return this.isDevClient\n ? (this.getUrlCreator().constructDevClientUrl(opts) ?? this.getDevServerUrl())\n : this.getUrlCreator().constructUrl({ ...opts, scheme: 'exp' });\n }\n\n /** Get the URL for the running instance of the dev server. */\n public getDevServerUrl(options: { hostType?: 'localhost' } = {}): string | null {\n const instance = this.getInstance();\n if (!instance?.location) {\n return null;\n }\n\n // If we have an active WS tunnel instance, we always need to return the tunnel location.\n if (this.tunnel && this.tunnel instanceof AsyncWsTunnel) {\n return this.getUrlCreator().constructUrl();\n }\n\n const { location } = instance;\n if (options.hostType === 'localhost') {\n return `${location.protocol}://localhost:${location.port}`;\n }\n\n return location.url ?? null;\n }\n\n public getDevServerUrlOrAssert(options: { hostType?: 'localhost' } = {}): string {\n const instance = this.getDevServerUrl(options);\n if (!instance) {\n throw new CommandError(\n 'DEV_SERVER',\n `Cannot get the dev server URL before the server has started - bundler[${this.name}]`\n );\n }\n\n return instance;\n }\n\n /** Get the base URL for JS inspector */\n public getJsInspectorBaseUrl(): string {\n if (this.name !== 'metro') {\n throw new CommandError(\n 'DEV_SERVER',\n `Cannot get the JS inspector base url - bundler[${this.name}]`\n );\n }\n return this.getUrlCreator().constructUrl({ scheme: 'http' });\n }\n\n /** Get the tunnel URL from the tunnel. */\n public getTunnelUrl(): string | null {\n return this.tunnel?.getActiveUrl() ?? null;\n }\n\n /** Open the dev server in a runtime. */\n public async openPlatformAsync(\n launchTarget: keyof PlatformManagers | 'desktop',\n resolver: BaseResolveDeviceProps<any> = {}\n ) {\n if (launchTarget === 'desktop') {\n const serverUrl = this.getDevServerUrl({ hostType: 'localhost' });\n // Allow opening the tunnel URL when using Metro web.\n const url = this.name === 'metro' ? (this.getTunnelUrl() ?? serverUrl) : serverUrl;\n // Only launch the browser automatically if the process is interactive, otherwise we'll assume it's an agent.\n if (isInteractive()) {\n await openBrowserAsync(url!);\n }\n return { url };\n }\n\n const runtime = this.isTargetingNative() ? (this.isDevClient ? 'custom' : 'expo') : 'web';\n const manager = await this.getPlatformManagerAsync(launchTarget);\n return manager.openAsync({ runtime }, resolver);\n }\n\n /** Open the dev server in a runtime. */\n public async openCustomRuntimeAsync<Platform extends keyof PlatformManagers>(\n launchTarget: Platform,\n launchProps: Partial<PlatformLaunchProps<Platform>> = {},\n resolver: BaseResolveDeviceProps<PlatformDevice<Platform>> = {}\n ) {\n const runtime = this.isTargetingNative() ? (this.isDevClient ? 'custom' : 'expo') : 'web';\n if (runtime !== 'custom') {\n throw new CommandError(\n `dev server cannot open custom runtimes either because it does not target native platforms or because it is not targeting dev clients. (target: ${runtime})`\n );\n }\n\n const manager = await this.getPlatformManagerAsync(launchTarget);\n return manager.openAsync(\n { runtime: 'custom', props: launchProps },\n resolver as BaseResolveDeviceProps<any>\n );\n }\n\n /** Get the URL for opening in Expo Go. */\n protected getExpoGoUrl(): string {\n return this.getUrlCreator().constructUrl({ scheme: 'exp' });\n }\n\n /** Should use the interstitial page for selecting which runtime to use. */\n protected isRedirectPageEnabled(): boolean {\n return (\n !env.EXPO_NO_REDIRECT_PAGE &&\n // if user passed --dev-client flag, skip interstitial page\n !this.isDevClient &&\n // Checks if dev client is installed.\n !!resolveFrom.silent(this.projectRoot, 'expo-dev-client')\n );\n }\n\n /** Get the redirect URL when redirecting is enabled. */\n public getRedirectUrl(platform: keyof PlatformManagers | null = null): string | null {\n if (!this.isRedirectPageEnabled()) {\n debug('Redirect page is disabled');\n return null;\n }\n\n return (\n this.getUrlCreator().constructLoadingUrl(\n {},\n platform === 'emulator' ? 'android' : platform === 'simulator' ? 'ios' : null\n ) ?? null\n );\n }\n\n protected async getPlatformManagerAsync<Platform extends keyof PlatformManagers>(\n ofPlatform: Platform\n ): Promise<PlatformManagers[Platform]> {\n const platform: keyof PlatformManagers = ofPlatform;\n if (!this.platformManagers[platform]) {\n const port = this.getInstance()?.location.port;\n if (!port || !this.urlCreator) {\n throw new CommandError(\n 'DEV_SERVER',\n 'Cannot interact with native platforms until dev server has started'\n );\n }\n debug(`Creating platform manager (platform: ${platform}, port: ${port})`);\n const managerParams = {\n getCustomRuntimeUrl: this.urlCreator.constructDevClientUrl.bind(this.urlCreator),\n getExpoGoUrl: this.getExpoGoUrl.bind(this),\n getRedirectUrl: this.getRedirectUrl.bind(this, platform),\n getDevServerUrl: this.getDevServerUrl.bind(this, { hostType: 'localhost' }),\n };\n switch (platform) {\n case 'simulator': {\n const Manager = PLATFORM_MANAGERS[platform]();\n this.platformManagers[platform] = new Manager(this.projectRoot, port, managerParams);\n break;\n }\n case 'emulator': {\n const Manager = PLATFORM_MANAGERS[platform]();\n this.platformManagers[platform] = new Manager(this.projectRoot, port, managerParams);\n break;\n }\n }\n }\n return this.platformManagers[platform] as PlatformManagers[Platform];\n }\n}\n"],"names":["BundlerDevServer","debug","require","PLATFORM_MANAGERS","simulator","ApplePlatformManager","emulator","AndroidPlatformManager","projectRoot","platformBundlers","options","tunnel","devSession","bonjour","instance","platformManagers","urlCreator","notifier","devToolsPluginManager","DevToolsPluginManager","isDevClient","setInstance","getManifestMiddlewareAsync","Middleware","ExpoGoManifestHandlerMiddleware","getUrlCreator","middleware","constructUrl","bind","mode","minify","isNativeWebpack","name","isTargetingNative","privateKeyPath","startAsync","stopAsync","headless","startHeadlessAsync","startImplementationAsync","postStartAsync","waitForTypeScriptAsync","watchEnvironmentVariables","port","CommandError","initUrlCreator","server","close","callback","addListener","location","host","url","protocol","messageSocket","broadcast","hostType","env","EXPO_OFFLINE","_startTunnelAsync","envIsWebcontainer","isExporting","Promise","all","startDevSessionAsync","startBonjourAsync","watchConfig","stopObserving","FileNotifier","getConfigModuleIds","startObserving","getInstance","_createTunnel","useV2Tunnel","EXPO_UNSTABLE_TUNNEL_V2","useExpoAccount","AsyncWsTunnel","AsyncNgrok","DevelopmentSession","getNativeRuntimeUrl","getDevServerUrl","runtime","Bonjour","announceAsync","isTargetingWeb","web","broadcastMessage","method","params","undefined","closeAsync","catch","e","Log","error","exception","resolveWithTimeout","resolve","reject","code","timeout","errorMessage","assert","UrlCreator","init","getTunnelUrl","opts","constructDevClientUrl","scheme","getDevServerUrlOrAssert","getJsInspectorBaseUrl","getActiveUrl","openPlatformAsync","launchTarget","resolver","serverUrl","isInteractive","openBrowserAsync","manager","getPlatformManagerAsync","openAsync","openCustomRuntimeAsync","launchProps","props","getExpoGoUrl","isRedirectPageEnabled","EXPO_NO_REDIRECT_PAGE","resolveFrom","silent","getRedirectUrl","platform","constructLoadingUrl","ofPlatform","managerParams","getCustomRuntimeUrl","Manager"],"mappings":"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"}
@@ -271,7 +271,8 @@ class MetroTerminalReporter extends _TerminalReporter.TerminalReporter {
271
271
  let hasStack = false;
272
272
  const parsed = data.map((msg)=>{
273
273
  // Quick check to see if an unsymbolicated stack is being logged.
274
- if (typeof msg === 'string' && msg.includes('.bundle//&platform=')) {
274
+ if (typeof msg === 'string' && // Native stack frames use `.bundle//&platform=...`; web stack frames use `.bundle?platform=...`.
275
+ (msg.includes('.bundle//&platform=') || msg.includes('.bundle?platform='))) {
275
276
  const stack = (0, _serverLogLikeMetro.parseErrorStringToObject)(msg);
276
277
  if (stack) {
277
278
  hasStack = true;