[
  {
    "path": ".eslintrc.json",
    "content": "{\n    \"extends\": \"airbnb-base\",\n    \"rules\": {\n        \"no-plusplus\": 0,\n        \"no-underscore-dangle\": 0,\n        \"no-unused-expressions\": 0,\n        \"import/no-extraneous-dependencies\": 0,\n        \"import/no-unresolved\": 0,\n        \"import/extensions\": 0,\n        \"import/prefer-default-export\": 0,\n        \"import/no-named-as-default\": 0\n    },\n    \"globals\": {\n        \"Meteor\": true,\n        \"Package\": true\n    },\n    \"settings\": {\n        \"import/resolver\": \"meteor\"\n    }\n}"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ntest/**\n"
  },
  {
    "path": ".meteorignore",
    "content": "test/**\ndocs\npackages\n"
  },
  {
    "path": ".npmrc",
    "content": "save-exact=true\npackage-lock=false\n"
  },
  {
    "path": ".travis.yml",
    "content": "dist: trusty\nsudo: required\nlanguage: node_js\nnode_js:\n  - \"8\"\naddons:\n  chrome: stable\ncache:\n  directories:\n    - ~/.npm\n    - \"node_modules\"\nbefore_install:\n  - \"curl https://install.meteor.com | /bin/sh\"\n  - export PATH=\"$HOME/.meteor:$PATH\"\n  - meteor --version\nbefore_script:\n  - \"export DISPLAY=:99.0\"\n  - \"sh -e /etc/init.d/xvfb start\"\n  - sleep 3 # give xvfb some time to start\nscript:\n  - npm run eslint -s\n  - npm test -s\n"
  },
  {
    "path": ".versions",
    "content": "accounts-base@2.2.2\nallow-deny@1.1.1\nbabel-compiler@7.9.0\nbabel-runtime@1.5.0\nbase64@1.0.12\nbinary-heap@1.0.11\nboilerplate-generator@1.7.1\ncallback-hook@1.4.0\ncheck@1.3.1\nddp@1.4.0\nddp-client@2.5.0\nddp-common@1.4.0\nddp-rate-limiter@1.1.0\nddp-server@2.5.0\ndiff-sequence@1.1.1\ndynamic-import@0.7.2\necmascript@0.16.2\necmascript-runtime@0.8.0\necmascript-runtime-client@0.12.1\necmascript-runtime-server@0.11.0\nejson@1.1.2\nfetch@0.1.1\ngeojson-utils@1.0.10\nid-map@1.1.1\ninter-process-messaging@0.1.1\nlocal-test:swydo:ddp-apollo@4.0.2\nlocalstorage@1.2.0\nlogging@1.3.1\nmeteor@1.10.0\nmeteortesting:browser-tests@0.1.2\nmeteortesting:mocha@0.4.4\nminimongo@1.8.0\nmodern-browsers@0.1.7\nmodules@0.18.0\nmodules-runtime@0.13.0\nmongo@1.14.6\nmongo-decimal@0.1.2\nmongo-dev-server@1.1.0\nmongo-id@1.0.8\nnpm-mongo@4.3.1\nordered-dict@1.1.0\npracticalmeteor:mocha-core@1.0.1\npromise@0.12.0\nrandom@1.2.0\nrate-limit@1.0.9\nreact-fast-refresh@0.2.3\nreactive-var@1.0.11\nreload@1.3.1\nretry@1.1.0\nroutepolicy@1.1.1\nservice-configuration@1.3.0\nsocket-stream-client@0.4.0\nswydo:ddp-apollo@4.0.2\ntracker@1.2.0\nunderscore@1.0.10\nurl@1.3.2\nwebapp@1.13.1\nwebapp-hashing@1.1.0\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## vNEXT\n\n-\n\n## 4.0.2\n- Bump package versions\n- Replace old imports using apollo-link-ddp to @swydo/apollo-link-ddp\n\n## 4.0.1\n- Import from @swydo/scoped package\n- Use named arguments for graphql methods\n- Bump apollo and graphql versions\n\n## 4.0.0\n- Upgrade to Apollo Client 3 (#392)\n- Avoid attribute errors when accessing `connection` (#368)\n- Verify that return is a function before executing in onStop\n\n## 3.0.0\n\n- Add support for Apollo Gateway [#356](https://github.com/Swydo/ddp-apollo/pull/356)\n\n## 2.2.0\n\n- Support async iterators in latest Node (and Meteor) versions [#351](https://github.com/Swydo/ddp-apollo/pull/51)\n\n## 2.1.0\n\n- Compile apollo-link-ddp browser code with babel [#300](https://github.com/Swydo/ddp-apollo/pull/300)\n\n## 2.0.1\n\n- Expose `createGraphQLPublication` on the server [#276](https://github.com/Swydo/ddp-apollo/pull/276)\n- The client module has been marked as lazy, so if using the Meteor package, `DDPLink` will only be included in the bundle when `import`ed [#277](https://github.com/Swydo/ddp-apollo/pull/277)\n- Remove `graphql` dependency from apollo-link-ddp, saving ~250kb [ca42d2cb1](https://github.com/Swydo/ddp-apollo/commit/ca42d2cb1c4a2f73755ecb542b1ee88db3b6c9ac)\n\n## 2.0.0\n\n- Move client code to stand-alone npm package [#207](https://github.com/Swydo/ddp-apollo/pull/207)\n\n## 1.4.0\n\n- Add HTTP support [#168](https://github.com/Swydo/ddp-apollo/pull/168)\n- Add a DDP retry switch [#186](https://github.com/Swydo/ddp-apollo/pull/186)\n- Allow custom DDP message observer [#198](https://github.com/Swydo/ddp-apollo/pull/198)\n- Add `ddpConnection` to the resolver context [#268](https://github.com/Swydo/ddp-apollo/pull/268)\n\n## 1.3.0\n\n- Add ability to pass a client context to the server context via `ddpContext` key.\n  ```js\n  // client\n  apolloClient.query({\n      query,\n      context: { ddpContext: { foo: 'bar' } }\n  });\n\n  // server\n  const context = (previousContext, ddpContext) => ({\n      ...previousContext,\n      foo: ddpContext.foo\n  });\n\n  setup({\n      schema,\n      context,\n  });\n  ```\n\n## 1.2.0\n\n- Remove support for Optics, because as of 2018-01-31 it's no longer operational\n\n## 1.1.0\n\n- Add `context` option to server setup\n\n## 1.0.0\n\n- Support Apollo Client 2.0 with subscriptions\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016-present Swydo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# DDP-Apollo\nDDP-Apollo leverages the power of DDP for GraphQL queries and subscriptions. Meteor developers do not need an HTTP server or extra websocket connection, because DDP offers all we need and has been well tested over time.\n\n<img width=\"830\" alt=\"Github Header\" src=\"https://user-images.githubusercontent.com/2283434/183906965-4d07a08e-81a7-4960-980d-768dcc188562.png\">\n\n- DDP-Apollo is one of the easiest ways to get GraphQL running for Meteor developers\n- Works with the Meteor accounts packages out of the box, giving a userId in your resolvers\n- Method calls and collection hooks will have `this.userId` when called within your resolvers\n- Doesn’t require an HTTP server to be setup, like with express, koa or hapi\n- Supports GraphQL Subscriptions out-of-the-box\n- Doesn’t require an extra websocket for GraphQL Subscriptions, because DDP already has a websocket\n- Already have a server setup? Use `DDPSubscriptionLink` stand-alone for just Subscriptions support. [Read more](#using-ddp-only-for-subscriptions)\n\n# Just another Apollo Link\nBecause it's \"just another Apollo Link\":\n- It works with [Apollo Dev Tools](https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm)\n- It's easy to combine with other Apollo Links\n- It's front-end agnostic\n\n# Starter Kit\nCheckout this [starter kit](https://github.com/jamiter/meteor-starter-kit) to see Meteor, Apollo, DDP and React all work together.\n\n*Note: DDP-Apollo works with all front-ends, not just React*\n\n[![Build Status](https://travis-ci.org/Swydo/ddp-apollo.svg?branch=master)](https://travis-ci.org/Swydo/ddp-apollo)\n\n## Contents\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Installation](#installation)\n- [Client setup](#client-setup)\n  - [Options](#options)\n- [Server setup](#server-setup)\n  - [Options](#options-1)\n  - [Custom context](#custom-context)\n- [GraphQL subscriptions](#graphql-subscriptions)\n  - [Setting up PubSub](#setting-up-pubsub)\n  - [Using DDP only for subscriptions](#using-ddp-only-for-subscriptions)\n- [Rate limiting GraphQL calls](#rate-limiting-graphql-calls)\n- [HTTP support](#http-support)\n  - [Installation](#installation-1)\n  - [Client setup](#client-setup-1)\n  - [Server setup](#server-setup-1)\n    - [Options](#options-2)\n- [Sponsor](#sponsor)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n```\nmeteor add swydo:ddp-apollo\n```\n\n```\nmeteor npm install --save @apollo/client @swydo/apollo-link-ddp graphql\n```\n\n## Client setup\nAll client code is in the `@swydo/apollo-link-ddp` npm package. It gives you a `DDPLink` for your Apollo Client. Creating an Apollo Client is the same as with any other Apollo Link.\n\n```javascript\n// Choose any cache implementation, but we'll use InMemoryCache as an example\nimport { ApolloClient, InMemoryCache } from '@apollo/client';\nimport { DDPLink } from '@swydo/apollo-link-ddp';\n\nexport const client = new ApolloClient ({\n  link: new DDPLink(),\n  cache: new InMemoryCache()\n});\n```\n\n### Options\n- `connection`: The DDP connection to use. Default `Meteor.connection`.\n- `method`: The name of the method. Default `__graphql`.\n- `publication`: The name of the publication. Default `__graphql-subscriptions`.\n- `ddpRetry`: Retry failed DDP method calls. Default `true`. Switch off and use [apollo-link-retry](https://www.npmjs.com/package/apollo-link-retry) for more control.\n- `socket`: Optionally pass a socket to listen to for messages. This makes it easy to integrate with non-Meteor DDP clients.\n\n```javascript\n// Pass options to the DDPLink constructor\nnew DDPLink({\n  connection: Meteor.connection\n});\n```\n\n## Server setup\nThe server will add a method and publication that will be used by the DDP Apollo Link.\n\n```javascript\nimport { schema } from './path/to/your/executable/schema';\nimport { setup } from 'meteor/swydo:ddp-apollo';\n\nsetup({\n  schema,\n  ...otherOptions\n});\n```\n\n### Options\n- `schema`: The GraphQL schema. Default `undefined`. Required when no `gateway` is provided.\n- `gateway`: An [Apollo Gateway](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-gateway). Default `undefined`. Required when no `schema` is provided.\n- `context`: A custom context. Either an object or a function returning an object. Optional.\n- `method`: The name of the method. Default `__graphql`.\n- `publication`: The name of the publication. Default `__graphql-subscriptions`.\n\n### Custom context\nTo modify or overrule the default context, you can pass a `context` object or function to the setup:\n\n```js\n// As an object:\nconst context = {\n  foo: 'bar'\n}\n\n// As a function, returning an object:\nconst context = (currentContext) => ({ ...currentContext, foo: 'bar' });\n\n// As an async function, returning a promise with an object\nconst context = async (currentContext) => ({ ...currentContext, foo: await doAsyncStuff() });\n\nsetup({\n  schema,\n  context,\n});\n```\n\n## GraphQL subscriptions\nSubscription support is baked into this package. Simply add the subscriptions to your schema and resolvers and everything works.\n\n*Note: [Apollo Gateway](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-gateway) does not yet support Subscriptions.*\n\n```graphql\n# schema.graphql\ntype Query {\n  name: String\n}\n\ntype Subscription {\n  message: String\n}\n```\n\n### Setting up PubSub\n```sh\nmeteor npm install --save graphql-subscriptions\n```\n\n```javascript\nimport { PubSub } from 'graphql-subscriptions';\n\n// The pubsub mechanism of your choice, for instance:\n// - PubSub from graphql-subscriptions (in-memory, so not recommended for production)\n// - RedisPubSub from graphql-redis-subscriptions\n// - MQTTPubSub from graphql-mqtt-subscriptions\nconst pubsub = new PubSub();\n\nexport const resolvers = {\n  Query: {\n    name: () => 'bar',\n  },\n  Subscription: {\n    message: {\n      subscribe: () => pubsub.asyncIterator('SOMETHING_CHANGED'),\n    },\n  },\n};\n\n// Later you can publish updates like this:\npubsub.publish('SOMETHING_CHANGED', { message: 'hello world' });\n```\n\nSee [graphql-subscriptions](https://github.com/apollographql/graphql-subscriptions) package for more setup details and other pubsub mechanisms. It also explains why the default `PubSub` isn't meant for production.\n\n### Using DDP only for subscriptions\nIf you already have an HTTP server setup and you are looking to support GraphQL Subscriptions in your Meteor application, you can use the `DDPSubscriptionLink` stand-alone.\n\n```javascript\nimport { ApolloClient, InMemoryCache, HttpLink, split } from '@apollo/client';\nimport { DDPSubscriptionLink, isSubscription } from '@swydo/apollo-link-ddp';\n\nconst httpLink = new HttpLink({ uri: \"/graphql\" });\nconst subscriptionLink = new DDPSubscriptionLink();\n\nconst link = split(\n  isSubscription,\n  subscriptionLink,\n  httpLink,\n);\n\nexport const client = new ApolloClient ({\n  link,\n  cache: new InMemoryCache()\n});\n```\n\n## Rate limiting GraphQL calls\nMeteor supports rate limiting for DDP calls. This means you can rate limit DDP-Apollo as well!\n\n```sh\nmeteor add ddp-rate-limiter\n```\n\n```js\nimport { DDPRateLimiter } from 'meteor/ddp-rate-limiter';\n\n// Define a rule that matches graphql method calls.\nconst graphQLMethodCalls = {\n  type: 'method',\n  name: '__graphql'\n};\n\n// Add the rule, allowing up to 5 messages every 1000 milliseconds.\nDDPRateLimiter.addRule(graphQLMethodCalls, 5, 1000);\n```\n\nSee [DDP Rate Limit documentation](https://docs.meteor.com/api/methods.html#ddpratelimiter).\n\n## HTTP support\nThere can be reasons to use HTTP instead of a Meteor method. There is support for it built in, but it requires a little different setup than the DDP version.\n\n### Installation\nWe'll need the HTTP link from Apollo and `body-parser` on top of the default dependencies:\n\n```\nmeteor npm install @apollo/client body-parser\n```\n\n### Client setup\n```js\nimport { ApolloClient, InMemoryCache } from '@apollo/client';\n// Use the MeteorLink instead of the DDPLink\n// It uses HTTP for queries and Meteor subscriptions (DDP) for GraphQL subscriptions\nimport { MeteorLink } from '@swydo/apollo-link-ddp';\n\nexport const client = new ApolloClient ({\n  link: new MeteorLink(),\n  cache: new InMemoryCache()\n});\n```\n\n### Server setup\n```js\nimport { schema } from './path/to/your/executable/schema';\nimport { setupHttpEndpoint, createGraphQLPublication } from 'meteor/swydo:ddp-apollo';\n\nsetupHttpEndpoint({\n  schema,\n  ...otherOptions,\n});\n\n// For subscription support (not required)\ncreateGraphQLPublication({ schema });\n```\n\n#### Options\n- `schema`: The GraphQL schema. Default `undefined`. Required when no `gateway` is provided.\n- `gateway`: An [Apollo Gateway](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-gateway). Default `undefined`. Required when no `schema` is provided.\n- `context`: A custom context. Either an object or a function returning an object. Optional.\n- `path`: The name of the HTTP path. Default `/graphql`.\n- `engine`: An Engine instance, in case you want monitoring on your HTTP endpoint. Optional.\n- `authMiddleware`: Middleware to get a userId and set it on the request. Default `meteorAuthMiddleware`, using a login token.\n- `jsonParser`: Custom JSON parser. Loads `body-parser` from your `node_modules` by default and uses `.json()`.\n\n## Sponsor\nWant to work with Meteor and GraphQL? [Join the team!](https://swy.do/jobs)\n"
  },
  {
    "path": "client.js",
    "content": "import { DDPLink } from '@swydo/apollo-link-ddp';\n\nexport * from '@swydo/apollo-link-ddp';\n\n// It is common for Apollo Links to export the link itself as the default\nexport default DDPLink;\n"
  },
  {
    "path": "docs/MIGRATION_GUIDE_1_0.md",
    "content": "# Migration guide to 1.0\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n**Table of Contents**\n\n- [Installation changes](#installation-changes)\n- [Client setup](#client-setup)\n  - [Previously](#previously)\n  - [Now](#now)\n- [Server setup](#server-setup)\n  - [Previously](#previously-1)\n  - [Now](#now-1)\n- [Subscriptions](#subscriptions)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation changes\nYou'll need the Apollo Link package and GrahpQL for Apollo Client 2 support:\n```\nmeteor npm install --save apollo-link graphql\n```\n\n## Client setup\n\n### Previously\n```javascript\nimport ApolloClient from 'apollo-client';\nimport { DDPNetworkInterface } from 'meteor/swydo:ddp-apollo';\n\nexport const client = new ApolloClient ({\n  networkInterface: new DDPNetworkInterface()\n});\n```\n\n### Now\n```javascript\nimport ApolloClient from 'apollo-client';\nimport { DDPLink } from 'meteor/swydo:ddp-apollo';\n// Apollo Clietn 2 requires a cache to be added as well\nimport { InMemoryCache } from 'apollo-cache-inmemory';\n\nexport const client = new ApolloClient ({\n  link: new DDPLink(),\n  cache: new InMemoryCache()\n});\n```\n\n## Server setup\n\n### Previously\n```javascript\nimport { schema } from './path/to/your/executable/schema';\nimport { setup } from 'meteor/swydo:ddp-apollo';\n\nsetup(schema, {\n  ...otherOptions\n});\n```\n\n### Now\n```javascript\nimport { schema } from './path/to/your/executable/schema';\nimport { setup } from 'meteor/swydo:ddp-apollo';\n\nsetup({\n  schema,\n  ...otherOptions\n});\n```\n\n## Subscriptions\n`SubscriptionsManager` from `graphql-subscriptions` has been deprecated. Support for Subscriptions is now build into this package. All you need to do is setup the PubSub mechanism:\n\n```javascript\nimport { PubSub } from 'graphql-subscriptions';\n\nconst pubsub = new PubSub();\n\nexport const resolvers = {\n  Query: {\n    name: () => 'bar',\n  },\n  Subscription: {\n    message: {\n      subscribe: () => pubsub.asyncIterator('SOMETHING_CHANGED'),\n    },\n  },\n};\n\n// Later you can publish updates like this:\npubsub.publish('SOMETHING_CHANGED', { message: 'hello world' });\n```"
  },
  {
    "path": "package.js",
    "content": "/* eslint-disable no-var, prefer-arrow-callback */\nvar packages = [\n  'ecmascript',\n  'promise',\n  'webapp',\n  'random',\n];\n\nPackage.describe({\n  name: 'swydo:ddp-apollo',\n  version: '4.0.2',\n  summary: 'DDP link and server for Apollo',\n  git: 'https://github.com/swydo/ddp-apollo',\n  documentation: 'README.md',\n});\n\nPackage.onUse(function use(api) {\n  api.versionsFrom('1.3.2.4');\n  api.use(packages);\n  api.mainModule('client.js', 'client', { lazy: true });\n  api.mainModule('server.js', 'server');\n});\n\nPackage.onTest(function test(api) {\n  api.use(packages);\n\n  api.use([\n    'meteortesting:mocha',\n    'accounts-base',\n  ]);\n\n  api.mainModule('specs/client.js', 'client');\n  api.mainModule('specs/server.js', 'server');\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"ddp-apollo\",\n  \"private\": true,\n  \"version\": \"1.0.0\",\n  \"description\": \"DDP integration for the Apollo Client.\",\n  \"main\": \"lib/setup.js\",\n  \"devDependencies\": {\n    \"@apollo/client\": \"3.5.10\",\n    \"@apollo/federation\": \"0.36.1\",\n    \"@apollo/gateway\": \"2.0.1\",\n    \"@swydo/apollo-link-ddp\": \"file:packages/apollo-link-ddp\",\n    \"asteroid\": \"2.0.3\",\n    \"body-parser\": \"1.20.0\",\n    \"chai\": \"4.3.6\",\n    \"doctoc\": \"2.1.0\",\n    \"eslint\": \"8.13.0\",\n    \"eslint-config-airbnb-base\": \"15.0.0\",\n    \"eslint-import-resolver-meteor\": \"0.4.0\",\n    \"eslint-plugin-import\": \"2.26.0\",\n    \"graphql\": \"16.3.0\",\n    \"graphql-subscriptions\": \"2.0.0\",\n    \"graphql-tag\": \"2.12.6\",\n    \"graphql-tools\": \"8.2.8\",\n    \"react\": \"18.0.0\",\n    \"simpleddp\": \"2.2.4\",\n    \"sinon\": \"13.0.2\"\n  },\n  \"scripts\": {\n    \"doctoc\": \"doctoc ./README.md\",\n    \"eslint\": \"eslint ./src ./specs\",\n    \"publish\": \"meteor npm install && meteor npm run eslint -s && meteor npm run doctoc -s && rm -rf ./node_modules && rm -rf ./package-lock.json && rm -rf ./test && meteor publish && cd ./packages/apollo-link-ddp && meteor npm publish\",\n    \"setup-test\": \"cd packages/apollo-link-ddp && npm install && npm build && cd ../../ && rm -rf ./test && meteor create --bare test && cd test && meteor npm i --save selenium-webdriver@3 chromedriver @babel/runtime@7.2.0\",\n    \"test\": \"meteor npm run setup-test && cd test && METEOR_PACKAGE_DIRS=../ TEST_BROWSER_DRIVER=chrome meteor test-packages --once --driver-package meteortesting:mocha ../\",\n    \"test-watch\": \"meteor npm run setup-test && cd test && TEST_WATCH=1 METEOR_PACKAGE_DIRS=../ TEST_BROWSER_DRIVER=chrome meteor test-packages --driver-package meteortesting:mocha ../\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/Swydo/ddp-apollo.git\"\n  },\n  \"author\": \"Swydo\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/Swydo/ddp-apollo/issues\"\n  },\n  \"homepage\": \"https://github.com/Swydo/ddp-apollo#readme\"\n}\n"
  },
  {
    "path": "packages/apollo-link-ddp/.babelrc",
    "content": "{\n    \"presets\": [\n      [\n        \"env\",\n        {\n          \"targets\": {\n            \"browsers\": [\">0.25%, not op_mini all\"]\n          }\n        }\n      ]\n    ],\n    \"plugins\": [\"transform-object-rest-spread\"],\n    \"comments\": false\n}\n"
  },
  {
    "path": "packages/apollo-link-ddp/.gitignore",
    "content": "node_modules\ndist\n"
  },
  {
    "path": "packages/apollo-link-ddp/.npmignore",
    "content": "node_modules\n"
  },
  {
    "path": "packages/apollo-link-ddp/.npmrc",
    "content": "save-exact=true\npackage-lock=false\n"
  },
  {
    "path": "packages/apollo-link-ddp/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016-present Swydo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/apollo-link-ddp/README.md",
    "content": "# Apollo Link using DDP\nThis is the client part of the DDP setup for Apollo. It works out of the box in a Meteor environment but is also usable with packages like `asteroid`.\n\n## Installation\n```\nmeteor npm install --save apollo-link-ddp apollo-link graphql\n```\n\n## Setup\nThis packages gives you a `DDPLink` for your Apollo Client. Creating an Apollo Client is the same as with any other Apollo Link.\n\n```javascript\nimport { ApolloClient, InMemoryCache } from '@apollo/client';\nimport { DDPLink } from '@swydo/apollo-link-ddp';\n\nexport const client = new ApolloClient ({\n  link: new DDPLink(),\n  cache: new InMemoryCache()\n});\n```\n\n### Options\n- `connection`: The DDP connection to use. Default `Meteor.connection`.\n- `method`: The name of the method. Default `__graphql`.\n- `publication`: The name of the publication. Default `__graphql-subscriptions`.\n- `ddpRetry`: Retry failed DDP method calls. Default `true`. Switch off and use [apollo-link-retry](https://www.npmjs.com/package/apollo-link-retry) for more control.\n- `socket`: Optionally pass a socket to listen to for messages. This makes it easy to integrate with non-Meteor DDP clients.\n\n```javascript\n// Pass options to the DDPLink constructor\nnew DDPLink({\n  connection: Meteor.connection\n});\n```\n\n## Setup with [Asteroid](https://github.com/mondora/asteroid)\n\n```javascript\nconst Asteroid = createClass();\nconst asteroid = new Asteroid({\n    endpoint: 'ws://localhost:3000/websocket',\n});\n\nconst link = new DDPLink({\n    connection: asteroid,\n    socket: asteroid.ddp.socket,\n    subscriptionIdKey: 'id',\n});\n```\n\n## Setup with [SimpleDDP](https://github.com/Gregivy/simpleddp)\nDDP-Apollo works with SimpleDDP version 2 and up.\n\n```javascript\nconst SimpleDDP = require('simpleddp');\n\nconst connection = new SimpleDDP({\n    endpoint: 'ws://localhost:3000/websocket',\n    SocketConstructor: global.WebSocket,\n});\n\nthis.link = new DDPLink({\n    connection: connection,\n    socket: connection.ddpConnection.socket,\n});\n```\n"
  },
  {
    "path": "packages/apollo-link-ddp/package.json",
    "content": "{\n  \"name\": \"@swydo/apollo-link-ddp\",\n  \"version\": \"4.0.2\",\n  \"description\": \"Apollo Link using DDP\",\n  \"main\": \"src/index.js\",\n  \"browser\": \"dist/index.js\",\n  \"devDependencies\": {\n    \"babel-cli\": \"6.26.0\",\n    \"babel-plugin-transform-object-rest-spread\": \"6.26.0\",\n    \"babel-preset-env\": \"1.7.0\"\n  },\n  \"peerDependencies\": {\n    \"@apollo/client\": \"^3.0.0\"\n  },\n  \"scripts\": {\n    \"build\": \"babel src --out-dir dist\",\n    \"prepublish\": \"npm run build\",\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/Swydo/ddp-apollo.git\"\n  },\n  \"keywords\": [\n    \"apollo\",\n    \"graphql\",\n    \"link\",\n    \"ddp\"\n  ],\n  \"author\": \"Swydo\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/Swydo/ddp-apollo/issues\"\n  },\n  \"homepage\": \"https://github.com/Swydo/ddp-apollo#readme\"\n}\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/client/apollo-link-ddp.js",
    "content": "const { ApolloLink, Observable, split } = require('@apollo/client/core');\nconst isSubscription = require('../common/isSubscription');\nconst {\n  DEFAULT_METHOD,\n  DEFAULT_PUBLICATION,\n  DEFAULT_CLIENT_CONTEXT_KEY,\n  DEFAULT_SUBSCRIPTION_ID_KEY,\n} = require('../common/defaults');\nconst {\n  createClientStreamObserver,\n  createSocketObserver,\n  filterGraphQLMessages,\n} = require('./listenToGraphQLMessages');\n\nfunction getDefaultMeteorConnection() {\n  try {\n    // eslint-disable-next-line global-require\n    const { Meteor } = require('meteor/meteor');\n    return Meteor.connection;\n  } catch (err) {\n    throw new Error('ddp-apollo: missing connection param');\n  }\n}\n\nfunction getClientContext(operation, key = DEFAULT_CLIENT_CONTEXT_KEY) {\n  return operation.getContext && operation.getContext()[key];\n}\n\nfunction callPromise(connection, name, args, options) {\n  return new Promise((resolve, reject) => {\n    const promise = connection.apply(name, args, options, (err, data) => {\n      err ? reject(err) : resolve(data);\n    });\n    if (promise && promise.then) { resolve(promise); }\n  });\n}\n\nclass DDPMethodLink extends ApolloLink {\n  constructor({\n    connection = getDefaultMeteorConnection(),\n    method = DEFAULT_METHOD,\n    ddpRetry = true,\n    clientContextKey,\n  } = {}) {\n    super();\n    this.connection = connection;\n    this.method = method;\n    this.clientContextKey = clientContextKey;\n    this.ddpRetry = ddpRetry;\n  }\n\n  request(operation = {}) {\n    const clientContext = getClientContext(operation, this.clientContextKey);\n    const args = [operation, clientContext];\n    const options = { noRetry: !this.ddpRetry };\n\n    return new Observable((observer) => {\n      callPromise(this.connection, this.method, args, options)\n        .then(result => observer.next(result))\n        .catch(err => observer.error(err))\n        .finally(() => observer.complete());\n\n      return () => {};\n    });\n  }\n}\n\nclass DDPSubscriptionLink extends ApolloLink {\n  constructor({\n    connection = getDefaultMeteorConnection(),\n    publication = DEFAULT_PUBLICATION,\n    subscriptionIdKey = DEFAULT_SUBSCRIPTION_ID_KEY,\n    clientContextKey,\n    socket,\n  } = {}) {\n    super();\n    this.connection = connection;\n    this.publication = publication;\n    this.clientContextKey = clientContextKey;\n    this.subscriptionIdKey = subscriptionIdKey;\n\n    this.subscriptionObservers = new Map();\n    this.ddpObserver = socket\n      ? createSocketObserver(socket)\n      : createClientStreamObserver(this.connection._stream);\n\n    this.ddpSubscription = this.ddpObserver\n      .subscribe({\n        next: filterGraphQLMessages(({\n          subscriptionId,\n          result,\n        }) => {\n          const observer = this.subscriptionObservers.get(subscriptionId);\n\n          if (observer) {\n            observer.next(result);\n          }\n        }),\n      });\n  }\n\n  request(operation = {}) {\n    const clientContext = getClientContext(operation, this.clientContextKey);\n    const subHandler = this.connection.subscribe(this.publication, operation, clientContext);\n    const subId = subHandler[this.subscriptionIdKey];\n\n    return new Observable((observer) => {\n      this.subscriptionObservers.set(subId, observer);\n\n      return () => {\n        if (subHandler.stop) {\n          subHandler.stop();\n        } else if (this.connection.unsubscribe) {\n          this.connection.unsubscribe(subId);\n        } else {\n          console.warn(`ddp-apollo: could not unsubscribe from subscription with ID ${subId}`);\n        }\n        this.subscriptionObservers.delete(subId);\n      };\n    });\n  }\n}\n\n/*\n* DDPLink combines the functionality from the method link and the subscription link\n* providing support for queries, mutations and subscriptions.\n*/\nclass DDPLink extends ApolloLink {\n  constructor(options) {\n    super();\n    this.methodLink = new DDPMethodLink(options);\n    this.subscriptionLink = new DDPSubscriptionLink(options);\n  }\n\n  request(operation = {}) {\n    return split(\n      isSubscription,\n      this.subscriptionLink,\n      this.methodLink,\n    ).request(operation);\n  }\n}\n\nfunction getDDPLink(options) {\n  return new DDPLink(options);\n}\n\nmodule.exports = {\n  getDDPLink,\n  DDPLink,\n  DDPMethodLink,\n  DDPSubscriptionLink,\n};\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/client/apollo-link-meteor-auth.js",
    "content": "const { ApolloLink } = require('@apollo/client/core');\nconst getLoginToken = require('./getLoginToken');\n\nconst meteorAuthLink = new ApolloLink((operation, forward) => {\n  operation.setContext(() => ({\n    headers: {\n      Authorization: `Bearer ${getLoginToken() || ''}`,\n    },\n  }));\n\n  return forward(operation);\n});\n\nmodule.exports = meteorAuthLink;\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/client/apollo-link-meteor.js",
    "content": "const { ApolloLink, HttpLink, split } = require('@apollo/client/core');\nconst isSubscription = require('../common/isSubscription');\nconst { DDPSubscriptionLink } = require('./apollo-link-ddp');\nconst { DEFAULT_PATH } = require('../common/defaults');\nconst meteorAuthLink = require('./apollo-link-meteor-auth');\n\nclass MeteorLink extends ApolloLink {\n  constructor(options = {}) {\n    super();\n    \n    const {\n      uri = Meteor.absoluteUrl(DEFAULT_PATH),\n      httpLink,\n      authLink = meteorAuthLink,\n    } = options;\n\n    this.meteorHttpLink = authLink.concat(httpLink || new HttpLink({ uri }));\n    this.subscriptionLink = new DDPSubscriptionLink(options);\n  }\n\n  request(operation = {}) {\n    return split(\n      isSubscription,\n      this.subscriptionLink,\n      this.meteorHttpLink,\n    ).request(operation);\n  }\n}\n\nmodule.exports = MeteorLink;\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/client/getLoginToken.js",
    "content": "/* global localStorage */\nconst LOGIN_TOKEN_KEY = 'Meteor.loginToken';\n\nfunction getLoginToken() {\n  if (typeof localStorage !== 'undefined') {\n    return localStorage.getItem(LOGIN_TOKEN_KEY);\n  }\n  return undefined;\n}\n\nmodule.exports = getLoginToken;\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/client/graphQLFetcher.js",
    "content": "/* global window, fetch */\nconst { DEFAULT_PATH } = require('../common/defaults');\nconst getLoginToken = require('./getLoginToken');\n\n/*\n* Create a graphQL fetcher for graphiQL\n*/\nfunction createGraphQLFetcher({\n  path = DEFAULT_PATH,\n} = {}) {\n  return function graphQLFetcher(graphQLParams) {\n    const headers = {\n      Authorization: `Bearer ${getLoginToken() || ''}`,\n      'Content-Type': 'application/json',\n    };\n\n    return fetch(`${window.location.origin}/${path}`, {\n      method: 'post',\n      headers,\n      body: JSON.stringify(graphQLParams),\n    }).then(response => response.json());\n  };\n}\n\nmodule.exports = createGraphQLFetcher;\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/client/listenToGraphQLMessages.js",
    "content": "const { Observable } = require('@apollo/client/core');\nconst { GRAPHQL_SUBSCRIPTION_MESSAGE_TYPE } = require('../common/defaults');\n\nfunction filterGraphQLMessages(callback) {\n  return (message) => {\n    const data = typeof message === 'string'\n      ? JSON.parse(message)\n      : message;\n\n    const {\n      type,\n      subId: subscriptionId,\n      graphqlData: result,\n    } = data;\n\n    if (\n      type === GRAPHQL_SUBSCRIPTION_MESSAGE_TYPE\n      && subscriptionId\n      && result\n    ) {\n      callback({\n        subscriptionId,\n        result,\n      });\n    }\n  };\n}\n\nfunction createClientStreamObserver(stream) {\n  return new Observable((observer) => {\n    const event = 'message';\n    const callback = message => observer.next(message);\n\n    if (stream) {\n      stream.on(event, callback);\n    }\n    return () => {\n      if (stream && stream.eventCallbacks && stream.eventCallbacks[event]) {\n        const index = stream.eventCallbacks[event].indexOf(callback);\n        if (index > -1) {\n          stream.eventCallbacks[event].splice(index, 1);\n        }\n      }\n    };\n  });\n}\n\nfunction createSocketObserver(socket) {\n  return new Observable((observer) => {\n    const event = 'message:in';\n    const listener = message => observer.next(message);\n\n    socket.on(event, listener);\n\n    return () => socket.off(event, listener);\n  });\n}\n\nmodule.exports = {\n  createClientStreamObserver,\n  createSocketObserver,\n  filterGraphQLMessages,\n};\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/common/defaults.js",
    "content": "const DEFAULT_METHOD = '__graphql';\nconst DEFAULT_PUBLICATION = '__graphql-subscriptions';\nconst DEFAULT_SUBSCRIPTION_ID_KEY = 'subscriptionId';\nconst DEFAULT_CLIENT_CONTEXT_KEY = 'ddpContext';\nconst GRAPHQL_SUBSCRIPTION_MESSAGE_TYPE = 'graphql-sub-message';\nconst DEFAULT_PATH = '/graphql';\nconst DEFAULT_CREATE_CONTEXT = context => context;\n\nconst defaults = {\n  DEFAULT_CLIENT_CONTEXT_KEY,\n  DEFAULT_CREATE_CONTEXT,\n  DEFAULT_METHOD,\n  DEFAULT_PATH,\n  DEFAULT_PUBLICATION,\n  DEFAULT_SUBSCRIPTION_ID_KEY,\n  GRAPHQL_SUBSCRIPTION_MESSAGE_TYPE,\n};\n\nmodule.exports = defaults;\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/common/isSubscription.js",
    "content": "const isSubscriptionDefinition = ({ kind, operation }) => kind === 'OperationDefinition' && operation === 'subscription';\n\nconst isSubscription = ({ query }) => query\n  && query.definitions\n  && query.definitions.some(isSubscriptionDefinition);\n\nmodule.exports = isSubscription;\n"
  },
  {
    "path": "packages/apollo-link-ddp/src/index.js",
    "content": "const defaults = require('./common/defaults');\nconst isSubscription = require('./common/isSubscription');\n\nconst {\n  getDDPLink,\n  DDPLink,\n  DDPMethodLink,\n  DDPSubscriptionLink,\n} = require('./client/apollo-link-ddp');\n\nconst MeteorLink = require('./client/apollo-link-meteor');\nconst meteorAuthLink = require('./client/apollo-link-meteor-auth');\nconst createGraphQLFetcher = require('./client/graphQLFetcher');\n\nmodule.exports = {\n  ...defaults,\n  isSubscription,\n  getDDPLink,\n  DDPLink,\n  DDPMethodLink,\n  DDPSubscriptionLink,\n  MeteorLink,\n  meteorAuthLink,\n  createGraphQLFetcher,\n};\n"
  },
  {
    "path": "server.js",
    "content": "export * from './src/setup';\n"
  },
  {
    "path": "specs/client/apollo-client.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport gql from 'graphql-tag';\nimport { Promise } from 'meteor/promise';\nimport { ApolloClient, InMemoryCache } from '@apollo/client';\nimport { getDDPLink } from '@swydo/apollo-link-ddp';\nimport { FOO_CHANGED_TOPIC } from '../data/resolvers';\n\ndescribe('ApolloClient with DDP link', function () {\n  beforeEach(function () {\n    // The ApolloClient won't recognize Promise in package tests unless exported like this\n    global.Promise = Promise;\n\n    this.link = getDDPLink();\n\n    this.client = new ApolloClient({\n      link: this.link,\n      cache: new InMemoryCache(),\n    });\n  });\n\n  afterEach(function () {\n    this.link.subscriptionLink.ddpSubscription.unsubscribe();\n  });\n\n  describe('#query', function () {\n    it('returns query data', async function () {\n      const { data } = await this.client.query({ query: gql`query { foo }` });\n\n      chai.expect(data.foo).to.be.a('string');\n    });\n\n    it('returns mutation data', async function () {\n      const { data } = await this.client.mutate({ mutation: gql`mutation { foo }` });\n\n      chai.expect(data.foo).to.be.a('string');\n    });\n\n    it('should pass and retrieve a ddp context', async function () {\n      const { data } = await this.client.query({\n        query: gql`query { ddpContextValue }`,\n        context: { ddpContext: 'ddpFoo' },\n      });\n\n      chai.expect(data.ddpContextValue).to.equal('ddpFoo');\n    });\n\n    it('handles errors', async function () {\n      try {\n        await this.client.query({\n          query: gql`query { somethingBad }`,\n        });\n        chai.expect(false, 'this should not happen').to.equal(true);\n      } catch (err) {\n        chai.expect(err.message).to.equal('SOMETHING_BAD');\n        chai.expect(err.graphQLErrors[0].message).to.equal('SOMETHING_BAD');\n      }\n    });\n  });\n\n  describe('#subscribe', function () {\n    it('returns subscription data', function (done) {\n      const message = { fooSub: 'bar' };\n      const observer = this.client.subscribe({ query: gql`subscription { fooSub }` });\n\n      const subscription = observer.subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data).to.deep.equal(message);\n            subscription.unsubscribe();\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n      });\n\n      this.link.subscriptionLink.connection.call('ddp-apollo/publish', FOO_CHANGED_TOPIC, message);\n    });\n  });\n});\n"
  },
  {
    "path": "specs/client/apollo-link-ddp.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport gql from 'graphql-tag';\nimport { ApolloLink, Observable } from '@apollo/client';\nimport {\n  DEFAULT_METHOD,\n  DEFAULT_PUBLICATION,\n  getDDPLink,\n  DDPMethodLink,\n  DDPSubscriptionLink,\n} from '@swydo/apollo-link-ddp';\nimport { loginWithUserId } from './helpers/login';\nimport { callPromise } from './helpers/callPromise';\nimport { FOO_CHANGED_TOPIC } from '../data/resolvers';\n\ndescribe('DDPMethodLink', function () {\n  beforeEach(function (done) {\n    this.link = new DDPMethodLink();\n\n    Meteor.call('ddp-apollo/setup', done);\n  });\n\n  it('should add a default method', function () {\n    chai.expect(this.link.method).to.equal(DEFAULT_METHOD);\n  });\n\n  describe('#request', function () {\n    it('should return an observer', function () {\n      const operation = {\n        query: gql`query { foo }`,\n      };\n\n      chai.expect(this.link.request(operation)).to.be.instanceof(Observable);\n    });\n\n    it('returns data', function (done) {\n      const operation = {\n        query: gql`query { foo }`,\n      };\n\n      const observer = this.link.request(operation);\n\n      observer.subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data.foo).to.be.a('string');\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n        error: done,\n      });\n    });\n  });\n\n  describe('when authenticated', function () {\n    before(async function () {\n      const userId = await callPromise('createTestUser');\n      chai.expect(userId).to.be.a('string');\n      this.userId = userId;\n      await loginWithUserId(userId);\n    });\n\n    after(function (done) {\n      Meteor.logout((err) => { err ? done(err) : done(); });\n    });\n\n    it('returns the userId', function (done) {\n      const operation = {\n        query: gql`query { userId }`,\n      };\n\n      const observer = this.link.request(operation);\n\n      observer.subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data.userId).to.be.a('string');\n            chai.expect(data.userId).to.equal(this.userId);\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n        error: done,\n      });\n    });\n\n    it('returns the meteorUserId', function (done) {\n      const operation = {\n        query: gql`query { meteorUserId }`,\n      };\n\n      const observer = this.link.request(operation);\n\n      observer.subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data.meteorUserId).to.be.a('string');\n            chai.expect(data.meteorUserId).to.equal(this.userId);\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n        error: done,\n      });\n    });\n\n    it('returns the isDDP flag', function (done) {\n      const operation = {\n        query: gql`query { isDDP }`,\n      };\n\n      const observer = this.link.request(operation);\n\n      observer.subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data.isDDP).to.be.a('boolean');\n            chai.expect(data.isDDP).to.be.true;\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n        error: done,\n      });\n    });\n  });\n\n  describe('when disconnected', function () {\n    beforeEach(function () {\n      Meteor.disconnect();\n    });\n\n    afterEach(function () {\n      // Reconnect in case the test fail before being able to do so\n      Meteor.reconnect();\n    });\n\n    it('retries automatically', function (done) {\n      const operation = {\n        query: gql`query { foo }`,\n      };\n\n      this.link.request(operation).subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data.foo).to.be.a('string');\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n        error: done,\n      });\n\n      Meteor.reconnect();\n    });\n\n    it('can be configured to prevent retrying automatically', function (done) {\n      this.link = new DDPMethodLink({ ddpRetry: false });\n\n      const operation = {\n        query: gql`query { foo }`,\n      };\n\n      this.link.request(operation).subscribe({\n        error: (err) => {\n          try {\n            chai.expect(err.message).to.contain('noRetry');\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n      });\n\n      Meteor.reconnect();\n    });\n  });\n});\n\ndescribe('DDPSubscriptionLink', function () {\n  beforeEach(function (done) {\n    this.link = new DDPSubscriptionLink();\n\n    Meteor.call('ddp-apollo/setup', done);\n  });\n\n  afterEach(function () {\n    this.link.ddpSubscription.unsubscribe();\n  });\n\n  it('should add a default publication', function () {\n    chai.expect(this.link.publication).to.equal(DEFAULT_PUBLICATION);\n  });\n\n  it('subscribes to DDP messages', function () {\n    chai.expect(this.link.ddpObserver).to.be.an('object');\n    chai.expect(this.link.ddpSubscription).to.be.an('object');\n  });\n\n  describe('#request', function () {\n    it('should return an id and data', function (done) {\n      const operation = {\n        query: gql`subscription { fooSub }`,\n      };\n      const message = { fooSub: 'bar' };\n\n      const observer = this.link.request(operation);\n\n      const subscription = observer.subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data).to.deep.equal(message);\n            subscription.unsubscribe();\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n      });\n\n      Meteor.call('ddp-apollo/publish', FOO_CHANGED_TOPIC, message);\n    });\n\n    it('should receive multiple updates', function (done) {\n      const loops = 5;\n      let count = 0;\n      const operation = {\n        query: gql`subscription { fooSub }`,\n      };\n      const value = 'bar';\n\n      const observer = this.link.request(operation);\n\n      const subscription = observer.subscribe({\n        next: () => ++count,\n      });\n\n      const promises = [];\n\n      for (let i = 0; i < loops; i += 1) {\n        promises.push(callPromise('ddp-apollo/publish', FOO_CHANGED_TOPIC, { fooSub: value }));\n      }\n\n      Promise.all(promises).then(() => {\n        Meteor.setTimeout(() => {\n          try {\n            chai.expect(count, 'number of next calls').to.equal(loops);\n            subscription.unsubscribe();\n            done();\n          } catch (e) {\n            done(e);\n          }\n        }, 100);\n      });\n    });\n\n    it('continues after a graphql error', function (done) {\n      const loops = 5;\n      let successCount = 0;\n      let errorCount = 0;\n      const operation = {\n        query: gql`subscription { fooSub }`,\n      };\n      const value = 'bar';\n\n      const observer = this.link.request(operation);\n\n      const subscription = observer.subscribe({\n        next: (data) => (data.errors ? ++errorCount : ++successCount),\n      });\n\n      const promises = [];\n\n      for (let i = 0; i < loops; i += 1) {\n        promises.push(callPromise('ddp-apollo/publish', FOO_CHANGED_TOPIC, { fooSub: i % 2 === 0 ? null : value }));\n      }\n\n      Promise.all(promises).then(() => {\n        Meteor.setTimeout(() => {\n          try {\n            chai.expect(successCount + errorCount, 'number of next calls').to.equal(loops);\n            chai.expect(successCount, 'number of succesfull loops').to.equal(2);\n            chai.expect(errorCount, 'number of errors').to.equal(loops - successCount);\n            subscription.unsubscribe();\n            done();\n          } catch (e) {\n            done(e);\n          }\n        }, 100);\n      });\n    });\n  });\n});\n\ndescribe('#getDDPLink', function () {\n  beforeEach(function () {\n    this.link = getDDPLink();\n  });\n\n  afterEach(function () {\n    this.link.subscriptionLink.ddpSubscription.unsubscribe();\n  });\n\n  it('should return an instance of ApolloLink', function () {\n    chai.expect(this.link).to.be.an.instanceOf(ApolloLink);\n  });\n});\n"
  },
  {
    "path": "specs/client/apollo-link-meteor-auth.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport gql from 'graphql-tag';\nimport { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';\nimport { DEFAULT_PATH, meteorAuthLink } from '@swydo/apollo-link-ddp';\n\ndescribe('MeteorAuthLink', function () {\n  beforeEach(function () {\n    const httpLink = new HttpLink({ uri: Meteor.absoluteUrl(DEFAULT_PATH) });\n    const cache = new InMemoryCache();\n\n    this.client = new ApolloClient({\n      link: meteorAuthLink.concat(httpLink),\n      cache,\n    });\n  });\n\n  describe('#query', function () {\n    it('should return data from the server', async function () {\n      const operation = {\n        query: gql`query { foo }`,\n      };\n\n      const { data, loading } = await this.client.query(operation);\n\n      chai.expect(data).to.deep.equal({ foo: 'bar' });\n      chai.expect(loading).to.equal(false);\n    });\n  });\n});\n"
  },
  {
    "path": "specs/client/apollo-link-meteor.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport gql from 'graphql-tag';\nimport { ApolloClient, InMemoryCache } from '@apollo/client';\nimport { MeteorLink } from '@swydo/apollo-link-ddp';\nimport { loginWithUserId } from './helpers/login';\nimport { callPromise } from './helpers/callPromise';\n\ndescribe('MeteorLink', function () {\n  beforeEach(function () {\n    this.link = new MeteorLink();\n\n    this.client = new ApolloClient({\n      link: this.link,\n      cache: new InMemoryCache(),\n    });\n\n    this.client.cache.reset();\n  });\n\n  afterEach(function () {\n    this.link.subscriptionLink.ddpSubscription.unsubscribe();\n  });\n\n  describe('#mutate', function () {\n    it('returns data from the server', async function () {\n      const operation = {\n        mutation: gql`mutation { foo }`,\n      };\n\n      const { data } = await this.client.mutate(operation);\n\n      chai.expect(data).to.deep.equal({ foo: 'fooMutated' });\n    });\n  });\n\n  describe('#query', function () {\n    it('should return data from the server', async function () {\n      const operation = {\n        query: gql`query { foo }`,\n      };\n\n      const { data, loading } = await this.client.query(operation);\n\n      chai.expect(data).to.deep.equal({ foo: 'bar' });\n      chai.expect(loading).to.equal(false);\n    });\n\n    describe('when authenticated', function () {\n      before(async function () {\n        const userId = await callPromise('createTestUser');\n        chai.expect(userId).to.be.a('string');\n        this.userId = userId;\n        await loginWithUserId(userId);\n      });\n\n      after(function (done) {\n        Meteor.logout((err) => { err ? done(err) : done(); });\n      });\n\n      it('returns the userId', async function () {\n        const operation = {\n          query: gql`query { userId }`,\n        };\n\n        const { data } = await this.client.query(operation);\n\n        chai.expect(data.userId).to.be.a('string');\n        chai.expect(data.userId).to.equal(this.userId);\n      });\n\n      it('returns the meteorUserId', async function () {\n        const operation = {\n          query: gql`query { meteorUserId }`,\n        };\n\n        const { data } = await this.client.query(operation);\n\n        chai.expect(data.meteorUserId).to.be.a('string');\n        chai.expect(data.meteorUserId).to.equal(this.userId);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "specs/client/asteroid.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport gql from 'graphql-tag';\nimport { Promise } from 'meteor/promise';\nimport { ApolloClient, InMemoryCache } from '@apollo/client';\nimport { getDDPLink } from '@swydo/apollo-link-ddp';\nimport { FOO_CHANGED_TOPIC } from '../data/resolvers';\n\ndescribe('Using Asteroid', function () {\n  beforeEach(function () {\n    // The ApolloClient won't recognize Promise in package tests unless exported like this\n    global.Promise = Promise;\n\n    // eslint-disable-next-line global-require\n    const { createClass } = require('asteroid');\n\n    const Asteroid = createClass();\n    const asteroid = new Asteroid({\n      endpoint: 'ws://localhost:3000/websocket',\n    });\n\n    this.link = getDDPLink({\n      connection: asteroid,\n      socket: asteroid.ddp.socket,\n      subscriptionIdKey: 'id',\n    });\n\n    this.client = new ApolloClient({\n      link: this.link,\n      cache: new InMemoryCache(),\n    });\n  });\n\n  afterEach(function () {\n    this.link.subscriptionLink.ddpSubscription.unsubscribe();\n  });\n\n  describe('#query', function () {\n    it('returns query data', async function () {\n      const { data } = await this.client.query({ query: gql`query { foo }` });\n\n      chai.expect(data.foo).to.be.a('string');\n    });\n  });\n\n  describe('#subscribe', function () {\n    it('returns subscription data', function (done) {\n      const message = { fooSub: 'bar' };\n      const observer = this.client.subscribe({ query: gql`subscription { fooSub }` });\n\n      const subscription = observer.subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data).to.deep.equal(message);\n            subscription.unsubscribe();\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n      });\n\n      this.link.subscriptionLink.connection.call('ddp-apollo/publish', FOO_CHANGED_TOPIC, message);\n    });\n  });\n});\n"
  },
  {
    "path": "specs/client/exports.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names, import/named */\n/* eslint-env mocha */\nimport { expect } from 'chai';\nimport DDPLink, {\n  DDPLink as namedDDPLink,\n  MeteorLink,\n  meteorAuthLink,\n  createGraphQLFetcher,\n} from '../../client';\n\ndescribe('Client exports', function () {\n  it('exports the DDPLink', function () {\n    expect(DDPLink).to.be.a('function');\n  });\n\n  it('exports a named DDPLink', function () {\n    expect(namedDDPLink).to.be.a('function');\n  });\n\n  it('exports a MeteorLink', function () {\n    expect(MeteorLink).to.be.a('function');\n  });\n\n  it('exports a meteorAuthLink', function () {\n    expect(meteorAuthLink).to.be.an('object');\n  });\n\n  it('exports a createGraphQLFetcher', function () {\n    expect(createGraphQLFetcher).to.be.a('function');\n  });\n});\n"
  },
  {
    "path": "specs/client/graphQLFetcher.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport { createGraphQLFetcher } from '@swydo/apollo-link-ddp';\nimport { callPromise } from './helpers/callPromise';\nimport { loginWithUserId } from './helpers/login';\n\ndescribe('graphQLFetcher', function () {\n  beforeEach(function () {\n    this.fetcher = createGraphQLFetcher();\n  });\n\n  it('should return data from the server', async function () {\n    const operation = {\n      query: 'query { foo }',\n    };\n\n    const { data } = await this.fetcher(operation);\n\n    chai.expect(data).to.deep.equal({ foo: 'bar' });\n  });\n\n  describe('when authenticated', function () {\n    before(async function () {\n      const userId = await callPromise('createTestUser');\n      chai.expect(userId).to.be.a('string');\n      this.userId = userId;\n      await loginWithUserId(userId);\n    });\n\n    after(function (done) {\n      Meteor.logout((err) => { err ? done(err) : done(); });\n    });\n\n    it('returns the userId', async function () {\n      const operation = {\n        query: 'query { userId, meteorUserId }',\n      };\n\n      const { data } = await this.fetcher(operation);\n\n      chai.expect(data.userId).to.equal(this.userId);\n      chai.expect(data.meteorUserId).to.equal(this.userId);\n    });\n  });\n});\n"
  },
  {
    "path": "specs/client/helpers/callPromise.js",
    "content": "export function callPromise(name, ...args) {\n  return new Promise((resolve, reject) => {\n    Meteor.apply(name, args, (err, data) => {\n      err ? reject(err) : resolve(data);\n    });\n  });\n}\n"
  },
  {
    "path": "specs/client/helpers/login.js",
    "content": "import { Accounts } from 'meteor/accounts-base';\n\nexport function loginWithUserId(userId) {\n  return new Promise((resolve, reject) => {\n    Accounts.callLoginMethod({\n      methodArguments: [{ userId }],\n      userCallback: (err) => (err ? reject(err) : resolve()),\n    });\n  });\n}\n"
  },
  {
    "path": "specs/client/simpleddp.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport gql from 'graphql-tag';\nimport { Promise } from 'meteor/promise';\nimport { ApolloClient, InMemoryCache } from '@apollo/client';\nimport { getDDPLink } from '@swydo/apollo-link-ddp';\nimport { FOO_CHANGED_TOPIC } from '../data/resolvers';\n\ndescribe('Using SimpleDDP', function () {\n  beforeEach(function () {\n    // The ApolloClient won't recognize Promise in package tests unless exported like this\n    global.Promise = Promise;\n\n    // eslint-disable-next-line global-require\n    const SimpleDDP = require('simpleddp');\n\n    const connection = new SimpleDDP({\n      endpoint: 'ws://localhost:3000/websocket',\n      SocketConstructor: global.WebSocket,\n    });\n\n    this.link = getDDPLink({\n      connection,\n      socket: connection.ddpConnection.socket,\n    });\n\n    this.client = new ApolloClient({\n      link: this.link,\n      cache: new InMemoryCache(),\n    });\n  });\n\n  afterEach(function () {\n    this.link.subscriptionLink.ddpSubscription.unsubscribe();\n  });\n\n  describe('#query', function () {\n    it('returns query data', async function () {\n      const { data } = await this.client.query({ query: gql`query { foo }` });\n\n      chai.expect(data.foo).to.be.a('string');\n    });\n  });\n\n  describe('#subscribe', function () {\n    it('returns subscription data', function (done) {\n      const message = { fooSub: 'bar' };\n      const observer = this.client.subscribe({ query: gql`subscription { fooSub }` });\n\n      const subscription = observer.subscribe({\n        next: ({ data }) => {\n          try {\n            chai.expect(data).to.deep.equal(message);\n            subscription.unsubscribe();\n            done();\n          } catch (e) {\n            done(e);\n          }\n        },\n      });\n\n      this.link.subscriptionLink.connection.call('ddp-apollo/publish', FOO_CHANGED_TOPIC, message);\n    });\n  });\n});\n"
  },
  {
    "path": "specs/client.js",
    "content": "import './client/exports';\nimport './client/apollo-link-ddp';\nimport './client/apollo-client';\nimport './client/apollo-link-meteor-auth';\nimport './client/apollo-link-meteor';\nimport './client/graphQLFetcher';\nimport './client/asteroid';\nimport './client/simpleddp';\n"
  },
  {
    "path": "specs/data/pubsub.js",
    "content": "import { PubSub } from 'graphql-subscriptions';\n\nexport const pubsub = new PubSub();\n"
  },
  {
    "path": "specs/data/resolvers.js",
    "content": "import { pubsub } from './pubsub';\n\nexport const FOO_CHANGED_TOPIC = 'foo_changed';\n\nexport const resolvers = {\n  Query: {\n    foo: () => 'bar',\n    userId: (_, __, { userId } = {}) => userId,\n    // Using Meteor.userId() yourself is not recommended. Use the context userId.\n    // But to support a lot of Meteor packages it's useful, because they use it underwater.\n    // See https://github.com/apollographql/meteor-integration/issues/92\n    meteorUserId: () => Meteor.userId(),\n    ddpContextValue: (_, __, { ddpContext } = {}) => ddpContext,\n    contextToString: (_, __, context = {}) => JSON.stringify(context),\n    isDDP: (_, __, { ddpConnection } = {}) => Boolean(ddpConnection),\n    somethingBad: () => { throw new Error('SOMETHING_BAD'); },\n  },\n  Mutation: {\n    foo: () => 'fooMutated',\n  },\n  Subscription: {\n    fooSub: {\n      subscribe: () => pubsub.asyncIterator(FOO_CHANGED_TOPIC),\n    },\n  },\n};\n"
  },
  {
    "path": "specs/data/typeDefs.js",
    "content": "import gql from 'graphql-tag';\n\nexport const typeDefs = gql`\ntype Query {\n  foo: String\n  userId: String\n  isDDP: Boolean\n  meteorUserId: String\n  ddpContextValue: String\n  contextToString: String\n  somethingBad: String\n}\n\ntype Mutation {\n  foo: String\n}\n\ntype Subscription {\n  fooSub: String!\n}\n`;\n"
  },
  {
    "path": "specs/server/exports.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names, import/named */\n/* eslint-env mocha */\nimport { expect } from 'chai';\nimport {\n  setup,\n  createGraphQLPublication,\n  setupHttpEndpoint,\n} from '../../server';\n\ndescribe('Server exports', function () {\n  it('exports the setup function', function () {\n    expect(setup).to.be.a('function');\n  });\n\n  it('exports the createGraphQLPublication function', function () {\n    expect(createGraphQLPublication).to.be.a('function');\n  });\n\n  it('exports the setupHttpEndpoint function', function () {\n    expect(setupHttpEndpoint).to.be.a('function');\n  });\n});\n"
  },
  {
    "path": "specs/server/getUserIdByLoginToken.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport { getUserIdByLoginToken, NO_VALID_USER_ERROR } from '../../src/getUserIdByLoginToken';\n\ndescribe('getUserIdByLoginToken', function () {\n  it('throws error for bad tokens', async function () {\n    try {\n      await getUserIdByLoginToken('foo');\n      chai.expect(false, 'this should not be touched').to.equal(true);\n    } catch (err) {\n      chai.expect(err).to.equal(NO_VALID_USER_ERROR);\n    }\n  });\n});\n"
  },
  {
    "path": "specs/server/helpers/setupLoginByUserId.js",
    "content": "import { Accounts } from 'meteor/accounts-base';\n\nMeteor.methods({\n  createTestUser() {\n    return Meteor.users.insert({});\n  },\n});\n\nAccounts.registerLoginHandler('testLogin', (request) => {\n  if (!(typeof request.userId === 'string')) {\n    return undefined;\n  }\n  const user = Meteor.users.findOne(request.userId);\n\n  if (!user) {\n    return { error: new Meteor.Error('USER_NOT_FOUND') };\n  }\n  return { userId: user._id };\n});\n"
  },
  {
    "path": "specs/server/helpers.js",
    "content": "import { makeExecutableSchema } from 'graphql-tools';\nimport {\n  DEFAULT_METHOD,\n  DEFAULT_PUBLICATION,\n} from '@swydo/apollo-link-ddp';\nimport { pubsub } from '../data/pubsub';\nimport { setup, setupHttpEndpoint } from '../../src/setup';\n\nimport { typeDefs } from '../data/typeDefs';\nimport { resolvers } from '../data/resolvers';\n\nexport function reset() {\n  delete Meteor.server.publish_handlers[DEFAULT_PUBLICATION];\n  delete Meteor.server.method_handlers[DEFAULT_METHOD];\n}\n\nMeteor.methods({\n  'ddp-apollo/setup': async function setupDdpApollo() {\n    reset();\n\n    const schema = makeExecutableSchema({\n      resolvers,\n      typeDefs,\n    });\n\n    // Add the client context to the previous context for testing\n    const context = (previousContext, clientContext) => ({\n      ...previousContext,\n      ddpContext: clientContext,\n    });\n\n    await setup({\n      schema,\n      context,\n    });\n\n    await setupHttpEndpoint({\n      schema,\n      context,\n    });\n  },\n\n  'ddp-apollo/publish': function publish(topic, data) {\n    pubsub.publish(topic, data);\n  },\n});\n"
  },
  {
    "path": "specs/server/server.js",
    "content": "import '../../server';\n"
  },
  {
    "path": "specs/server/setup.js",
    "content": "/* eslint-disable prefer-arrow-callback, func-names */\n/* eslint-env mocha */\nimport chai from 'chai';\nimport { makeExecutableSchema } from 'graphql-tools';\nimport gql from 'graphql-tag';\nimport { ApolloGateway, LocalGraphQLDataSource } from '@apollo/gateway';\nimport { buildFederatedSchema } from '@apollo/federation';\nimport { DEFAULT_METHOD } from '@swydo/apollo-link-ddp';\n\nimport { setup } from '../../src/setup';\nimport { DDP_APOLLO_SCHEMA_REQUIRED } from '../../src/initSchema';\nimport { createGraphQLMethod } from '../../src/createGraphQLMethod';\n\nimport { typeDefs } from '../data/typeDefs';\nimport { resolvers } from '../data/resolvers';\nimport { reset } from './helpers';\nimport { callPromise } from '../client/helpers/callPromise';\n\nasync function callMethod(...args) {\n  return callPromise.apply(this, [DEFAULT_METHOD, ...args]);\n}\n\ndescribe('#setup', function () {\n  beforeEach(function () {\n    reset();\n  });\n\n  it('requires a schema', async function () {\n    try {\n      await setup();\n      throw new Error('Setup without schema should fail!');\n    } catch (e) {\n      chai.expect(e.message).to.equal(DDP_APOLLO_SCHEMA_REQUIRED);\n    }\n  });\n\n  describe('method', function () {\n    beforeEach(async function () {\n      const schema = makeExecutableSchema({\n        resolvers,\n        typeDefs,\n      });\n\n      await setup({ schema });\n    });\n\n    it('should add a method', function (done) {\n      Meteor.call(DEFAULT_METHOD, done);\n    });\n\n    it('should return data', async function () {\n      const request = {\n        query: gql`{ foo }`,\n      };\n\n      const { data } = await callMethod(request);\n\n      chai.expect(data.foo).to.equal('bar');\n    });\n  });\n\n  describe('context', function () {\n    it('accepts an object', async function () {\n      const schema = makeExecutableSchema({\n        resolvers: {\n          Query: {\n            foo: (_, __, { foo, bar }) => [foo, bar].join(':'),\n          },\n        },\n        typeDefs,\n      });\n\n      const context = {\n        foo: 'baz',\n        bar: 'qux',\n      };\n\n      const request = {\n        query: gql`{ foo }`,\n      };\n\n      await setup({ schema, context });\n\n      const { data } = await callMethod(request);\n\n      chai.expect(data.foo).to.equal('baz:qux');\n    });\n\n    it('accepts a function', async function () {\n      const schema = makeExecutableSchema({\n        resolvers: {\n          Query: {\n            foo: (_, __, { foo, bar }) => [foo, bar].join(':'),\n          },\n        },\n        typeDefs,\n      });\n\n      const context = () => ({\n        foo: 'baz',\n        bar: 'qux',\n      });\n\n      const request = {\n        query: gql`{ foo }`,\n      };\n\n      await setup({ schema, context });\n\n      const { data } = await callMethod(request);\n\n      chai.expect(data.foo).to.equal('baz:qux');\n    });\n\n    it('accepts an async function', async function () {\n      const schema = makeExecutableSchema({\n        resolvers: {\n          Query: {\n            foo: (_, __, { foo, bar }) => [foo, bar].join(':'),\n          },\n        },\n        typeDefs,\n      });\n\n      const getQux = async () => 'qux';\n\n      const context = async () => ({\n        foo: 'baz',\n        bar: await getQux(),\n      });\n\n      const request = {\n        query: gql`{ foo }`,\n      };\n\n      await setup({ schema, context });\n\n      const { data } = await callMethod(request);\n\n      chai.expect(data.foo).to.equal('baz:qux');\n    });\n\n    it('leaves the original values alone', async function (done) {\n      const schema = makeExecutableSchema({\n        resolvers: {\n          Query: {\n            foo: (_, __, context) => {\n              chai.expect(Object.getOwnPropertyNames(context)).to.include('userId');\n              chai.expect(Object.getOwnPropertyNames(context)).to.include('foo');\n              done();\n            },\n          },\n        },\n        typeDefs,\n      });\n\n      const context = { foo: 'baz' };\n\n      const request = { query: gql`{ foo }` };\n\n      await setup({ schema, context });\n\n      await callMethod(request);\n    });\n  });\n\n  describe('createContext', function () {\n    it('is called with the current context', function (done) {\n      const request = { query: gql`{ foo }` };\n\n      const schema = makeExecutableSchema({\n        resolvers,\n        typeDefs,\n      });\n\n      function context(currentContext) {\n        chai.expect(Object.getOwnPropertyNames(currentContext)).to.include('userId');\n        done();\n      }\n\n      createGraphQLMethod({ schema, context })(request).catch(done);\n    });\n\n    it('returns a modified context', async function () {\n      const request = { query: gql`{ foo }` };\n\n      const schema = makeExecutableSchema({\n        resolvers: {\n          Query: {\n            foo: (_, __, { foo, bar }) => [foo, bar].join(':'),\n          },\n        },\n        typeDefs,\n      });\n\n      const context = () => ({ foo: 'baz', bar: 'qux' });\n\n      const { data } = await createGraphQLMethod({ schema, context })(request);\n\n      chai.expect(data.foo).to.equal('baz:qux');\n    });\n\n    it('accepts a ddp context param', async function () {\n      const request = { query: gql`{ foo }` };\n\n      const schema = makeExecutableSchema({\n        resolvers: {\n          Query: { foo: (_, __, { foo }) => foo },\n        },\n        typeDefs,\n      });\n\n      const context = (_, clientContext) => clientContext;\n\n      const { data } = await createGraphQLMethod({ schema, context })(request, { foo: 'bar' });\n\n      chai.expect(data.foo).to.equal('bar');\n    });\n  });\n\n  describe('gateway', function () {\n    beforeEach(async function () {\n      const {\n        Subscription,\n        ...resolversWithoutSubscriptions\n      } = resolvers;\n\n      const typeDefsWithoutSubscriptions = {\n        ...typeDefs,\n        definitions: typeDefs.definitions.filter((def) => def.name.value !== 'Subscription'),\n      };\n\n      const schema = buildFederatedSchema([{\n        resolvers: resolversWithoutSubscriptions,\n        typeDefs: typeDefsWithoutSubscriptions,\n      }]);\n\n      const gateway = new ApolloGateway({\n        serviceList: [{ name: 'local', url: 'foo' }],\n        buildService: () => new LocalGraphQLDataSource(schema),\n      });\n\n      await setup({ gateway });\n    });\n\n    it('returns data via method', async function () {\n      const request = {\n        query: gql`{ foo }`,\n      };\n\n      const { data } = await callMethod(request);\n\n      chai.expect(data.foo).to.equal('bar');\n    });\n\n    it('supports context with userId', async function () {\n      const request = {\n        query: gql`{ contextToString }`,\n      };\n\n      const { data } = await callMethod(request);\n\n      chai.expect(data.contextToString).to.be.ok;\n      chai.expect(JSON.parse(data.contextToString)).to.have.property('userId');\n    });\n  });\n});\n"
  },
  {
    "path": "specs/server.js",
    "content": "// helpers\nimport './server/helpers';\nimport './server/helpers/setupLoginByUserId';\n\n// specs\nimport './server/exports';\nimport './server/server';\nimport './server/setup';\nimport './server/getUserIdByLoginToken';\n"
  },
  {
    "path": "src/contextToFunction.js",
    "content": "import { DEFAULT_CREATE_CONTEXT } from '@swydo/apollo-link-ddp';\n\nexport function contextToFunction(context) {\n  switch (typeof context) {\n    case 'object':\n      return (defaultContext) => ({ ...defaultContext, ...context });\n    case 'function':\n      return context;\n    default:\n      return DEFAULT_CREATE_CONTEXT;\n  }\n}\n"
  },
  {
    "path": "src/createExecutor.js",
    "content": "import { execute } from 'graphql';\n\nexport function createExecutor(gatewayExecutor) {\n  return function executor({\n    schema,\n    query,\n    context,\n    operationName,\n    variables,\n  }) {\n    if (gatewayExecutor) {\n      return gatewayExecutor({\n        document: query,\n        operationName,\n        context,\n        request: {\n          query,\n          operationName,\n          variables,\n        },\n      });\n    }\n\n    return execute({\n      schema,\n      document: query,\n      rootValue: {},\n      contextValue: context,\n      variableValues: variables,\n      operationName,\n    });\n  };\n}\n"
  },
  {
    "path": "src/createGraphQLMethod.js",
    "content": "import { createExecutor } from './createExecutor';\nimport { contextToFunction } from './contextToFunction';\n\nexport const DDP_APOLLO_SCHEMA_REQUIRED = 'DDP_APOLLO_SCHEMA_REQUIRED';\n\nexport function createGraphQLMethod({\n  schema,\n  context,\n  execute = createExecutor(),\n}) {\n  if (!schema) {\n    throw new Error(DDP_APOLLO_SCHEMA_REQUIRED);\n  }\n\n  const createContext = contextToFunction(context);\n\n  // eslint-disable-next-line default-param-last\n  return async function graphQlMethod({ query, variables, operationName } = {}, clientContext) {\n    if (!query) {\n      return {};\n    }\n\n    if (this.unblock) {\n      this.unblock();\n    }\n\n    const { userId, connection: ddpConnection } = this;\n    const completeContext = await createContext({ userId, ddpConnection }, clientContext);\n\n    return execute({\n      schema,\n      query,\n      context: completeContext,\n      variables,\n      operationName,\n    });\n  };\n}\n"
  },
  {
    "path": "src/createGraphQLMiddleware.js",
    "content": "import { parse } from 'graphql';\nimport { createExecutor } from './createExecutor';\nimport { contextToFunction } from './contextToFunction';\nimport { invokeDDP } from './invokeDDP';\n\nexport function createGraphQLMiddleware({\n  schema,\n  context,\n  execute = createExecutor(),\n}) {\n  const createContext = contextToFunction(context);\n\n  return async function handleRequest(req, res) {\n    const {\n      query,\n      variables,\n      operationName,\n    } = req.body;\n\n    const data = await invokeDDP(async () => execute({\n      schema,\n      query: parse(query),\n      context: await createContext({ userId: req.userId }),\n      operationName,\n      variables,\n    }), { userId: req.userId });\n\n    const json = JSON.stringify(data);\n\n    res.setHeader('Content-Type', 'application/json');\n    res.setHeader('Content-Length', Buffer.byteLength(json, 'utf8'));\n    res.write(json);\n    res.end();\n  };\n}\n"
  },
  {
    "path": "src/createGraphQLPublication.js",
    "content": "import { Meteor } from 'meteor/meteor';\nimport {\n  DEFAULT_PUBLICATION,\n  GRAPHQL_SUBSCRIPTION_MESSAGE_TYPE,\n} from '@swydo/apollo-link-ddp';\nimport { subscribe } from 'graphql';\nimport forAwaitEach from './forAwaitEach';\nimport { contextToFunction } from './contextToFunction';\n\n// The 'pong' message is the only messages that is ignored by the client-side DDP parser\nconst SUBSCRIPTION_MESSAGE_TYPE = 'pong';\nconst { warn } = console;\n\nexport function createGraphQLPublication({\n  schema,\n  context,\n  publication = DEFAULT_PUBLICATION,\n} = {}) {\n  if (!subscribe) {\n    warn('DDP-Apollo: You need graphl@0.11 or higher for subscription support');\n    return;\n  }\n\n  const createContext = contextToFunction(context);\n\n  // eslint-disable-next-line default-param-last\n  Meteor.publish(publication, function publishGraphQL({\n    query,\n    variables,\n    operationName,\n  } = {}, clientContext) {\n    const {\n      userId,\n      _subscriptionId: subId,\n      _session: session,\n      connection: ddpConnection,\n    } = this;\n    if (!query) {\n      this.stop();\n      return;\n    }\n\n    Promise.resolve()\n      .then(() => createContext({ userId, ddpConnection }, clientContext))\n      .then((completeContext) => subscribe({\n        schema,\n        document: query,\n        rootValue: {},\n        contextValue: completeContext,\n        variableValues: variables,\n        operationName,\n      }))\n      .then((iterator) => {\n        this.ready();\n\n        // When the Meteor subscriptions stops we should break out of the iterator\n        this.onStop(() => iterator.return && iterator.return());\n\n        return forAwaitEach(iterator, (graphqlData) => {\n          session.socket.send(JSON.stringify({\n            msg: SUBSCRIPTION_MESSAGE_TYPE,\n            type: GRAPHQL_SUBSCRIPTION_MESSAGE_TYPE,\n            subId,\n            graphqlData,\n          }));\n        });\n      })\n      // When the GraphQL subscriptions stops we should stop the Meteor subscription\n      .then(() => this.stop())\n      .catch((err) => this.error(err));\n  });\n}\n"
  },
  {
    "path": "src/forAwaitEach.js",
    "content": "function createAsyncIterator(source) {\n  const method = source && (source[Symbol.asyncIterator] || source['@@asyncIterator']);\n  if (typeof method === 'function') {\n    return method.call(source);\n  }\n  return null;\n}\n\nexport default function forAwaitEach(source, callback, thisArg) {\n  const asyncIterator = createAsyncIterator(source);\n  if (asyncIterator) {\n    let i = 0;\n    return new Promise((resolve, reject) => {\n      function next() {\n        asyncIterator\n          .next()\n          .then((step) => {\n            if (!step.done) {\n              Promise.resolve(callback.call(thisArg, step.value, i++, source))\n                .then(next)\n                .catch(reject);\n            } else {\n              resolve();\n            }\n          })\n          .catch(reject);\n      }\n      next();\n    });\n  }\n\n  return Promise.resolve();\n}\n"
  },
  {
    "path": "src/getUserIdByLoginToken.js",
    "content": "const USER_TOKEN_PATH = 'services.resume.loginTokens.hashedToken';\nexport const NO_VALID_USER_ERROR = new Error('NO_VALID_USER');\n\nexport async function getUserIdByLoginToken(loginToken) {\n  // `accounts-base` is a weak dependency, so we'll try to require it\n  // eslint-disable-next-line global-require, prefer-destructuring\n  const { Accounts } = require('meteor/accounts-base');\n\n  if (!loginToken) { throw NO_VALID_USER_ERROR; }\n\n  if (typeof loginToken !== 'string') {\n    throw new Error(\"GraphQL login token isn't a string\");\n  }\n\n  // the hashed token is the key to find the possible current user in the db\n  const hashedToken = Accounts._hashLoginToken(loginToken);\n\n  // get the possible user from the database with minimal fields\n  const fields = { _id: 1, 'services.resume': 1 };\n  const user = await Meteor.users.rawCollection()\n    .findOne({ [USER_TOKEN_PATH]: hashedToken }, { fields });\n\n  if (!user) { throw NO_VALID_USER_ERROR; }\n\n  // find the corresponding token: the user may have several open sessions on different clients\n  const currentToken = user.services.resume.loginTokens\n    .find((token) => token.hashedToken === hashedToken);\n\n  const tokenExpiresAt = Accounts._tokenExpiration(currentToken.when);\n  const isTokenExpired = tokenExpiresAt < new Date();\n\n  if (isTokenExpired) { throw NO_VALID_USER_ERROR; }\n\n  return user._id;\n}\n"
  },
  {
    "path": "src/initSchema.js",
    "content": "export const DDP_APOLLO_SCHEMA_REQUIRED = 'DDP_APOLLO_SCHEMA_REQUIRED';\nexport const DDP_APOLLO_SCHEMA_AND_GATEWAY = 'DDP_APOLLO_CANNOT_COMBINE_SCHEMA_AND_GATEWAY';\n\nexport async function initSchema({\n  schema,\n  gateway,\n}) {\n  if (!schema && !gateway) {\n    throw new Error(DDP_APOLLO_SCHEMA_REQUIRED);\n  }\n\n  if (schema && gateway) {\n    throw new Error(DDP_APOLLO_SCHEMA_AND_GATEWAY);\n  }\n\n  if (gateway) {\n    return gateway.load();\n  }\n\n  return { schema };\n}\n"
  },
  {
    "path": "src/invokeDDP.js",
    "content": "import { DDP } from 'meteor/ddp';\nimport { Random } from 'meteor/random';\n\nconst { DDPCommon } = Package['ddp-common'];\n\n// For details, please see https://github.com/meteor/meteor/issues/6388\n\nfunction createInvocation(options) {\n  return new DDPCommon.MethodInvocation({\n    isSimulation: false,\n    setUserId: () => {},\n    unblock: () => {},\n    connection: {},\n    randomSeed: Random.id(),\n    ...options,\n  });\n}\n\nexport function invokeDDP(func, options) {\n  const invocation = createInvocation(options);\n\n  return DDP._CurrentMethodInvocation.withValue(invocation, func);\n}\n"
  },
  {
    "path": "src/meteorAuthMiddleware.js",
    "content": "import { getUserIdByLoginToken, NO_VALID_USER_ERROR } from './getUserIdByLoginToken';\n\nexport function meteorAuthMiddleware(req, res, next) {\n  let tokenType;\n  let loginToken;\n\n  // get the login token from the request headers, given by meteorAuthLink\n  if (req.headers && req.headers.authorization) {\n    const parts = req.headers.authorization.split(' ');\n    [tokenType, loginToken] = parts;\n\n    if (parts.length !== 2 || tokenType !== 'Bearer') {\n      // Not a valid login token, so unset\n      loginToken = undefined;\n    }\n  }\n\n  // get the user for the context\n  getUserIdByLoginToken(loginToken)\n    .then((userId) => {\n      req.userId = userId;\n      next();\n    })\n    .catch((err) => {\n      err === NO_VALID_USER_ERROR ? next() : next(err);\n    });\n}\n"
  },
  {
    "path": "src/setup.js",
    "content": "import { Meteor } from 'meteor/meteor';\nimport {\n  DEFAULT_METHOD,\n} from '@swydo/apollo-link-ddp';\nimport { initSchema } from './initSchema';\nimport { createExecutor } from './createExecutor';\nimport { createGraphQLMethod } from './createGraphQLMethod';\nimport { createGraphQLPublication } from './createGraphQLPublication';\nimport { setupHttpEndpoint } from './setupHttpEndpoint';\n\nexport async function setup({\n  schema,\n  gateway,\n  method = DEFAULT_METHOD,\n  publication,\n  context,\n} = {}) {\n  const {\n    schema: initializedSchema,\n    executor: gatewayExecutor,\n  } = await initSchema({\n    schema,\n    gateway,\n  });\n\n  Meteor.methods({\n    [method]: createGraphQLMethod({\n      schema: initializedSchema,\n      execute: createExecutor(gatewayExecutor),\n      context,\n    }),\n  });\n\n  if (!gateway) {\n    createGraphQLPublication({\n      schema: initializedSchema,\n      publication,\n      context,\n    });\n  }\n}\n\nexport {\n  createGraphQLPublication,\n  setupHttpEndpoint,\n};\n"
  },
  {
    "path": "src/setupHttpEndpoint.js",
    "content": "import { WebApp } from 'meteor/webapp';\nimport {\n  DEFAULT_PATH,\n} from '@swydo/apollo-link-ddp';\nimport { initSchema } from './initSchema';\nimport { createExecutor } from './createExecutor';\nimport { createGraphQLPublication } from './createGraphQLPublication';\nimport { createGraphQLMiddleware } from './createGraphQLMiddleware';\nimport { meteorAuthMiddleware } from './meteorAuthMiddleware';\n\nexport async function setupHttpEndpoint({\n  schema,\n  gateway,\n  path = DEFAULT_PATH,\n  context,\n  engine,\n  jsonParser,\n  authMiddleware = meteorAuthMiddleware,\n} = {}) {\n  const {\n    schema: initializedSchema,\n    executor: gatewayExecutor,\n  } = await initSchema({\n    schema,\n    gateway,\n  });\n\n  const graphQLMiddleware = createGraphQLMiddleware({\n    schema: initializedSchema,\n    context,\n    execute: createExecutor(gatewayExecutor),\n  });\n\n  if (engine && engine.expressMiddleware) {\n    WebApp.connectHandlers.use(engine.expressMiddleware());\n  }\n\n  if (!jsonParser) {\n    // Only require the body-parser for users who actually use the http version\n    // eslint-disable-next-line global-require\n    const bodyParser = require('body-parser');\n    // eslint-disable-next-line no-param-reassign\n    jsonParser = bodyParser.json();\n  }\n\n  WebApp.connectHandlers.use(path, jsonParser);\n\n  if (authMiddleware) {\n    WebApp.connectHandlers.use(path, authMiddleware);\n  }\n\n  WebApp.connectHandlers.use(path, (req, res, next) => graphQLMiddleware(req, res).catch(next));\n}\n\nexport {\n  createGraphQLPublication,\n};\n"
  }
]