UNPKG

gatsby

Version:
208 lines (205 loc) • 7.02 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.websocketManager = exports.WebsocketManager = void 0; var _redux = require("../redux"); var _internal = require("../redux/actions/internal"); var _gatsbyTelemetry = _interopRequireDefault(require("gatsby-telemetry")); var _url = _interopRequireDefault(require("url")); var _crypto = require("crypto"); var _findPageByPath = require("./find-page-by-path"); var _socket = require("socket.io"); var _pageMode = require("./page-mode"); /* eslint-disable no-invalid-this */ function hashPaths(paths) { return paths.map(path => (0, _crypto.createHash)(`sha256`).update(path).digest(`hex`)); } class WebsocketManager { activePaths = new Set(); clients = new Set(); errors = new Map(); staticQueryResults = new Map(); init = ({ server }) => { // make typescript happy, else it complained about this.websocket being undefined const websocket = new _socket.Server(server, { // we see ping-pong timeouts on gatsby-cloud when socket.io is running for a while // increasing it should help // @see https://github.com/socketio/socket.io/issues/3259#issuecomment-448058937 pingTimeout: 30000, // whitelist all (https://github.com/expressjs/cors#configuration-options) cors: { origin: true }, cookie: true }); this.websocket = websocket; const updateServerActivePaths = () => { const serverActivePaths = new Set(); for (const client of this.clients) { if (client.activePath) { serverActivePaths.add(client.activePath); } } this.activePaths = serverActivePaths; }; websocket.on(`connection`, socket => { var _socket$handshake, _socket$handshake$hea; const clientInfo = { activePath: null, socket }; this.clients.add(clientInfo); const setActivePath = (newActivePath, fallbackTo404 = false) => { let activePagePath = null; if (newActivePath) { const page = (0, _findPageByPath.findPageByPath)(_redux.store.getState(), newActivePath, fallbackTo404); if (page) { // when it's SSR we don't want to return the page path but the actualy url used, // this is necessary when matchPaths are used. if ((0, _pageMode.getPageMode)(page) === `SSR`) { activePagePath = newActivePath; } else { activePagePath = page.path; } } } clientInfo.activePath = activePagePath; updateServerActivePaths(); }; if (socket !== null && socket !== void 0 && (_socket$handshake = socket.handshake) !== null && _socket$handshake !== void 0 && (_socket$handshake$hea = _socket$handshake.headers) !== null && _socket$handshake$hea !== void 0 && _socket$handshake$hea.referer) { const path = _url.default.parse(socket.handshake.headers.referer).path; setActivePath(path, true); } this.errors.forEach((message, errorID) => { socket.send({ type: `overlayError`, payload: { id: errorID, message } }); }); socket.on(`registerPath`, path => { setActivePath(path, true); }); socket.on(`disconnect`, () => { setActivePath(null); this.clients.delete(clientInfo); }); socket.on(`unregisterPath`, _path => { setActivePath(null); }); }); if (process.env.GATSBY_QUERY_ON_DEMAND) { // page-data marked stale due to dirty query tracking const boundEmitStalePageDataPathsFromDirtyQueryTracking = this.emitStalePageDataPathsFromDirtyQueryTracking.bind(this); _redux.emitter.on(`CREATE_PAGE`, boundEmitStalePageDataPathsFromDirtyQueryTracking); _redux.emitter.on(`CREATE_NODE`, boundEmitStalePageDataPathsFromDirtyQueryTracking); _redux.emitter.on(`DELETE_NODE`, boundEmitStalePageDataPathsFromDirtyQueryTracking); _redux.emitter.on(`QUERY_EXTRACTED`, boundEmitStalePageDataPathsFromDirtyQueryTracking); } // page-data marked stale due to static query hashes change _redux.emitter.on(`ADD_PENDING_TEMPLATE_DATA_WRITE`, this.emitStalePageDataPathsFromStaticQueriesAssignment.bind(this)); return websocket; }; getSocket = () => this.websocket; emitStaticQueryData = data => { this.staticQueryResults.set(data.id, data); if (this.websocket) { this.websocket.send({ type: `staticQueryResult`, payload: data }); if (this.clients.size > 0) { _gatsbyTelemetry.default.trackCli(`WEBSOCKET_EMIT_STATIC_PAGE_DATA_UPDATE`, { siteMeasurements: { clientsCount: this.clients.size, paths: hashPaths(Array.from(this.activePaths)) } }, { debounce: true }); } } }; emitPageData = data => { if (this.websocket) { this.websocket.send({ type: `pageQueryResult`, payload: data }); if (this.clients.size > 0) { _gatsbyTelemetry.default.trackCli(`WEBSOCKET_EMIT_PAGE_DATA_UPDATE`, { siteMeasurements: { clientsCount: this.clients.size, paths: hashPaths(Array.from(this.activePaths)) } }, { debounce: true }); } } }; emitSliceData = data => { if (this.websocket) { this.websocket.send({ type: `sliceQueryResult`, payload: data }); } }; emitError = (id, message) => { if (message) { this.errors.set(id, message); } else { this.errors.delete(id); } if (this.websocket) { this.websocket.send({ type: `overlayError`, payload: { id, message } }); } }; emitStalePageDataPathsFromDirtyQueryTracking() { const dirtyQueries = _redux.store.getState().queries.dirtyQueriesListToEmitViaWebsocket; if (this.emitStalePageDataPaths(dirtyQueries)) { _redux.store.dispatch((0, _internal.clearDirtyQueriesListToEmitViaWebsocket)()); } } emitStalePageDataPathsFromStaticQueriesAssignment(pendingTemplateDataWrite) { this.emitStalePageDataPaths(Array.from(pendingTemplateDataWrite.payload.pages)); } emitStalePageDataPaths(stalePageDataPaths) { if (stalePageDataPaths.length > 0) { if (this.websocket) { this.websocket.send({ type: `stalePageData`, payload: { stalePageDataPaths } }); return true; } } return false; } emitStaleServerData() { if (this.websocket) { this.websocket.send({ type: `staleServerData` }); return true; } return false; } } exports.WebsocketManager = WebsocketManager; const websocketManager = new WebsocketManager(); exports.websocketManager = websocketManager; //# sourceMappingURL=websocket-manager.js.map