@ofload/graphql-mongodb-subscriptions 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Matthew Wheatley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # graphql-mongodb-subscriptions
2
+
3
+ This repository was bootstrapped by cloning [graphql-redis-subscriptions](https://github.com/davidyaha/graphql-redis-subscriptions) and also referenced the [graphql-postgres-subscriptions](https://github.com/GraphQLCollege/graphql-postgres-subscriptions).
4
+
5
+ This package implements the PubSubEngine Interface from the [graphql-subscriptions](https://github.com/apollographql/graphql-subscriptions) package and also the new AsyncIterator interface.
6
+ It allows you to connect your subscriptions manager to a MongoDB Pub Sub mechanism to support
7
+ multiple subscription manager instances.
8
+
9
+ ## Installation
10
+ At first, install the `graphql-mongodb-subscriptions` package:
11
+ ```
12
+ npm install graphql-mongodb-subscriptions
13
+ ```
14
+
15
+ As the [graphql-subscriptions](https://github.com/apollographql/graphql-subscriptions) package is declared as a peer dependency, you might receive warning about an unmet peer dependency if it's not installed already by one of your other packages. In that case you also need to install it too:
16
+ ```
17
+ npm install graphql-subscriptions
18
+ ```
19
+
20
+ ## Using as AsyncIterator
21
+
22
+ Define your GraphQL schema with a `Subscription` type:
23
+
24
+ ```graphql
25
+ schema {
26
+ query: Query
27
+ mutation: Mutation
28
+ subscription: Subscription
29
+ }
30
+
31
+ type Subscription {
32
+ somethingChanged: Result
33
+ }
34
+
35
+ type Result {
36
+ id: String
37
+ }
38
+ ```
39
+
40
+ Now, let's create a simple `MongodbPubSub` instance:
41
+
42
+ ```javascript
43
+ import { MongodbPubSub } from 'graphql-mongodb-subscriptions';
44
+ const pubsub = new MongodbPubSub();
45
+ ```
46
+
47
+ Now, implement your Subscriptions type resolver, using the `pubsub.asyncIterator` to map the event you need:
48
+
49
+ ```javascript
50
+ const SOMETHING_CHANGED_TOPIC = 'something_changed';
51
+
52
+ export const resolvers = {
53
+ Subscription: {
54
+ somethingChanged: {
55
+ subscribe: () => pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC),
56
+ },
57
+ },
58
+ }
59
+ ```
60
+
61
+ > Subscriptions resolvers are not a function, but an object with `subscribe` method, that returns `AsyncIterable`.
62
+
63
+ Calling the method `asyncIterator` of the `MongodbPubSub` instance will send MongoDB a `SUBSCRIBE` message to the topic provided and will return an `AsyncIterator` binded to the MongodbPubSub instance and listens to any event published on that topic.
64
+ Now, the GraphQL engine knows that `somethingChanged` is a subscription, and every time we will use `pubsub.publish` over this topic, the `MongodbPubSub` will `PUBLISH` the event over MongoDB to all subscribed instances and those in their turn will emit the event to GraphQL using the `next` callback given by the GraphQL engine.
65
+
66
+ ```js
67
+ pubsub.publish(SOMETHING_CHANGED_TOPIC, { somethingChanged: { id: "123" }});
68
+ ```
69
+
70
+ ## Dynamically create a topic based on subscription args passed on the query
71
+
72
+ ```javascript
73
+ export const resolvers = {
74
+ Subscription: {
75
+ somethingChanged: {
76
+ subscribe: (_, args) => pubsub.asyncIterator(`${SOMETHING_CHANGED_TOPIC}.${args.relevantId}`),
77
+ },
78
+ },
79
+ }
80
+ ```
81
+
82
+ ## Using both arguments and payload to filter events
83
+
84
+ ```javascript
85
+ import { withFilter } from 'graphql-subscriptions';
86
+
87
+ export const resolvers = {
88
+ Subscription: {
89
+ somethingChanged: {
90
+ subscribe: withFilter(
91
+ (_, args) => pubsub.asyncIterator(`${SOMETHING_CHANGED_TOPIC}.${args.relevantId}`),
92
+ (payload, variables) => payload.somethingChanged.id === variables.relevantId,
93
+ ),
94
+ },
95
+ },
96
+ }
97
+ ```
98
+
99
+ ## Configuring MongodbPubSub
100
+
101
+ `MongodbPubSub` constructor can be passed a configuration object to enable some advanced features.
102
+
103
+ `MongoPubSubChannelOptions` are used to change the default options for the capped collection that will be created. [View MongoDB capped collection docs](https://www.mongodb.com/docs/manual/core/capped-collections/)
104
+
105
+ ```ts
106
+ export type CommonMessageHandler = (message: any) => any;
107
+
108
+ export interface MongoPubSubChannelOptions {
109
+ size: number;
110
+ max: number;
111
+ }
112
+
113
+ export interface PubSubMongoDbOptions {
114
+ connectionDb: Db;
115
+ channelName?: string;
116
+ channelOptions?: MongoPubSubChannelOptions;
117
+ connectionListener?: (event: string, data: any) => void;
118
+ commonMessageHandler?: CommonMessageHandler;
119
+ }
120
+ ```
121
+
122
+ | option | type | default | description |
123
+ |----------------------|------------|-----------------|-------------------------------------------------------------------------------------------------------------------|
124
+ | `connectionDb` | `Db` | `undefined` | pass in an instance of a Mongo DB |
125
+ | `channelName` | `string` | `mubsub` | The name of the capped collection to create inside the provided DB instance. |
126
+ | `channelOptions` | `MongoPubSubChannelOptions` | { size: 100000 } | The options are used to configure the size and constraints of the MongoDB capped collection that will be created. |
127
+ | `connectionListener` | `function` | `undefined` | pass in connection listener to log errors or make sure connection to the MubSub instance is actively listening. |
128
+ | `commonMessageHandler` | `function` | `undefined` | The default handler just passes the message object as is. Use this to pass in a common message handler . |
129
+
130
+ ### commonMessageHandler
131
+
132
+ The common message handler gets called with the received document from MongoDB.
133
+ You can transform the message before it is passed to the individual filter/resolver methods of the subscribers.
134
+ This way it is for example possible to inject one instance of a [DataLoader](https://github.com/facebook/dataloader) which can be used in all filter/resolver methods.
135
+
136
+ ```javascript
137
+ const getDataLoader = () => new DataLoader(...)
138
+ const commonMessageHandler = ({attributes: {id}, data}) => ({id, dataLoader: getDataLoader()})
139
+ const pubsub = new PostgresPubSub({ client, commonMessageHandler });
140
+ ```
141
+
142
+ ```javascript
143
+ export const resolvers = {
144
+ Subscription: {
145
+ somethingChanged: {
146
+ resolve: ({ id, dataLoader }) => dataLoader.load(id)
147
+ }
148
+ }
149
+ };
150
+ ```
151
+
152
+ ### Test
153
+ ```shell script
154
+ npm run test
155
+ ```
@@ -0,0 +1,3 @@
1
+ export { MongodbPubSub } from './mongodb-pub-sub';
2
+ export { PubSubAsyncIterator } from './pubsub-async-iterator';
3
+ export { withFilter } from './with-filter';
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withFilter = exports.PubSubAsyncIterator = exports.MongodbPubSub = void 0;
4
+ var mongodb_pub_sub_1 = require("./mongodb-pub-sub");
5
+ Object.defineProperty(exports, "MongodbPubSub", { enumerable: true, get: function () { return mongodb_pub_sub_1.MongodbPubSub; } });
6
+ var pubsub_async_iterator_1 = require("./pubsub-async-iterator");
7
+ Object.defineProperty(exports, "PubSubAsyncIterator", { enumerable: true, get: function () { return pubsub_async_iterator_1.PubSubAsyncIterator; } });
8
+ var with_filter_1 = require("./with-filter");
9
+ Object.defineProperty(exports, "withFilter", { enumerable: true, get: function () { return with_filter_1.withFilter; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAkD;AAAzC,gHAAA,aAAa,OAAA;AACtB,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAC5B,6CAA2C;AAAlC,yGAAA,UAAU,OAAA"}
@@ -0,0 +1,31 @@
1
+ import { PubSubEngine } from 'graphql-subscriptions';
2
+ import { Db } from 'mongodb';
3
+ type OnMessage<T> = (message: T) => void;
4
+ export type CommonMessageHandler = (message: any) => any;
5
+ export interface MongoPubSubChannelOptions {
6
+ size: number;
7
+ max: number;
8
+ capped?: boolean;
9
+ }
10
+ export interface PubSubMongoDbOptions {
11
+ connectionDb: Db;
12
+ channelName?: string;
13
+ channelOptions?: MongoPubSubChannelOptions;
14
+ connectionListener?: (event: string, data: any) => void;
15
+ commonMessageHandler?: CommonMessageHandler;
16
+ }
17
+ export declare class MongodbPubSub implements PubSubEngine {
18
+ private channelName;
19
+ private channel;
20
+ private commonMessageHandler;
21
+ private readonly subscriptionMap;
22
+ private readonly subsRefsMap;
23
+ private currentSubscriptionId;
24
+ constructor(options: PubSubMongoDbOptions);
25
+ publish<T>(trigger: string, payload: T): Promise<void>;
26
+ subscribe<T = any>(trigger: string, onMessage: OnMessage<T>, options?: unknown): Promise<number>;
27
+ unsubscribe(subId: number): void;
28
+ asyncIterator<T>(triggers: string | string[], options?: unknown): AsyncIterator<T>;
29
+ close(): void;
30
+ }
31
+ export {};
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.MongodbPubSub = void 0;
51
+ var pubsub_async_iterator_1 = require("./pubsub-async-iterator");
52
+ var mongopubsub_1 = require("@mawhea/mongopubsub");
53
+ var defaultCommonMessageHandler = function (message) {
54
+ console.log("MongodbPubSub.defaultCommonMessageHandler()", message);
55
+ return message;
56
+ };
57
+ var MongodbPubSub = (function () {
58
+ function MongodbPubSub(options) {
59
+ var connectionDb = options.connectionDb, channelName = options.channelName, channelOptions = options.channelOptions, connectionListener = options.connectionListener, commonMessageHandler = options.commonMessageHandler;
60
+ this.subscriptionMap = {};
61
+ this.subsRefsMap = new Map();
62
+ this.currentSubscriptionId = 0;
63
+ this.channelName = channelName;
64
+ this.commonMessageHandler = commonMessageHandler || defaultCommonMessageHandler;
65
+ this.channel = new mongopubsub_1.MubSub(__assign(__assign({ mongoDb: connectionDb }, channelOptions), { name: channelName }));
66
+ if (connectionListener) {
67
+ this.channel.on('error', function (error) {
68
+ connectionListener("error", error);
69
+ });
70
+ this.channel.on("ready", function (data) {
71
+ connectionListener("channel ready", data);
72
+ });
73
+ }
74
+ }
75
+ MongodbPubSub.prototype.publish = function (trigger, payload) {
76
+ return __awaiter(this, void 0, void 0, function () {
77
+ return __generator(this, function (_a) {
78
+ switch (_a.label) {
79
+ case 0:
80
+ console.log("MongodbPubSub publish()", { trigger: trigger, payload: payload });
81
+ return [4, this.channel.publish({ event: trigger, message: payload })];
82
+ case 1:
83
+ _a.sent();
84
+ return [2];
85
+ }
86
+ });
87
+ });
88
+ };
89
+ MongodbPubSub.prototype.subscribe = function (trigger, onMessage, options) {
90
+ var _this = this;
91
+ if (options === void 0) { options = {}; }
92
+ console.log("MongodbPubSub subscribe()", { trigger: trigger });
93
+ var triggerName = trigger;
94
+ var id = this.currentSubscriptionId++;
95
+ var callback = function (message) {
96
+ console.log("MongodbPubSub subscription callback[".concat(id, "]"), message);
97
+ onMessage(message instanceof Error
98
+ ? message
99
+ : _this.commonMessageHandler(message));
100
+ };
101
+ var subscription = this.channel.subscribe({ event: triggerName, callback: callback });
102
+ console.log("subscription[".concat(id, "]"), "trigger[".concat(triggerName, "]"));
103
+ this.subscriptionMap[id] = [triggerName, subscription];
104
+ if (!this.subsRefsMap.has(triggerName)) {
105
+ this.subsRefsMap.set(triggerName, new Set());
106
+ }
107
+ var refs = this.subsRefsMap.get(triggerName);
108
+ refs.add(id);
109
+ return Promise.resolve(id);
110
+ };
111
+ MongodbPubSub.prototype.unsubscribe = function (subId) {
112
+ console.log("MongodbPubSub.unsubscribe()", "subId[".concat(subId, "]"));
113
+ console.log("MongodbPubSub subscriptionMap", this.subscriptionMap);
114
+ var _a = this.subscriptionMap[subId] || [], _b = _a[0], triggerName = _b === void 0 ? null : _b, subscription = _a[1];
115
+ var refs = this.subsRefsMap.get(triggerName);
116
+ if (!subscription) {
117
+ throw new Error("There is no subscription of id \"".concat(subId, "\""));
118
+ }
119
+ subscription.unsubscribe(triggerName);
120
+ if (refs.size === 1) {
121
+ this.subsRefsMap.delete(triggerName);
122
+ }
123
+ else {
124
+ refs.delete(subId);
125
+ }
126
+ delete this.subscriptionMap[subId];
127
+ };
128
+ MongodbPubSub.prototype.asyncIterator = function (triggers, options) {
129
+ return new pubsub_async_iterator_1.PubSubAsyncIterator(this, triggers, options);
130
+ };
131
+ MongodbPubSub.prototype.close = function () {
132
+ this.channel.close();
133
+ };
134
+ return MongodbPubSub;
135
+ }());
136
+ exports.MongodbPubSub = MongodbPubSub;
137
+ //# sourceMappingURL=mongodb-pub-sub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongodb-pub-sub.js","sourceRoot":"","sources":["../src/mongodb-pub-sub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { PubSubEngine } from 'graphql-subscriptions';
2
+ export declare class PubSubAsyncIterator<T> implements AsyncIterableIterator<T> {
3
+ constructor(pubsub: PubSubEngine, eventNames: string | string[], options?: unknown);
4
+ next(): Promise<IteratorResult<any, any>>;
5
+ return(): Promise<{
6
+ value: unknown;
7
+ done: true;
8
+ }>;
9
+ throw(error: any): Promise<never>;
10
+ [Symbol.asyncIterator](): this;
11
+ private pullQueue;
12
+ private pushQueue;
13
+ private eventsArray;
14
+ private subscriptionIds;
15
+ private listening;
16
+ private pubsub;
17
+ private options;
18
+ private pushValue;
19
+ private pullValue;
20
+ private emptyQueue;
21
+ private subscribeAll;
22
+ private unsubscribeAll;
23
+ }
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.PubSubAsyncIterator = void 0;
40
+ var PubSubAsyncIterator = (function () {
41
+ function PubSubAsyncIterator(pubsub, eventNames, options) {
42
+ this.pubsub = pubsub;
43
+ this.options = options;
44
+ this.pullQueue = [];
45
+ this.pushQueue = [];
46
+ this.listening = true;
47
+ this.eventsArray = typeof eventNames === 'string' ? [eventNames] : eventNames;
48
+ }
49
+ PubSubAsyncIterator.prototype.next = function () {
50
+ return __awaiter(this, void 0, void 0, function () {
51
+ return __generator(this, function (_a) {
52
+ switch (_a.label) {
53
+ case 0: return [4, this.subscribeAll()];
54
+ case 1:
55
+ _a.sent();
56
+ return [2, this.listening ? this.pullValue() : this.return()];
57
+ }
58
+ });
59
+ });
60
+ };
61
+ PubSubAsyncIterator.prototype.return = function () {
62
+ return __awaiter(this, void 0, void 0, function () {
63
+ return __generator(this, function (_a) {
64
+ switch (_a.label) {
65
+ case 0: return [4, this.emptyQueue()];
66
+ case 1:
67
+ _a.sent();
68
+ return [2, { value: undefined, done: true }];
69
+ }
70
+ });
71
+ });
72
+ };
73
+ PubSubAsyncIterator.prototype.throw = function (error) {
74
+ return __awaiter(this, void 0, void 0, function () {
75
+ return __generator(this, function (_a) {
76
+ switch (_a.label) {
77
+ case 0: return [4, this.emptyQueue()];
78
+ case 1:
79
+ _a.sent();
80
+ return [2, Promise.reject(error)];
81
+ }
82
+ });
83
+ });
84
+ };
85
+ PubSubAsyncIterator.prototype[Symbol.asyncIterator] = function () {
86
+ return this;
87
+ };
88
+ PubSubAsyncIterator.prototype.pushValue = function (event) {
89
+ return __awaiter(this, void 0, void 0, function () {
90
+ return __generator(this, function (_a) {
91
+ switch (_a.label) {
92
+ case 0: return [4, this.subscribeAll()];
93
+ case 1:
94
+ _a.sent();
95
+ if (this.pullQueue.length !== 0) {
96
+ this.pullQueue.shift()({ value: event, done: false });
97
+ }
98
+ else {
99
+ this.pushQueue.push(event);
100
+ }
101
+ return [2];
102
+ }
103
+ });
104
+ });
105
+ };
106
+ PubSubAsyncIterator.prototype.pullValue = function () {
107
+ var _this = this;
108
+ return new Promise(function (resolve) {
109
+ if (_this.pushQueue.length !== 0) {
110
+ resolve({ value: _this.pushQueue.shift(), done: false });
111
+ }
112
+ else {
113
+ _this.pullQueue.push(resolve);
114
+ }
115
+ });
116
+ };
117
+ PubSubAsyncIterator.prototype.emptyQueue = function () {
118
+ return __awaiter(this, void 0, void 0, function () {
119
+ var _a;
120
+ return __generator(this, function (_b) {
121
+ switch (_b.label) {
122
+ case 0:
123
+ if (!this.listening) return [3, 3];
124
+ this.listening = false;
125
+ if (!this.subscriptionIds) return [3, 2];
126
+ _a = this.unsubscribeAll;
127
+ return [4, this.subscriptionIds];
128
+ case 1:
129
+ _a.apply(this, [_b.sent()]);
130
+ _b.label = 2;
131
+ case 2:
132
+ this.pullQueue.forEach(function (resolve) { return resolve({ value: undefined, done: true }); });
133
+ this.pullQueue.length = 0;
134
+ this.pushQueue.length = 0;
135
+ _b.label = 3;
136
+ case 3: return [2];
137
+ }
138
+ });
139
+ });
140
+ };
141
+ PubSubAsyncIterator.prototype.subscribeAll = function () {
142
+ var _this = this;
143
+ if (!this.subscriptionIds) {
144
+ this.subscriptionIds = Promise.all(this.eventsArray.map(function (eventName) { return _this.pubsub.subscribe(eventName, _this.pushValue.bind(_this), _this.options); }));
145
+ }
146
+ return this.subscriptionIds;
147
+ };
148
+ PubSubAsyncIterator.prototype.unsubscribeAll = function (subscriptionIds) {
149
+ for (var _i = 0, subscriptionIds_1 = subscriptionIds; _i < subscriptionIds_1.length; _i++) {
150
+ var subscriptionId = subscriptionIds_1[_i];
151
+ this.pubsub.unsubscribe(subscriptionId);
152
+ }
153
+ };
154
+ return PubSubAsyncIterator;
155
+ }());
156
+ exports.PubSubAsyncIterator = PubSubAsyncIterator;
157
+ //# sourceMappingURL=pubsub-async-iterator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub-async-iterator.js","sourceRoot":"","sources":["../src/pubsub-async-iterator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export type FilterFn = (rootValue?: any, args?: any, context?: any, info?: any) => boolean;
2
+ export declare const withFilter: (asyncIteratorFn: () => AsyncIterableIterator<any>, filterFn: FilterFn) => (rootValue: any, args: any, context: any, info: any) => AsyncIterator<any>;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withFilter = void 0;
4
+ var withFilter = function (asyncIteratorFn, filterFn) {
5
+ return function (rootValue, args, context, info) {
6
+ var _a;
7
+ var asyncIterator = asyncIteratorFn();
8
+ var getNextPromise = function () {
9
+ return asyncIterator
10
+ .next()
11
+ .then(function (payload) { return Promise.all([
12
+ payload,
13
+ Promise.resolve(filterFn(payload.value, args, context, info)).catch(function () { return false; }),
14
+ ]); })
15
+ .then(function (_a) {
16
+ var payload = _a[0], filterResult = _a[1];
17
+ if (filterResult === true) {
18
+ return payload;
19
+ }
20
+ return getNextPromise();
21
+ });
22
+ };
23
+ return _a = {
24
+ next: function () {
25
+ return getNextPromise();
26
+ },
27
+ return: function () {
28
+ return asyncIterator.return();
29
+ },
30
+ throw: function (error) {
31
+ return asyncIterator.throw(error);
32
+ }
33
+ },
34
+ _a[Symbol.asyncIterator] = function () {
35
+ return this;
36
+ },
37
+ _a;
38
+ };
39
+ };
40
+ exports.withFilter = withFilter;
41
+ //# sourceMappingURL=with-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-filter.js","sourceRoot":"","sources":["../src/with-filter.ts"],"names":[],"mappings":";;;AAEO,IAAM,UAAU,GAAG,UAAC,eAAiD,EAAE,QAAkB;IAC9F,OAAO,UAAC,SAAc,EAAE,IAAS,EAAE,OAAY,EAAE,IAAS;;QACxD,IAAM,aAAa,GAAG,eAAe,EAAE,CAAC;QAExC,IAAM,cAAc,GAAG;YACrB,OAAO,aAAa;iBACjB,IAAI,EAAE;iBACN,IAAI,CAAC,UAAA,OAAO,IAAI,OAAA,OAAO,CAAC,GAAG,CAAC;gBAC3B,OAAO;gBACP,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,cAAM,OAAA,KAAK,EAAL,CAAK,CAAC;aACjF,CAAC,EAHe,CAGf,CAAC;iBACF,IAAI,CAAC,UAAC,EAAuB;oBAAtB,OAAO,QAAA,EAAE,YAAY,QAAA;gBAC3B,IAAI,YAAY,KAAK,IAAI,EAAE;oBACzB,OAAO,OAAO,CAAC;iBAChB;gBAGD,OAAO,cAAc,EAAE,CAAC;YAC1B,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;QAEF,OAAO;gBACL,IAAI;oBACF,OAAO,cAAc,EAAE,CAAC;gBAC1B,CAAC;gBACD,MAAM;oBACJ,OAAO,aAAa,CAAC,MAAM,EAAE,CAAC;gBAChC,CAAC;gBACD,KAAK,YAAC,KAAK;oBACT,OAAO,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACpC,CAAC;;YACD,GAAC,MAAM,CAAC,aAAa,IAArB;gBACE,OAAO,IAAI,CAAC;YACd,CAAC;cACK,CAAC;IACX,CAAC,CAAC;AACJ,CAAC,CAAC;AApCW,QAAA,UAAU,cAoCrB"}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@ofload/graphql-mongodb-subscriptions",
3
+ "version": "1.0.0",
4
+ "description": "A graphql-subscriptions PubSub Engine using MongoDB",
5
+ "main": "dist/index.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/jingp-ofload/graphql-mongodb-subscriptions"
9
+ },
10
+ "keywords": [
11
+ "graphql",
12
+ "mongodb",
13
+ "apollo",
14
+ "subscriptions"
15
+ ],
16
+ "author": "Matthew Wheatley",
17
+ "contributors": [
18
+ {
19
+ "name": "Matthew Wheatley",
20
+ "url": "https://github.com/mjwheatley"
21
+ },
22
+ {
23
+ "name": "David Yahalomi",
24
+ "url": "https://github.com/davidyaha"
25
+ },
26
+ {
27
+ "name": "Michał Lytek",
28
+ "url": "https://github.com/19majkel94"
29
+ }
30
+ ],
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/mjwheatley/graphql-mongodb-subscriptions.git/issues"
34
+ },
35
+ "homepage": "https://github.com/mjwheatley/graphql-mongodb-subscriptions.git",
36
+ "scripts": {
37
+ "build": "tsc -p .",
38
+ "test": "npm run coverage",
39
+ "lint": "eslint src --ext ts",
40
+ "watch": "tsc-watch --noClear -p ./tsconfig.json",
41
+ "test:tests": "jest test/mongodb-pub-sub.test.ts",
42
+ "test:integration": "jest test/integration-tests.test.ts",
43
+ "coverage": "jest --coverage test/",
44
+ "prepare": "npm run build",
45
+ "prepublish": "npm run build",
46
+ "prepublishOnly": "npm run test"
47
+ },
48
+ "dependencies": {
49
+ "@mawhea/mongopubsub": "1.0.0"
50
+ },
51
+ "peerDependencies": {
52
+ "graphql-subscriptions": "^1.0.0 || ^2.0.0",
53
+ "mongodb": "^5.3.0"
54
+ },
55
+ "devDependencies": {
56
+ "@types/graphql": "^14.5.0",
57
+ "@types/jest": "^29.5.1",
58
+ "@types/node": "16.11.7",
59
+ "@typescript-eslint/eslint-plugin": "^5.36.0",
60
+ "@typescript-eslint/parser": "^5.36.0",
61
+ "eslint": "8.22.0",
62
+ "graphql": "^15.7.2",
63
+ "graphql-subscriptions": "^2.0.0",
64
+ "mongoose": "^7.1.0",
65
+ "mongopubsub": "^1.0.0",
66
+ "ts-jest": "^29.1.0",
67
+ "ts-node": "^10.9.1",
68
+ "tsc-watch": "^4.4.0",
69
+ "typescript": "^4.8.2"
70
+ },
71
+ "typings": "dist/index.d.ts",
72
+ "typescript": {
73
+ "definition": "dist/index.d.ts"
74
+ },
75
+ "files": [
76
+ "dist/"
77
+ ]
78
+ }