apollo-link-dedup
Advanced tools
Comparing version 1.0.0 to 1.0.2
### vNext | ||
### 1.0.2 | ||
- fixed bug where next observable subscription was not deduplicated | ||
- moved to better rollup build | ||
### 1.0.1 [Unpublished] | ||
<!-- This build had a nasty but that was caught by the apollo client test suite --> | ||
- fixed bug where next observable subscription was not deduplicated | ||
### 1.0.0 | ||
- moved from default export to named to be consistent with rest of link ecosystem |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('apollo-link')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'apollo-link'], factory) : | ||
(factory((global.dedupLink = {}),global.apolloLink)); | ||
(factory((global.apolloLink = global.apolloLink || {}, global.apolloLink.dedup = {}),global.apolloLink.core)); | ||
}(this, (function (exports,apolloLink) { 'use strict'; | ||
@@ -20,4 +20,5 @@ | ||
function DedupLink() { | ||
var _this = _super.call(this) || this; | ||
_this.inFlightRequestObservables = {}; | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.inFlightRequestObservables = new Map(); | ||
_this.subscribers = new Map(); | ||
return _this; | ||
@@ -31,23 +32,46 @@ } | ||
var key = operation.toKey(); | ||
if (!this.inFlightRequestObservables[key]) { | ||
this.inFlightRequestObservables[key] = forward(operation); | ||
var cleanup = function (key) { | ||
_this.inFlightRequestObservables.delete(key); | ||
var prev = _this.subscribers.get(key); | ||
return prev; | ||
}; | ||
if (!this.inFlightRequestObservables.get(key)) { | ||
var singleObserver_1 = forward(operation); | ||
var subscription_1; | ||
var sharedObserver = new apolloLink.Observable(function (observer) { | ||
var prev = _this.subscribers.get(key); | ||
if (!prev) | ||
prev = { next: [], error: [], complete: [] }; | ||
_this.subscribers.set(key, { | ||
next: prev.next.concat([observer.next.bind(observer)]), | ||
error: prev.error.concat([observer.error.bind(observer)]), | ||
complete: prev.complete.concat([observer.complete.bind(observer)]), | ||
}); | ||
if (!subscription_1) { | ||
subscription_1 = singleObserver_1.subscribe({ | ||
next: function (result) { | ||
var prev = cleanup(key); | ||
_this.subscribers.delete(key); | ||
if (prev) { | ||
prev.next.forEach(function (next) { return next(result); }); | ||
prev.complete.forEach(function (complete) { return complete(); }); | ||
} | ||
}, | ||
error: function (error) { | ||
var prev = cleanup(key); | ||
_this.subscribers.delete(key); | ||
if (prev) | ||
prev.error.forEach(function (err) { return err(error); }); | ||
}, | ||
}); | ||
} | ||
return function () { | ||
if (subscription_1) | ||
subscription_1.unsubscribe(); | ||
_this.inFlightRequestObservables.delete(key); | ||
}; | ||
}); | ||
this.inFlightRequestObservables.set(key, sharedObserver); | ||
} | ||
return new apolloLink.Observable(function (observer) { | ||
var subscription = _this.inFlightRequestObservables[key].subscribe({ | ||
next: function (result) { | ||
delete _this.inFlightRequestObservables[key]; | ||
observer.next(result); | ||
}, | ||
error: function (error) { | ||
delete _this.inFlightRequestObservables[key]; | ||
observer.error(error); | ||
}, | ||
complete: observer.complete.bind(observer), | ||
}); | ||
return function () { | ||
if (subscription) | ||
subscription.unsubscribe(); | ||
delete _this.inFlightRequestObservables[key]; | ||
}; | ||
}); | ||
return this.inFlightRequestObservables.get(key); | ||
}; | ||
@@ -54,0 +78,0 @@ return DedupLink; |
@@ -5,4 +5,4 @@ /// <reference types="zen-observable" /> | ||
private inFlightRequestObservables; | ||
constructor(); | ||
private subscribers; | ||
request(operation: Operation, forward: NextLink): Observable<FetchResult>; | ||
} |
@@ -15,4 +15,5 @@ var __extends = (this && this.__extends) || (function () { | ||
function DedupLink() { | ||
var _this = _super.call(this) || this; | ||
_this.inFlightRequestObservables = {}; | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.inFlightRequestObservables = new Map(); | ||
_this.subscribers = new Map(); | ||
return _this; | ||
@@ -26,23 +27,46 @@ } | ||
var key = operation.toKey(); | ||
if (!this.inFlightRequestObservables[key]) { | ||
this.inFlightRequestObservables[key] = forward(operation); | ||
var cleanup = function (key) { | ||
_this.inFlightRequestObservables.delete(key); | ||
var prev = _this.subscribers.get(key); | ||
return prev; | ||
}; | ||
if (!this.inFlightRequestObservables.get(key)) { | ||
var singleObserver_1 = forward(operation); | ||
var subscription_1; | ||
var sharedObserver = new Observable(function (observer) { | ||
var prev = _this.subscribers.get(key); | ||
if (!prev) | ||
prev = { next: [], error: [], complete: [] }; | ||
_this.subscribers.set(key, { | ||
next: prev.next.concat([observer.next.bind(observer)]), | ||
error: prev.error.concat([observer.error.bind(observer)]), | ||
complete: prev.complete.concat([observer.complete.bind(observer)]), | ||
}); | ||
if (!subscription_1) { | ||
subscription_1 = singleObserver_1.subscribe({ | ||
next: function (result) { | ||
var prev = cleanup(key); | ||
_this.subscribers.delete(key); | ||
if (prev) { | ||
prev.next.forEach(function (next) { return next(result); }); | ||
prev.complete.forEach(function (complete) { return complete(); }); | ||
} | ||
}, | ||
error: function (error) { | ||
var prev = cleanup(key); | ||
_this.subscribers.delete(key); | ||
if (prev) | ||
prev.error.forEach(function (err) { return err(error); }); | ||
}, | ||
}); | ||
} | ||
return function () { | ||
if (subscription_1) | ||
subscription_1.unsubscribe(); | ||
_this.inFlightRequestObservables.delete(key); | ||
}; | ||
}); | ||
this.inFlightRequestObservables.set(key, sharedObserver); | ||
} | ||
return new Observable(function (observer) { | ||
var subscription = _this.inFlightRequestObservables[key].subscribe({ | ||
next: function (result) { | ||
delete _this.inFlightRequestObservables[key]; | ||
observer.next(result); | ||
}, | ||
error: function (error) { | ||
delete _this.inFlightRequestObservables[key]; | ||
observer.error(error); | ||
}, | ||
complete: observer.complete.bind(observer), | ||
}); | ||
return function () { | ||
if (subscription) | ||
subscription.unsubscribe(); | ||
delete _this.inFlightRequestObservables[key]; | ||
}; | ||
}); | ||
return this.inFlightRequestObservables.get(key); | ||
}; | ||
@@ -49,0 +73,0 @@ return DedupLink; |
{ | ||
"name": "apollo-link-dedup", | ||
"version": "1.0.0", | ||
"version": "1.0.2", | ||
"description": "Deduplicates queries that are currently on the wire", | ||
@@ -14,5 +14,5 @@ "author": "Evans Hauser <evanshauser@gmail.com>", | ||
"main": "./lib/bundle.umd.js", | ||
"module": "./lib/dedupLink.js", | ||
"jsnext:main": "./lib/dedupLink.js", | ||
"typings": "./lib/dedupLink.d.ts", | ||
"module": "./lib/index.js", | ||
"jsnext:main": "./lib/index.js", | ||
"typings": "./lib/index.d.ts", | ||
"repository": { | ||
@@ -44,8 +44,8 @@ "type": "git", | ||
"peerDependencies": { | ||
"apollo-link": "^1.0.0" | ||
"apollo-link": "^1.0.3" | ||
}, | ||
"devDependencies": { | ||
"@types/graphql": "0.11.5", | ||
"@types/jest": "21.1.4", | ||
"apollo-link": "^1.0.0", | ||
"@types/graphql": "0.11.6", | ||
"@types/jest": "21.1.6", | ||
"apollo-link": "^1.0.3", | ||
"browserify": "14.5.0", | ||
@@ -56,7 +56,7 @@ "graphql": "0.11.7", | ||
"rimraf": "2.6.1", | ||
"rollup": "0.45.2", | ||
"ts-jest": "21.1.3", | ||
"rollup": "0.51.5", | ||
"ts-jest": "21.2.2", | ||
"tslint": "5.8.0", | ||
"typescript": "2.5.1", | ||
"uglify-js": "3.1.5" | ||
"typescript": "2.6.1", | ||
"uglify-js": "3.1.9" | ||
}, | ||
@@ -63,0 +63,0 @@ "jest": { |
@@ -1,17 +0,3 @@ | ||
export default { | ||
entry: 'lib/dedupLink.js', | ||
dest: 'lib/bundle.umd.js', | ||
format: 'umd', | ||
sourceMap: true, | ||
moduleName: 'dedupLink', | ||
exports: 'named', | ||
onwarn, | ||
}; | ||
import build from '../../rollup.config'; | ||
function onwarn(message) { | ||
const suppressed = ['UNRESOLVED_IMPORT', 'THIS_IS_UNDEFINED']; | ||
if (!suppressed.find(code => message.code === code)) { | ||
return console.warn(message.message); | ||
} | ||
} | ||
export default build('dedup'); |
@@ -68,3 +68,3 @@ import { | ||
let error; | ||
const data = { data: { data: 'some data' } }; | ||
const data = { data: 'some data' }; | ||
@@ -100,16 +100,20 @@ const request: GraphQLRequest = { | ||
execute(deduper, request).subscribe({ | ||
error: actualError => { | ||
expect(actualError).toEqual(error); | ||
try { | ||
execute(deduper, request).subscribe({ | ||
error: actualError => { | ||
expect(actualError).toEqual(error); | ||
//second query | ||
execute(deduper, request).subscribe({ | ||
next: result => { | ||
expect(result).toEqual(data); | ||
expect(called).toBe(2); | ||
done(); | ||
}, | ||
}); | ||
}, | ||
}); | ||
//second query | ||
execute(deduper, request).subscribe({ | ||
next: result => { | ||
expect(result).toEqual(data); | ||
expect(called).toBe(2); | ||
done(); | ||
}, | ||
}); | ||
}, | ||
}); | ||
} catch (e) { | ||
done.fail(e); | ||
} | ||
}); | ||
@@ -142,4 +146,4 @@ | ||
new ApolloLink(() => { | ||
called += 1; | ||
return new Observable(observer => { | ||
called += 1; | ||
setTimeout(observer.complete.bind(observer)); | ||
@@ -154,3 +158,46 @@ }); | ||
}); | ||
it(`works for nested queries`, done => { | ||
const document: DocumentNode = gql` | ||
query test1($x: String) { | ||
test(x: $x) | ||
} | ||
`; | ||
const variables1 = { x: 'Hello World' }; | ||
const variables2 = { x: 'Hello World' }; | ||
const request1: GraphQLRequest = { | ||
query: document, | ||
variables: variables1, | ||
operationName: getOperationName(document), | ||
}; | ||
const request2: GraphQLRequest = { | ||
query: document, | ||
variables: variables2, | ||
operationName: getOperationName(document), | ||
}; | ||
let called = 0; | ||
const deduper = ApolloLink.from([ | ||
new DedupLink(), | ||
new ApolloLink(() => { | ||
return new Observable(observer => { | ||
called += 1; | ||
observer.next({ data: { test: 1 } }); | ||
}); | ||
}), | ||
]); | ||
execute(deduper, request1).subscribe({ | ||
complete: () => { | ||
execute(deduper, request2).subscribe({ | ||
complete: () => { | ||
expect(called).toBe(2); | ||
done(); | ||
}, | ||
}); | ||
}, | ||
}); | ||
}); | ||
it(`can bypass deduplication if desired`, () => { | ||
@@ -157,0 +204,0 @@ const document: DocumentNode = gql` |
@@ -13,11 +13,8 @@ import { | ||
export class DedupLink extends ApolloLink { | ||
private inFlightRequestObservables: { | ||
[key: string]: Observable<FetchResult>; | ||
}; | ||
private inFlightRequestObservables: Map< | ||
string, | ||
Observable<FetchResult> | ||
> = new Map(); | ||
private subscribers: Map<string, any> = new Map(); | ||
constructor() { | ||
super(); | ||
this.inFlightRequestObservables = {}; | ||
} | ||
public request( | ||
@@ -33,24 +30,57 @@ operation: Operation, | ||
const key = operation.toKey(); | ||
if (!this.inFlightRequestObservables[key]) { | ||
this.inFlightRequestObservables[key] = forward(operation); | ||
} | ||
return new Observable<FetchResult>(observer => { | ||
const subscription = this.inFlightRequestObservables[key].subscribe({ | ||
next: result => { | ||
delete this.inFlightRequestObservables[key]; | ||
observer.next(result); | ||
}, | ||
error: error => { | ||
delete this.inFlightRequestObservables[key]; | ||
observer.error(error); | ||
}, | ||
complete: observer.complete.bind(observer), | ||
const cleanup = key => { | ||
this.inFlightRequestObservables.delete(key); | ||
const prev = this.subscribers.get(key); | ||
return prev; | ||
}; | ||
if (!this.inFlightRequestObservables.get(key)) { | ||
// this is a new request, i.e. we haven't deduplicated it yet | ||
// call the next link | ||
const singleObserver = forward(operation); | ||
let subscription; | ||
const sharedObserver = new Observable(observer => { | ||
// this will still be called by each subscriber regardless of | ||
// deduplication status | ||
let prev = this.subscribers.get(key); | ||
if (!prev) prev = { next: [], error: [], complete: [] }; | ||
this.subscribers.set(key, { | ||
next: prev.next.concat([observer.next.bind(observer)]), | ||
error: prev.error.concat([observer.error.bind(observer)]), | ||
complete: prev.complete.concat([observer.complete.bind(observer)]), | ||
}); | ||
if (!subscription) { | ||
subscription = singleObserver.subscribe({ | ||
next: result => { | ||
const prev = cleanup(key); | ||
this.subscribers.delete(key); | ||
if (prev) { | ||
prev.next.forEach(next => next(result)); | ||
prev.complete.forEach(complete => complete()); | ||
} | ||
}, | ||
error: error => { | ||
const prev = cleanup(key); | ||
this.subscribers.delete(key); | ||
if (prev) prev.error.forEach(err => err(error)); | ||
}, | ||
}); | ||
} | ||
return () => { | ||
if (subscription) subscription.unsubscribe(); | ||
this.inFlightRequestObservables.delete(key); | ||
}; | ||
}); | ||
return () => { | ||
if (subscription) subscription.unsubscribe(); | ||
delete this.inFlightRequestObservables[key]; | ||
}; | ||
}); | ||
this.inFlightRequestObservables.set(key, sharedObserver); | ||
} | ||
// return shared Observable | ||
return this.inFlightRequestObservables.get(key); | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29970
16
494