'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.startAsync = exports.createAuthenticationContextAsync = undefined; var _extends2 = require('babel-runtime/helpers/extends'); var _extends3 = _interopRequireDefault(_extends2); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); let generateSecureRandomTokenAsync = (() => { var _ref = (0, _asyncToGenerator3.default)(function* () { return new _promise2.default(function (resolve, reject) { _crypto2.default.randomBytes(32, function (error, buffer) { if (error) reject(error);else resolve(_base64url2.default.fromBase64(buffer.toString('base64'))); }); }); }); return function generateSecureRandomTokenAsync() { return _ref.apply(this, arguments); }; })(); let createAuthenticationContextAsync = exports.createAuthenticationContextAsync = (() => { var _ref2 = (0, _asyncToGenerator3.default)(function* ({ port }) { const clientAuthenticationToken = yield generateSecureRandomTokenAsync(); const endpointUrlToken = yield generateSecureRandomTokenAsync(); const graphQLEndpointPath = `/${endpointUrlToken}/graphql`; const hostname = `localhost:${port}`; const webSocketGraphQLUrl = `ws://${hostname}${graphQLEndpointPath}`; const allowedOrigin = `http://${hostname}`; return { clientAuthenticationToken, graphQLEndpointPath, webSocketGraphQLUrl, allowedOrigin, requestHandler: function requestHandler(request, response) { response.json({ webSocketGraphQLUrl, clientAuthenticationToken }); } }; }); return function createAuthenticationContextAsync(_x) { return _ref2.apply(this, arguments); }; })(); let startAsync = exports.startAsync = (() => { var _ref3 = (0, _asyncToGenerator3.default)(function* (projectDir) { const port = yield (0, _freeportAsync2.default)(19002); const server = (0, _express2.default)(); const authenticationContext = yield createAuthenticationContextAsync({ port }); const webSocketGraphQLUrl = authenticationContext.webSocketGraphQLUrl, clientAuthenticationToken = authenticationContext.clientAuthenticationToken; server.get('/dev-tools-info', authenticationContext.requestHandler); server.use('/_next', _express2.default.static(_path2.default.join(__dirname, '../client/_next'), { // All paths in the _next folder include hashes, so they can be cached more aggressively. immutable: true, maxAge: '1y', setHeaders })); server.use(_express2.default.static(_path2.default.join(__dirname, '../client'), { setHeaders })); const httpServer = _http2.default.createServer(server); yield new _promise2.default(function (resolve, reject) { httpServer.once('error', reject); httpServer.once('listening', resolve); httpServer.listen(port, 'localhost'); }); startGraphQLServer(projectDir, httpServer, authenticationContext); yield _xdl.ProjectSettings.setPackagerInfoAsync(projectDir, { devToolsPort: port }); return `http://localhost:${port}`; }); return function startAsync(_x2) { return _ref3.apply(this, arguments); }; })(); exports.startGraphQLServer = startGraphQLServer; var _xdl = require('@expo/xdl'); var _subscriptionsTransportWs = require('subscriptions-transport-ws'); var _graphql = require('graphql'); var graphql = _interopRequireWildcard(_graphql); var _express = require('express'); var _express2 = _interopRequireDefault(_express); var _freeportAsync = require('freeport-async'); var _freeportAsync2 = _interopRequireDefault(_freeportAsync); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _http = require('http'); var _http2 = _interopRequireDefault(_http); var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto); var _base64url = require('base64url'); var _base64url2 = _interopRequireDefault(_base64url); var _AsyncIterableRingBuffer = require('./graphql/AsyncIterableRingBuffer'); var _AsyncIterableRingBuffer2 = _interopRequireDefault(_AsyncIterableRingBuffer); var _GraphQLSchema = require('./graphql/GraphQLSchema'); var _GraphQLSchema2 = _interopRequireDefault(_GraphQLSchema); var _createContext = require('./graphql/createContext'); var _createContext2 = _interopRequireDefault(_createContext); var _Issues = require('./graphql/Issues'); var _Issues2 = _interopRequireDefault(_Issues); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const serverStartTimeUTCString = new Date().toUTCString(); function setHeaders(res) { // Set the Last-Modified header to server start time because otherwise it // becomes Sat, 26 Oct 1985 08:15:00 GMT for files installed from npm. res.setHeader('Last-Modified', serverStartTimeUTCString); } function startGraphQLServer(projectDir, httpServer, authenticationContext) { const layout = createLayout(); const issues = new _Issues2.default(); const messageBuffer = createMessageBuffer(projectDir, issues); _subscriptionsTransportWs.SubscriptionServer.create({ schema: _GraphQLSchema2.default, execute: graphql.execute, subscribe: graphql.subscribe, onOperation: (operation, params) => (0, _extends3.default)({}, params, { context: (0, _createContext2.default)({ projectDir, messageBuffer, layout, issues }) }), onConnect: connectionParams => { if (!connectionParams.clientAuthenticationToken || connectionParams.clientAuthenticationToken !== authenticationContext.clientAuthenticationToken) { throw new Error('Dev Tools API authentication failed.'); } return true; } }, { server: httpServer, path: authenticationContext.graphQLEndpointPath, verifyClient: info => { return info.origin === authenticationContext.allowedOrigin; } }); } function createLayout() { let layout = { selected: null, sources: null, sourceLastReads: {} }; return { get() { return layout; }, set(newLayout) { layout = (0, _extends3.default)({}, layout, newLayout); }, setLastRead(sourceId, lastReadCursor) { layout.sourceLastReads[sourceId] = lastReadCursor; } }; } function createMessageBuffer(projectRoot, issues) { const buffer = new _AsyncIterableRingBuffer2.default(10000); // eslint-disable-next-line no-new new _xdl.PackagerLogsStream({ projectRoot, updateLogs: updater => { const chunks = updater([]); chunks.forEach(chunk => { if (chunk.issueId) { if (chunk.issueCleared) { issues.clearIssue(chunk.issueId); } else { issues.addIssue(chunk.issueId, chunk); } return; } buffer.push({ type: 'ADDED', sourceId: _createContext.PROCESS_SOURCE.id, node: chunk }); }); }, onStartBuildBundle: chunk => { buffer.push({ type: 'ADDED', sourceId: _createContext.PROCESS_SOURCE.id, node: (0, _extends3.default)({}, chunk, { progress: 0, duration: 0 }) }); }, onProgressBuildBundle: (percentage, start, chunk) => { buffer.push({ type: 'UPDATED', sourceId: _createContext.PROCESS_SOURCE.id, node: (0, _extends3.default)({}, chunk, { progress: percentage, duration: new Date() - (start || new Date()) }) }); }, onFinishBuildBundle: (error, start, end, chunk) => { buffer.push({ type: 'UPDATED', sourceId: _createContext.PROCESS_SOURCE.id, node: (0, _extends3.default)({}, chunk, { error, duration: end - (start || new Date()) }) }); } }); // needed for validation logging to function _xdl.ProjectUtils.attachLoggerStream(projectRoot, { stream: { write: chunk => { if (chunk.tag === 'device') { buffer.push({ type: 'ADDED', sourceId: chunk.deviceId, node: chunk }); } } }, type: 'raw' }); _xdl.Logger.global.addStream({ stream: { write: chunk => { buffer.push({ type: 'ADDED', sourceId: _createContext.PROCESS_SOURCE.id, node: chunk }); } }, type: 'raw' }); return buffer; }