'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;
}