if (!Object.assign) { throw "GraphQLChannel requires Object.assign" } var GraphQLChannel = { _getQueryId: function() { return Date.now() }, // Override to log sends and receives log: function() { // pass }, subscription: { _backlog: [], // Handle any queries set up before the channel was ready connected: function() { this._graphQLReady = true var _this = this this._backlog.forEach(function(req) { _this.logOrPerform(req[0], req[1]) }) }, // Called by server-sent events received: function(data) { GraphQLChannel.log("[GraphQLChannel received]", data) var queryId = data.query_id var entry = GraphQLChannel.registry.get(queryId) if (!entry) { // The queryId doesn't exist on this client, // it must be for another one of this user's tabs } else if (data.close) { // An existing request is now completed GraphQLChannel.log("[GraphQLChannel closing]", queryId) GraphQLChannel.registry.unset(queryId) } else { // Patch for an existing request GraphQLChannel.registry.mergePatch(entry, data.patch) entry.onResponse(entry.responseData) } }, // @return [Any] A handle for cancelling this query fetch: function(queryString, variables, onResponse) { var queryId = GraphQLChannel._getQueryId() GraphQLChannel.registry.set(queryId, onResponse) GraphQLChannel.log("[GraphQLChannel sending]", queryString, variables, queryId) this.logOrPerform("fetch", { query: queryString, variables: JSON.stringify(variables), query_id: queryId, }) return queryId }, // Unsubscribe from any future patches // // @example Ignoring any subscription or deferred fields // var queryHandle = App.GraphqlChannel.fetch(/* ... */) // App.GraphqlChannel.clear(queryHandle) // // @param [Any] a handle returned by `fetch` // @return void clear: function(queryId) { GraphQLChannel.log("[GraphQLChannel clearing]", queryId) GraphQLChannel.registry.unset(queryId) this.logOrPerform("clear", {query_id: queryId}) }, logOrPerform(operation, params) { if (this._graphQLReady) { this.perform(operation, params) } else { this._backlog.push([operation, params]) } }, }, // This registry keeps track of outstanding requests registry: { // {queryId => entry} pairs of outstanding queries. // They should be removed when we receive a payload with `close: true` _requests: {}, // Store handlers & results for `queryId`. // These handlers & results can be fetched by `.get(queryId)` set: function(queryId, onResponse) { var entry = { queryId: queryId, onResponse: onResponse, responseData: {}, } this._requests[queryId] = entry }, get: function(queryId) { return this._requests[queryId] }, unset: function(queryId) { delete this._requests[queryId] }, // Mutate `entry` by merging `patch` into its `responseData` mergePatch: function(entry, patch) { if (patch.path.length === 0) { entry.responseData = patch.value } else { var targetHash = entry.responseData var steps = patch.path.slice(0, patch.path.length - 1) var lastKey = patch.path[patch.path.length - 1] steps.forEach(function(step) { var nextStep = targetHash[step] if (nextStep == null) { nextStep = targetHash[step] = {} } targetHash = nextStep }) targetHash[lastKey] = patch.value } }, } }