[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\njobs:\n  build:\n    docker:\n      - image: cimg/node:14.15.1\n\n    steps:\n      - checkout\n\n      - restore_cache:\n          name: Restore yarn package cache\n          keys:\n            - yarn-packages-{{ .Branch }}-{{ checksum \"yarn.lock\" }}\n            - yarn-packages-{{ .Branch }}\n            - yarn-packages-master\n            - yarn-packages-\n\n      - run:\n          name: Install dependencies and build packages\n          command: yarn\n\n      - save_cache:\n          name: Save yarn package cache\n          key: yarn-packages-{{ .Branch }}-{{ checksum \"yarn.lock\" }}\n          paths:\n            - node_modules/\n\n      - run:\n          name: Run tests\n          command: yarn test:coverage\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Typescript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# next.js build output\n.next\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Typescript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# next.js build output\n.next\n\n# vscode\n.vscode\n\n# output directories\ndist/\n\n# Typescript\n.rpt2_cache\n\n# temp folder\ntemp\n\n# mac\n\n.DS_store\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at albernazmiguel@gmail.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## Not sure where to start?\n\nIf you're not sure where to start? You'll probably want to learn a bit about a few topics before getting dirt in your hands.\n\n- [ASTs](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (Abstract Syntax Tree): this project makes heavy use of code transformation with Babel and GraphQL. Check out [AST Explorer](http://astexplorer.net/) to learn more about ASTs interactively\n- [Babel](https://github.com/babel/babel): I'd recommend a read to the [the Babel Plugin Handbook](https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md#babel-plugin-handbook) to understand how a plugin is written.\n- [GraphQL](https://graphql.org/graphql-js/graphql): the GraphQL.js module is not only meant to build servers, it also exports a core subset of GraphQL functionality for creation of GraphQL type systems.\n- [Lerna](https://github.com/lerna/lerna): this is mono repository and we use Lerna to manage our packages.\n- [Yarn workspaces](https://yarnpkg.com/lang/en/docs/workspaces/): Lerna is setup to be used with Yarn workspaces.\n\n## Chat\n\nHave read this contributing guide and still need some help? Feel free join our [slack channel](https://grafoo-slack.herokuapp.com).\n\n## Disclaimer\n\n**As Lerna is configured in this package to be used with Yarn, not using NPM will save you a lot of time.**\n\n## Setup\n\n```sh\n$ git clone https://github.com/grafoojs/grafoo\n$ cd grafoo\n$ yarn # this command will install dependencies and automatically build every package\n```\n\n## Build packages\n\n#### Build all packages\n\nAs mentioned above after every `yarn` install all the packages are built automatically. But if you want to build then anyway just run:\n\n```sh\n$ yarn prepare\n```\n\n#### Build single package\n\n```sh\n$ cd packages/[any-package]\n$ yarn build\n```\n\n## Run tests\n\n#### All tests\n\n```sh\n$ yarn test\n```\n\n#### All tests with coverage\n\nI recomend the usage of [NPX](https://www.npmjs.com/package/npx) for any Lerna command if you don't want to install it globally.\n\n```sh\n$ npx lerna run test:coverage\n```\n\n#### Test individual package\n\n```sh\n$ cd packages/[any-package]\n$ yarn test\n```\n\n#### Test individual package in watch mode\n\n```sh\n$ cd packages/[any-package]\n$ yarn test --watch\n```\n\n#### Test individual package with coverage\n\n```sh\n$ cd packages/[any-package]\n$ yarn test:coverage\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Miguel Albernaz <albernazmiguel@gmail.com>\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": "changelog.md",
    "content": "# CHANGELOG\n\n## 1.4.0\n\n### Features\n\n- [babel-plugin, core] adds an option to babel-plugin to generate an id side by side with the query in Grafoo Object. This feature will enable persistent queries in the future.\n\n### Contributors:\n\n- @[adjourn](/adjourn)\n\n## 1.3.0\n\n### Features\n\n- [core] add reset method to client to clear the cache.\n\n### Fixes\n\n- [babel-plugin] Correctly identify variables in arguments\n- [babel-plugin] Don't throw error when encountering a Union node\n- [babel-plugin] Prevent multiple instances of idFields to be added to the same node in a query\n\n### Contributors:\n\n- @[mogelbrod](/mogelbrod)\n\n## 1.2.0\n\n### Features\n\n- [babel-plugin] allow schema to be omited on config\n\n### Fixes\n\n- [babel-plugin] fix client factory throwing when called with a variable as the second argument\n\n## 1.1.1\n\n### Fixes\n\n- [preact] fix variables not being updated\n\n## 1.1.0\n\n### Features\n\n- [bindings] enable load method to receive and update variables\n\n## 1.0.9\n\n### Fixes\n\n- [core] fix bug preventing write to work when graphql payload wasn't destructured beforehand\n\n## 1.0.8\n\n### Fixes\n\n- [bindings] for safety reusing `props` argument to avoid declaration of new variables\n\n## 1.0.7\n\n### Fixes\n\n- [react, preact] reload when variables change\n\n## 1.0.6\n\n### Fixes\n\n- [preact] correctly type children prop\n\n## v0.0.1-beta.15\n\n### Fixes\n\n- [core] fix latest bug that afected also `client.read`\n\n## v0.0.1-beta.14\n\n### Fixes\n\n- [core] allow queries to be partially cached on `client.write`\n\n## v0.0.1-beta.13\n\n### Features\n\n- [preact, react] reload query on skip `Consumer` prop change to true\n\n## v0.0.1-beta.12\n\n### Fixes\n\n- [preact] fix preact not rendering on nested componenents when data have been already received\n\n## v0.0.1-beta.11\n\n### Features\n\n- [core] return a `partial` property in read to flag if a query result is only partially cached\n\n### Fixes\n\n- [bindings] fix bindings returning `loaded` equals to true if a query is only partially cached\n\n## v0.0.1-beta.10\n\n### Features\n\n- [bindings, react, preact] revert latest release reintroducing `loaded` prop from bindings\n\n## v0.0.1-beta.9\n\n### Features\n\n- [bindings, react, preact] remove `loaded` prop from bindings\n\n## v0.0.1-beta.8\n\n### Features\n\n- [core] transport logic has been removed from core. This is a breaking change and here is the fix:\n\n```diff\n  import createClient from \"@grafoo/core\";\n\n+ function fetchQuery(query, variables) {\n+   const init = {\n+     method: \"POST\",\n+     body: JSON.stringify({ query, variables }),\n+     headers: {\n+       \"content-type\": \"application/json\"\n+     }\n+   };\n+\n+   return fetch(\"http://some.graphql.api\", init).then(res => res.json());\n+ }\n\n- const client = createClient(\"http://some.graphql.api\");\n+ const client = createClient(fetchQuery);\n```\n\n## v0.0.1-beta.7\n\n### Features\n\n- [core, transport] allow other fetch options to be set other then headers\n\n## v0.0.1-beta.6\n\n### Fixes\n\n- building packages locally\n\n## v0.0.1-beta.5\n\n### Fixes\n\n- last failed attempt to install packages on local install\n\n## v0.0.1-beta.4\n\n### Fixes\n\n- [babel-plugin] add @babel/cli\n\n## v0.0.1-beta.3\n\n### Fixes\n\n- fix further packages dependencies\n\n## v0.0.1-beta.2\n\n### Fixes\n\n- [bundle] add mri to package dependecies\n\n## v0.0.1-beta.1\n\n### Fixes\n\n- add coverage to packages .npmignore\n\n## v0.0.1-beta.0\n\n### Fixes\n\n- [core] fix objects not being cleaned from the cache on removal\n\n## v0.0.1-alpha.17\n\n### Fixes\n\n- [bindings] fix block scope bug due to the use of var instead of let\n\n## v0.0.1-alpha.16\n\n### Fixes\n\n- [babel-plugin] fix fragments not being compiled correctly in babel-plugin;\n- [bindings] fix shouldupdate logic in bindings\n\n## v0.0.1-alpha.15\n\n### Fixes\n\n- [babel-plugin] fix bug the was preventing fragments to be compiled in babel-plugin `sort-query`\n- [babel-plugin] improve coverage\n\n## v0.0.1-alpha.14\n\n### Fixes\n\n- [react] same as before but now it's working\n\n## v0.0.1-alpha.13\n\n### Fixes\n\n- [react] fix a bug that was preventing component setState to work within the consumer render function\n\n## v0.0.1-alpha.12\n\n### Features\n\n- replace `@babel/preset-typescript` for `rollup-plugin-typescript2` in `grafoo-bundle`\n\n## v0.0.1-alpha.11\n\n### Features\n\n- bindings generated mutation functions now resolve with the mutation response\n- bindings mutations `prop` does not require the update hook anymore\n\n### Fixes\n\n- bindings `loading` flag is always false whenever the `load` is triggered\n"
  },
  {
    "path": "lerna.json",
    "content": "{\n  \"packages\": [\n    \"packages/*\"\n  ],\n  \"npmClient\": \"yarn\",\n  \"useWorkspaces\": true,\n  \"version\": \"1.4.2\",\n  \"command\": {\n    \"publish\": {\n      \"message\": \"chore(release): publish %s\",\n      \"npmClient\": \"npm\"\n    }\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"grafoo\",\n  \"description\": \"a graphql client and toolkit\",\n  \"repository\": \"https://github.com/grafoojs/grafoo\",\n  \"author\": \"malbernaz <albernazmiguel@gmail.com>\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"bootstrap\": \"lerna bootstrap\",\n    \"test\": \"lerna run test\",\n    \"test:coverage\": \"lerna run test:coverage && codecov\",\n    \"prepare\": \"node scripts/build.js\",\n    \"clean\": \"rimraf packages/**/dist\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-push\": \"lerna run test\",\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"lint-staged\": {\n    \"*.{js,ts,tsx,json,graphql}\": [\n      \"eslint --fix\",\n      \"prettier --write\"\n    ]\n  },\n  \"prettier\": {\n    \"printWidth\": 100,\n    \"trailingComma\": \"none\"\n  },\n  \"eslintConfig\": {\n    \"extends\": [\n      \"eslint:recommended\",\n      \"plugin:@typescript-eslint/eslint-recommended\",\n      \"plugin:@typescript-eslint/recommended\"\n    ],\n    \"parser\": \"@typescript-eslint/parser\",\n    \"parserOptions\": {\n      \"sourceType\": \"module\",\n      \"ecmaVersion\": 2017,\n      \"ecmaFeatures\": {\n        \"jsx\": true\n      }\n    },\n    \"plugins\": [\n      \"@typescript-eslint\",\n      \"prefer-let\"\n    ],\n    \"env\": {\n      \"browser\": true,\n      \"commonjs\": true,\n      \"es6\": true,\n      \"node\": true,\n      \"jest\": true\n    },\n    \"rules\": {\n      \"prefer-const\": 0,\n      \"prefer-let/prefer-let\": 2,\n      \"@typescript-eslint/ban-ts-comment\": 1,\n      \"@typescript-eslint/no-empty-function\": 1\n    },\n    \"ignorePatterns\": [\n      \"packages/bundle\",\n      \"scripts\"\n    ]\n  },\n  \"devDependencies\": {\n    \"@babel/cli\": \"^7.0.0\",\n    \"@babel/core\": \"^7.0.0\",\n    \"@babel/preset-env\": \"^7.0.0\",\n    \"@babel/preset-react\": \"^7.0.0\",\n    \"@babel/preset-typescript\": \"^7.0.0\",\n    \"@babel/register\": \"^7.0.0\",\n    \"@graphql-tools/schema\": \"^8.2.0\",\n    \"@types/jest\": \"^27.0.2\",\n    \"@types/node\": \"^16.10.1\",\n    \"@types/react\": \"^17.0.24\",\n    \"@types/react-test-renderer\": \"^17.0.1\",\n    \"@types/uuid\": \"^8.3.1\",\n    \"@types/ws\": \"^8.2.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^4.9.1\",\n    \"@typescript-eslint/parser\": \"^4.9.1\",\n    \"babel-plugin-jsx-pragmatic\": \"^1.0.2\",\n    \"babel-plugin-tester\": \"^10.0.0\",\n    \"casual\": \"^1.5.19\",\n    \"codecov\": \"^3.2.0\",\n    \"eslint\": \"^7.15.0\",\n    \"eslint-plugin-prefer-let\": \"^1.1.0\",\n    \"fetch-mock\": \"^9.11.0\",\n    \"graphql\": \"^15.4.0\",\n    \"husky\": \"^7.0.2\",\n    \"jest\": \"^27.2.2\",\n    \"lerna\": \"^4.0.0\",\n    \"lint-staged\": \"^11.1.2\",\n    \"lowdb\": \"^3.0.0\",\n    \"node-fetch\": \"^3.0.0\",\n    \"preact\": \"^8.3.0\",\n    \"preact-render-spy\": \"^1.3.0\",\n    \"prettier\": \"^2.2.1\",\n    \"react\": \"^16.8.2\",\n    \"react-test-renderer\": \"^16.8.2\",\n    \"resolve.exports\": \"^1.0.2\",\n    \"rimraf\": \"^3.0.2\",\n    \"typescript\": \"^4.1.2\",\n    \"uuid\": \"^8.3.2\"\n  }\n}\n"
  },
  {
    "path": "packages/babel-plugin/.babelrc",
    "content": "{\n  \"presets\": [[\"@babel/preset-env\", { \"targets\": { \"node\": 4 } }]]\n}\n"
  },
  {
    "path": "packages/babel-plugin/.npmignore",
    "content": "coverage\n__tests__\n.rpt2_cache\n.babelrc\nschema.graphql\n"
  },
  {
    "path": "packages/babel-plugin/__tests__/__snapshots__/index.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`@grafoo/babel-plugin should compress the query string if the option compress is specified: should compress the query string if the option compress is specified 1`] = `\n\nimport gql from \"@grafoo/core/tag\";\nlet query = gql\\`\n  query($start: Int!, $offset: Int!, $id: ID!) {\n    posts(start: $start, offset: $offset) {\n      title\n      body\n      createdAt\n      tags { name }\n      authors { name username }\n    }\n    user(id: $id) { name username }\n  }\n\\`;\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nlet query = {\n  query:\n    \"query($id:ID!,$offset:Int!,$start:Int!){posts(offset:$offset,start:$start){authors{id name username}body createdAt id tags{id name}title}user(id:$id){id name username}}\",\n  paths: {\n    \"posts(offset:$offset,start:$start){authors{id name username}body createdAt id tags{id name}title}\":\n      {\n        name: \"posts\",\n        args: [\"offset\", \"start\"]\n      },\n    \"user(id:$id){id name username}\": {\n      name: \"user\",\n      args: [\"id\"]\n    }\n  }\n};\n\n\n`;\n\nexports[`@grafoo/babel-plugin should generate md5 hash and add it to object if the option generateIds is specified: should generate md5 hash and add it to object if the option generateIds is specified 1`] = `\n\nimport gql from \"@grafoo/core/tag\";\nlet query = gql\\`\n  query($start: Int!, $offset: Int!, $id: ID!) {\n    posts(start: $start, offset: $offset) {\n      title\n      body\n      createdAt\n      tags { name }\n      authors { name username }\n    }\n    user(id: $id) { name username }\n  }\n\\`;\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nlet query = {\n  id: \"6e0697df8f2453f2643bbd1e8a39c348\",\n  query:\n    \"query ($id: ID!, $offset: Int!, $start: Int!) {\\\\n  posts(offset: $offset, start: $start) {\\\\n    authors {\\\\n      id\\\\n      name\\\\n      username\\\\n    }\\\\n    body\\\\n    createdAt\\\\n    id\\\\n    tags {\\\\n      id\\\\n      name\\\\n    }\\\\n    title\\\\n  }\\\\n  user(id: $id) {\\\\n    id\\\\n    name\\\\n    username\\\\n  }\\\\n}\",\n  paths: {\n    \"posts(offset:$offset,start:$start){authors{id name username}body createdAt id tags{id name}title}\":\n      {\n        name: \"posts\",\n        args: [\"offset\", \"start\"]\n      },\n    \"user(id:$id){id name username}\": {\n      name: \"user\",\n      args: [\"id\"]\n    }\n  }\n};\n\n\n`;\n\nexports[`@grafoo/babel-plugin should include \\`idFields\\` in the client instantiation even if options are provided: should include \\`idFields\\` in the client instantiation even if options are provided 1`] = `\n\nimport createClient from \"@grafoo/core\";\nlet query = createClient(someTransport, {\n  headers: () => ({ authorization: \"some-token\" })\n});\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nimport createClient from \"@grafoo/core\";\nlet query = createClient(someTransport, {\n  headers: () => ({\n    authorization: \"some-token\"\n  }),\n  idFields: [\"id\"]\n});\n\n\n`;\n\nexports[`@grafoo/babel-plugin should include \\`idFields\\` in the client instantiation if not present in options: should include \\`idFields\\` in the client instantiation if not present in options 1`] = `\n\nimport createClient from \"@grafoo/core\";\nlet query = createClient(someTransport, {});\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nimport createClient from \"@grafoo/core\";\nlet query = createClient(someTransport, {\n  idFields: [\"id\"]\n});\n\n\n`;\n\nexports[`@grafoo/babel-plugin should include \\`idFields\\` in the client instantiation if options are not provided: should include \\`idFields\\` in the client instantiation if options are not provided 1`] = `\n\nimport createClient from \"@grafoo/core\";\nlet query = createClient(someTransport);\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nimport createClient from \"@grafoo/core\";\nlet query = createClient(someTransport, {\n  idFields: [\"id\"]\n});\n\n\n`;\n\nexports[`@grafoo/babel-plugin should include \\`idFields\\` in the client instantiation if options is a variable: should include \\`idFields\\` in the client instantiation if options is a variable 1`] = `\n\nimport createClient from \"@grafoo/core\";\nlet options = {};\nlet query = createClient(someTransport, options);\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nimport createClient from \"@grafoo/core\";\nlet options = {\n  idFields: [\"id\"]\n};\nlet query = createClient(someTransport, options);\n\n\n`;\n\nexports[`@grafoo/babel-plugin should not generate md5 hash and add it to object if the option generateIds is falsey: should not generate md5 hash and add it to object if the option generateIds is falsey 1`] = `\n\nimport gql from \"@grafoo/core/tag\";\nlet query = gql\\`\n  query($start: Int!, $offset: Int!, $id: ID!) {\n    posts(start: $start, offset: $offset) {\n      title\n      body\n      createdAt\n      tags { name }\n      authors { name username }\n    }\n    user(id: $id) { name username }\n  }\n\\`;\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nlet query = {\n  query:\n    \"query ($id: ID!, $offset: Int!, $start: Int!) {\\\\n  posts(offset: $offset, start: $start) {\\\\n    authors {\\\\n      id\\\\n      name\\\\n      username\\\\n    }\\\\n    body\\\\n    createdAt\\\\n    id\\\\n    tags {\\\\n      id\\\\n      name\\\\n    }\\\\n    title\\\\n  }\\\\n  user(id: $id) {\\\\n    id\\\\n    name\\\\n    username\\\\n  }\\\\n}\",\n  paths: {\n    \"posts(offset:$offset,start:$start){authors{id name username}body createdAt id tags{id name}title}\":\n      {\n        name: \"posts\",\n        args: [\"offset\", \"start\"]\n      },\n    \"user(id:$id){id name username}\": {\n      name: \"user\",\n      args: [\"id\"]\n    }\n  }\n};\n\n\n`;\n\nexports[`@grafoo/babel-plugin should overide \\`idFields\\` in the client instantiation if options is a variable: should overide \\`idFields\\` in the client instantiation if options is a variable 1`] = `\n\nimport createClient from \"@grafoo/core\";\nlet options = { idFields: [\"err\"] };\nlet query = createClient(someTransport, options);\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nimport createClient from \"@grafoo/core\";\nlet options = {\n  idFields: [\"id\"]\n};\nlet query = createClient(someTransport, options);\n\n\n`;\n\nexports[`@grafoo/babel-plugin should remove the imported path: should remove the imported path 1`] = `\n\nimport gql from \"@grafoo/core/tag\";\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\n\n\n`;\n\nexports[`@grafoo/babel-plugin should replace a tagged template literal with the compiled grafoo object: should replace a tagged template literal with the compiled grafoo object 1`] = `\n\nimport gql from \"@grafoo/core/tag\";\nlet query = gql\\`\n  query($start: Int!, $offset: Int!, $id: ID!) {\n    posts(start: $start, offset: $offset) {\n      title\n      body\n      createdAt\n      tags { name }\n      authors { name username }\n    }\n    user(id: $id) { name username }\n  }\n\\`;\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\nlet query = {\n  query:\n    \"query ($id: ID!, $offset: Int!, $start: Int!) {\\\\n  posts(offset: $offset, start: $start) {\\\\n    authors {\\\\n      id\\\\n      name\\\\n      username\\\\n    }\\\\n    body\\\\n    createdAt\\\\n    id\\\\n    tags {\\\\n      id\\\\n      name\\\\n    }\\\\n    title\\\\n  }\\\\n  user(id: $id) {\\\\n    id\\\\n    name\\\\n    username\\\\n  }\\\\n}\",\n  paths: {\n    \"posts(offset:$offset,start:$start){authors{id name username}body createdAt id tags{id name}title}\":\n      {\n        name: \"posts\",\n        args: [\"offset\", \"start\"]\n      },\n    \"user(id:$id){id name username}\": {\n      name: \"user\",\n      args: [\"id\"]\n    }\n  }\n};\n\n\n`;\n"
  },
  {
    "path": "packages/babel-plugin/__tests__/compile-document.js",
    "content": "import * as babel from \"@babel/core\";\nimport plugin from \"../src\";\n\nlet transform = (program, opts) =>\n  babel.transform(program, {\n    plugins: [\n      [plugin, Object.assign({ schema: \"__tests__/schema.graphql\", idFields: [\"id\"] }, opts)],\n    ],\n  });\n\ndescribe(\"compile document\", () => {\n  it(\"should throw if a schema path points to a inexistent file\", () => {\n    let program = `\n      import gql from \"@grafoo/core/tag\";\n      let query = gql\\`{ hello }\\`;\n    `;\n\n    expect(() => transform(program, { schema: \"?\" })).toThrow();\n  });\n\n  it(\"should throw if more then one operation is specified\", () => {\n    let program = `\n      import gql from \"@grafoo/core/tag\";\n      let query = gql\\`\n        { hello }\n        { goodbye }\n      \\`;\n    `;\n\n    expect(() => transform(program)).toThrow();\n  });\n\n  it(\"should accept fragments\", () => {\n    let program = `\n      import gql from \"@grafoo/core/tag\";\n      let query = gql\\`\n        fragment UserInfo on User {\n          name\n          bio\n        }\n      \\`;\n    `;\n\n    expect(() => transform(program)).not.toThrow();\n  });\n\n  it(\"should accept named queries\", () => {\n    let program = `\n      import gql from \"@grafoo/core/tag\";\n      let query = gql\\`\n        query NamedQuery {\n          me { id }\n        }\n      \\`;\n    `;\n\n    expect(() => transform(program)).not.toThrow();\n  });\n\n  it(\"should accept named queries with arguments\", () => {\n    let program = `\n      import gql from \"@grafoo/core/tag\";\n      let query = gql\\`\n        query NamedQuery($var: ID!) {\n          post(id: $var) {\n            id\n            title\n          }\n        }\n      \\`;\n    `;\n\n    expect(() => transform(program)).not.toThrow();\n  });\n});\n"
  },
  {
    "path": "packages/babel-plugin/__tests__/index.js",
    "content": "import pluginTester from \"babel-plugin-tester\";\nimport plugin from \"../src\";\n\npluginTester({\n  plugin,\n  pluginName: \"@grafoo/babel-plugin\",\n  pluginOptions: {\n    schema: \"__tests__/schema.graphql\",\n    idFields: [\"id\"],\n  },\n  tests: {\n    \"should throw if a import is not default\": {\n      code: 'import { gql } from \"@grafoo/core/tag\";',\n      error: true,\n    },\n    \"should throw if a schema is not present on the root directory\": {\n      pluginOptions: {\n        idFields: [\"id\"],\n      },\n      code: `\n        import gql from \"@grafoo/core/tag\";\n        let query = gql\\`{ hello }\\`;\n      `,\n      error: true,\n    },\n    \"should throw if a tagged template string literal has expressions in it\": {\n      code: `\n        import gql from \"@grafoo/core/tag\";\n        let query = gql\\`{ user(id: \"\\${1}\") { name } }\\`;\n      `,\n      error: true,\n    },\n    \"should remove the imported path\": {\n      code: 'import gql from \"@grafoo/core/tag\";',\n      snapshot: true,\n    },\n    \"should throw if idFields is not defined\": {\n      pluginOptions: {\n        schema: \"__tests__/schema.graphql\",\n      },\n      code: `\n        import gql from \"@grafoo/core/tag\";\n        let query = gql\\`{ hello }\\`;\n      `,\n      error: true,\n    },\n    \"should throw if during client instatiation options is passed with a type other then object\": {\n      code: `\n        import createClient from \"@grafoo/core\";\n        let query = createClient(someTransport, \"I AM ERROR\");\n      `,\n      error: true,\n    },\n    \"should throw if the type of some field in `idFields` is not of type string\": {\n      pluginOptions: {\n        schema: \"__tests__/schema.graphql\",\n        idFields: [\"id\", true],\n      },\n      code: `\n        import createClient from \"@grafoo/core\";\n        let query = createClient(someTransport);\n      `,\n      error: true,\n    },\n    \"should replace a tagged template literal with the compiled grafoo object\": {\n      code: `\n        import gql from \"@grafoo/core/tag\";\n        let query = gql\\`\n          query($start: Int!, $offset: Int!, $id: ID!) {\n            posts(start: $start, offset: $offset) {\n              title\n              body\n              createdAt\n              tags { name }\n              authors { name username }\n            }\n            user(id: $id) { name username }\n          }\n        \\`;\n      `,\n      snapshot: true,\n    },\n    \"should compress the query string if the option compress is specified\": {\n      pluginOptions: {\n        schema: \"__tests__/schema.graphql\",\n        idFields: [\"id\"],\n        compress: true,\n      },\n      code: `\n        import gql from \"@grafoo/core/tag\";\n        let query = gql\\`\n          query($start: Int!, $offset: Int!, $id: ID!) {\n            posts(start: $start, offset: $offset) {\n              title\n              body\n              createdAt\n              tags { name }\n              authors { name username }\n            }\n            user(id: $id) { name username }\n          }\n        \\`;\n      `,\n      snapshot: true,\n    },\n    \"should generate md5 hash and add it to object if the option generateIds is specified\": {\n      pluginOptions: {\n        schema: \"__tests__/schema.graphql\",\n        idFields: [\"id\"],\n        generateIds: true,\n      },\n      code: `\n        import gql from \"@grafoo/core/tag\";\n        let query = gql\\`\n          query($start: Int!, $offset: Int!, $id: ID!) {\n            posts(start: $start, offset: $offset) {\n              title\n              body\n              createdAt\n              tags { name }\n              authors { name username }\n            }\n            user(id: $id) { name username }\n          }\n        \\`;\n      `,\n      snapshot: true,\n    },\n    \"should not generate md5 hash and add it to object if the option generateIds is falsey\": {\n      pluginOptions: {\n        schema: \"__tests__/schema.graphql\",\n        idFields: [\"id\"],\n      },\n      code: `\n        import gql from \"@grafoo/core/tag\";\n        let query = gql\\`\n          query($start: Int!, $offset: Int!, $id: ID!) {\n            posts(start: $start, offset: $offset) {\n              title\n              body\n              createdAt\n              tags { name }\n              authors { name username }\n            }\n            user(id: $id) { name username }\n          }\n        \\`;\n      `,\n      snapshot: true,\n    },\n    \"should include `idFields` in the client instantiation if options are not provided\": {\n      code: `\n        import createClient from \"@grafoo/core\";\n        let query = createClient(someTransport);\n      `,\n      snapshot: true,\n    },\n    \"should include `idFields` in the client instantiation if not present in options\": {\n      code: `\n        import createClient from \"@grafoo/core\";\n        let query = createClient(someTransport, {});\n      `,\n      snapshot: true,\n    },\n    \"should include `idFields` in the client instantiation if options is a variable\": {\n      code: `\n        import createClient from \"@grafoo/core\";\n        let options = {};\n        let query = createClient(someTransport, options);\n      `,\n      snapshot: true,\n    },\n    \"should overide `idFields` in the client instantiation if options is a variable\": {\n      code: `\n        import createClient from \"@grafoo/core\";\n        let options = { idFields: [\"err\"] };\n        let query = createClient(someTransport, options);\n      `,\n      snapshot: true,\n    },\n    \"should throw if `idFields` in the client instantiation if options is not an object variable\": {\n      code: `\n        import createClient from \"@grafoo/core\";\n        let options = [];\n        let query = createClient(someTransport, options);\n      `,\n      error: true,\n    },\n    \"should include `idFields` in the client instantiation even if options are provided\": {\n      code: `\n        import createClient from \"@grafoo/core\";\n        let query = createClient(someTransport, {\n          headers: () => ({ authorization: \"some-token\" })\n        });\n      `,\n      snapshot: true,\n    },\n  },\n});\n"
  },
  {
    "path": "packages/babel-plugin/__tests__/insert-fields.js",
    "content": "import fs from \"fs\";\nimport { parse, print } from \"graphql\";\nimport path from \"path\";\nimport insertFields from \"../src/insert-fields\";\n\nlet schema = fs.readFileSync(path.join(__dirname, \"schema.graphql\"), \"utf-8\");\n\nlet cases = [\n  {\n    should: \"should insert a field\",\n    input: \"{ author { name } }\",\n    expectedOutput: \"{ author { name id } }\",\n    idFields: [\"id\"],\n  },\n  {\n    should: \"should insert more then a field if specified\",\n    input: \"{ author { name } }\",\n    expectedOutput: \"{ author { name username email id } }\",\n    idFields: [\"username\", \"email\", \"id\"],\n  },\n  {\n    should: \"should insert `__typename` if specified\",\n    input: \"{ author { name } }\",\n    expectedOutput: \"{ author { name __typename } }\",\n    idFields: [\"__typename\"],\n  },\n  {\n    should: \"should insert props in queries with fragments\",\n    input: `\n      {\n        user {\n          ...UserFrag\n        }\n      }\n\n      fragment UserFrag on Author {\n        name\n        posts {\n          title\n        }\n      }\n    `,\n    expectedOutput: `\n      {\n        user {\n          ...UserFrag\n          id\n        }\n      }\n\n      fragment UserFrag on Author {\n        name\n        posts {\n          title\n          id\n        }\n      }\n    `,\n    idFields: [\"id\"],\n  },\n  {\n    should: \"should insert props in queries with inline fragments\",\n    input: `\n      {\n        user {\n          name\n          ...on Author {\n            posts {\n              title\n            }\n          }\n        }\n      }\n    `,\n    expectedOutput: `\n      {\n        user {\n          name\n          ...on Author {\n            posts {\n              title\n              id\n              __typename\n            }\n          }\n          id\n          __typename\n        }\n      }\n    `,\n    idFields: [\"id\", \"__typename\"],\n  },\n  {\n    should: \"should not insert `__typename` inside fragments\",\n    input: `\n      {\n        user {\n          ...UserFrag\n        }\n      }\n\n      fragment UserFrag on Author {\n        name\n        posts {\n          title\n        }\n      }\n    `,\n    expectedOutput: `\n      {\n        user {\n          ...UserFrag\n          __typename\n        }\n      }\n\n      fragment UserFrag on Author {\n        name\n        posts {\n          title\n          __typename\n        }\n      }\n    `,\n    idFields: [\"__typename\"],\n  },\n  {\n    should: \"should not insert `__typename` inside inline fragments\",\n    input: `\n      {\n        user {\n          name\n          ...on Author {\n            posts {\n              title\n            }\n          }\n        }\n      }\n    `,\n    expectedOutput: `\n      {\n        user {\n          name\n          ...on Author {\n            posts {\n              title\n              __typename\n            }\n          }\n          __typename\n        }\n      }\n    `,\n    idFields: [\"__typename\"],\n  },\n  {\n    should: \"should insert field present on a fragment\",\n    input: `\n      {\n        user {\n          ...UserFrag\n        }\n      }\n\n      fragment UserFrag on Author {\n        name\n        posts {\n          title\n        }\n      }\n    `,\n    expectedOutput: `\n      {\n        user {\n          ...UserFrag\n        }\n      }\n\n      fragment UserFrag on Author {\n        name\n        posts {\n          title\n        }\n        bio\n      }\n    `,\n    idFields: [\"bio\"],\n  },\n  {\n    should: \"should insert field present in an inline fragment\",\n    input: `\n      {\n        user {\n          name\n          ...on Author {\n            posts {\n              title\n            }\n          }\n        }\n      }\n    `,\n    expectedOutput: `\n      {\n        user {\n          name\n          ...on Author {\n            posts {\n              title\n            }\n            bio\n          }\n        }\n      }\n    `,\n    idFields: [\"bio\"],\n  },\n  {\n    should: \"should insert fields in inline fragments while leaving unions\",\n    input: `\n      {\n        viewer {\n          ...on Visitor {\n            ip\n          }\n          ...on User {\n            username\n          }\n        }\n      }\n    `,\n    expectedOutput: `\n      {\n        viewer {\n          ...on Visitor {\n            ip\n            id\n          }\n          ...on User {\n            username\n            id\n          }\n        }\n      }\n    `,\n    idFields: [\"id\"],\n  },\n  {\n    should: \"should not insert `__typename` in an operation definition\",\n    input: `\n      mutation createPost($title: Int!, $body: Int!, $id: ID! $authors: [ID!]!) {\n        createPost(title: $title, body: $body, authors: $authors) {\n          title\n          body\n          createdAt\n          tags { name }\n          authors { name username }\n        }\n      }\n    `,\n    expectedOutput: `\n      mutation createPost($title: Int!, $body: Int!, $id: ID! $authors: [ID!]!) {\n        createPost(title: $title, body: $body, authors: $authors) {\n          title\n          body\n          createdAt\n          tags { name id __typename }\n          authors { name username id __typename }\n          id\n          __typename\n        }\n      }\n    `,\n    idFields: [\"id\", \"__typename\"],\n  },\n];\n\ndescribe(\"insert-fields\", () => {\n  for (let { should, input, expectedOutput, idFields } of cases) {\n    it(should, () => {\n      expect(print(insertFields(schema, parse(input), idFields))).toBe(\n        print(parse(expectedOutput))\n      );\n    });\n  }\n});\n"
  },
  {
    "path": "packages/babel-plugin/__tests__/schema.graphql",
    "content": "type Mutation {\n  createPost(title: String!, body: String!, authors: [ID!]!, tags: [String!]): Post\n  deletePost(id: ID): Post\n  createTag(name: String!): Tag\n  register(username: String!, email: String!, password: String!): User\n  login(email: String!, password: String!): String\n  updateUser(username: String, name: String, bio: String, email: String, password: String): User\n}\n\ntype Post {\n  id: ID!\n  title: String!\n  slug: String!\n  body: String!\n  published: Boolean!\n  createdAt: String!\n  updateAt: String!\n  authors: [User!]!\n  tags: [Tag!]!\n}\n\ntype Query {\n  author(id: ID!): Author\n  authors(start: Int!, offset: Int!): [Author]\n  posts(start: Int!, offset: Int!): [Post]\n  post(id: ID!): Post\n  tag(id: ID!): Tag\n  users(start: Int!, offset: Int!): [User]\n  user(id: ID!): User\n  me: User\n  viewer: Viewer\n}\n\nunion Viewer = Visitor | User\n\ntype Tag {\n  id: ID!\n  name: String!\n  posts: [Post!]!\n  createdAt: String!\n  updateAt: String!\n}\n\ninterface User {\n  id: ID!\n  username: String!\n  email: String!\n  createdAt: String!\n  updatedAt: String!\n}\n\ntype Author implements User {\n  name: String\n  bio: String\n  posts: [Post!]!\n}\n\ntype Visitor {\n  id: ID!\n  ip: String!\n}\n"
  },
  {
    "path": "packages/babel-plugin/__tests__/sort-query.js",
    "content": "import { parse, print as graphqlPrint } from \"graphql\";\nimport sortQuery from \"../src/sort-query\";\n\nlet gql = String.raw;\n\nfunction print(query, sort = false) {\n  return sort ? graphqlPrint(sortQuery(parse(query))) : graphqlPrint(parse(query));\n}\n\ndescribe(\"sort-query\", () => {\n  it(\"should sort fields, variable declarations and arguments\", () => {\n    let query = gql`\n      query($f: ID, $e: ID, $d: ID, $c: ID, $b: ID, $a: ID) {\n        f\n        e\n        d\n        c\n        b\n        a(f: $f, e: $e, d: $d, c: $c, b: $b, a: $a) {\n          f\n          e\n          d\n          c\n          b\n          a(f: $f, e: $e, d: $d, c: $c, b: $b, a: $a) {\n            f\n            e\n            d\n            c\n            b\n          }\n        }\n      }\n    `;\n\n    let expected = gql`\n      query($a: ID, $b: ID, $c: ID, $d: ID, $e: ID, $f: ID) {\n        a(a: $a, b: $b, c: $c, d: $d, e: $e, f: $f) {\n          a(a: $a, b: $b, c: $c, d: $d, e: $e, f: $f) {\n            b\n            c\n            d\n            e\n            f\n          }\n          b\n          c\n          d\n          e\n          f\n        }\n        b\n        c\n        d\n        e\n        f\n      }\n    `;\n\n    expect(print(query, true)).toBe(print(expected));\n  });\n\n  it(\"should sort fragments\", () => {\n    let query = gql`\n      query {\n        user {\n          posts {\n            ...PostInfo\n          }\n          ...UserInfo\n        }\n      }\n      fragment UserInfo on User {\n        id\n        id\n        name\n        bio\n      }\n      fragment PostInfo on Post {\n        title\n        content\n      }\n    `;\n\n    let expected = gql`\n      fragment PostInfo on Post {\n        content\n        title\n      }\n      fragment UserInfo on User {\n        bio\n        id\n        id\n        name\n      }\n      query {\n        user {\n          ...UserInfo\n          posts {\n            ...PostInfo\n          }\n        }\n      }\n    `;\n\n    expect(print(query, true)).toBe(print(expected));\n  });\n\n  it(\"should sort inline fragments\", () => {\n    let query = gql`\n      query {\n        user {\n          posts {\n            ... on Post {\n              title\n              content\n            }\n          }\n          ... on User {\n            id\n            name\n            bio\n          }\n        }\n      }\n    `;\n\n    let expected = gql`\n      {\n        user {\n          ... on User {\n            bio\n            id\n            name\n          }\n          posts {\n            ... on Post {\n              content\n              title\n            }\n          }\n        }\n      }\n    `;\n\n    expect(print(query, true)).toBe(print(expected));\n  });\n\n  it(\"should sort directives\", () => {\n    let query = gql`\n      query($c: ID, $b: ID, $a: ID) {\n        someField @c(c: $c) @a(a: $a) @b(c: $b)\n      }\n    `;\n\n    let expected = gql`\n      query($a: ID, $b: ID, $c: ID) {\n        someField @a(a: $a) @b(c: $b) @c(c: $c)\n      }\n    `;\n\n    expect(print(query, true)).toBe(print(expected));\n  });\n});\n"
  },
  {
    "path": "packages/babel-plugin/package.json",
    "content": "{\n  \"name\": \"@grafoo/babel-plugin\",\n  \"version\": \"1.4.2\",\n  \"description\": \"grafoo client babel plugin\",\n  \"repository\": \"https://github.com/grafoojs/grafoo/tree/master/packages/babel-plugin\",\n  \"main\": \"dist/index.js\",\n  \"author\": \"malbernaz<albernazmiguel@gmail.com>\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"babel\",\n    \"babel-plugin\",\n    \"graphql\",\n    \"graphql-client\",\n    \"grafoo\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"babel src --out-dir dist\",\n    \"test\": \"jest\",\n    \"test:coverage\": \"jest --coverage\"\n  },\n  \"jest\": {\n    \"transform\": {\n      \"^.+\\\\.(ts|tsx|js)$\": \"<rootDir>/../../scripts/jest-setup.js\"\n    }\n  },\n  \"dependencies\": {\n    \"babel-literal-to-ast\": \"^2.1.0\",\n    \"crypto-js\": \"^4.0.0\",\n    \"graphql\": \"^15.4.0\",\n    \"graphql-query-compress\": \"^1.0.0\"\n  },\n  \"gitHead\": \"0bc67d8b398884a1f387a1813e485d2c5318b974\",\n  \"devDependencies\": {\n    \"graphql\": \"^15.4.0\"\n  }\n}\n"
  },
  {
    "path": "packages/babel-plugin/readme.md",
    "content": "# `@grafoo/babel-plugin`\n\n<p><i>Grafoo Babel Plugin</i></p>\n\n<p>\n  <a href=https://circleci.com/gh/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/circleci/project/github/grafoojs/grafoo/master.svg?label=build\n      alt=build\n    />\n  </a>\n  <a href=https://codecov.io/github/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/codecov/c/github/grafoojs/grafoo/master.svg\n      alt=\"coverage\"\n    />\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/babel-plugin>\n    <img\n      src=https://img.shields.io/npm/v/@grafoo/babel-plugin.svg\n      alt=npm\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/babel-plugin>\n    <img\n      src=https://img.shields.io/npm/dm/@grafoo/babel-plugin.svg\n      alt=downloads\n    >\n  </a>\n  <a href=https://prettier.io>\n    <img\n      src=https://img.shields.io/badge/code_style-prettier-ff69b4.svg\n      alt=\"code style: prettier\"\n    />\n  </a>\n  <a href=https://lernajs.io>\n    <img\n      src=https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg\n      alt=\"mantained with: lerna\"\n    />\n  </a>\n  <a href=https://grafoo-slack.herokuapp.com>\n    <img\n      src=https://grafoo-slack.herokuapp.com/badge.svg\n      alt=\"slack\"\n    />\n  </a>\n</p>\n\nA premise Grafoo takes is that it should be able to extract an unique identifier from every node on the queries you write. It can be a GraphQL `ID` field, or more fields that together can form one (eg: an incremental integer and the GraphQL meta field `__typename`). It is `@grafoo/babel-plugin`'s responsibility to insert those fields on your queries automatically. If you have already used Apollo this should be very familiar to you, as our `idFields` configuration has the same pourpose of Apollo Cache's `dataIdFromObject`: to normalize your data.\n\n## Install\n\n```\n$ npm i @grafoo/core && npm i -D @grafoo/babel-plugin\n```\n\n## Configuration\n\nTo configure the plugin is required to specify the option `idFields`, an array of strings that represent the fields that Grafoo will use to build object identifiers. The option `schema`, is a path to a GraphQL schema in your file system relative to the root of your project, if not specified the plugin will look for the schema in the root of your project:\n\n```json\n{\n  \"plugins\": [\n    [\n      \"@grafoo/babel-plugin\",\n      {\n        \"schema\": \"schema.graphql\",\n        \"idFields\": [\"id\"],\n        \"generateIds\": false\n      }\n    ]\n  ]\n}\n```\n\n## How to get my schema?\n\nThe recommendation for now is to use the [`get-graphql-schema`](https://github.com/prismagraphql/get-graphql-schema), by [Prisma](https://www.prisma.io/). In the near future we are planning to introduce a `schemaUrl` option to this plugin so that this step won't be required anymore.\n\n## Transformations\n\n`@grafoo/babel-plugin` transforms your code in three ways:\n\n- Template tag literals using the default export from submodule `@grafoo/core/tag` will be compiled to a special object that will assist the client on the caching process.\n- Imports from submodule `@grafoo/core/tag` statements will be removed.\n- `idFields` will be inserted automatically on client instantiation.\n\n```diff\n  import createClient from \"@grafoo/core\";\n- import graphql from \"@grafoo/core/tag\";\n\n  function fetchQuery(query, variables) {\n    const init = {\n      method: \"POST\",\n      body: JSON.stringify({ query, variables }),\n      headers: {\n        \"content-type\": \"application/json\"\n      }\n    };\n\n    return fetch(\"http://some.graphql.api\", init).then(res => res.json());\n  }\n\n- const client = createClient(fetchQuery);\n+ const client = createClient(fetchQuery, {\n+   idFields: [\"id\"]\n+ });\n\n- const USER_QUERY = graphql`\n-   query($id: ID!) {\n-     user(id: $id) {\n-       name\n-       posts {\n-         title\n-       }\n-     }\n-   }\n- `;\n+ const USER_QUERY = {\n+   id: \"d4b567cd2a8891aa4cd1840f1a53002e\", // only if option \"generateIds\" is true\n+   query: \"query($id: ID!) { user(id: $id) { id name posts { id title } } }\",\n+   paths: {\n+     \"user(id:$id){id name posts{id title}}\": {\n+       name: \"user\"\n+       args: [\"id\"]\n+     }\n+   }\n+ };\n```\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "packages/babel-plugin/src/compile-document.js",
    "content": "import fs from \"fs\";\nimport { parse, print } from \"graphql\";\nimport compress from \"graphql-query-compress\";\nimport md5Hash from \"crypto-js/md5\";\nimport path from \"path\";\nimport insertFields from \"./insert-fields\";\nimport sortDocument from \"./sort-query\";\n\nlet schema;\nfunction getSchema(schemaPath) {\n  if (schema) return schema;\n\n  let fullPath;\n\n  if (!schemaPath) {\n    let schemaJson = path.join(process.cwd(), \"schema.json\");\n    let schemaGraphql = path.join(process.cwd(), \"schema.graphql\");\n    let schemaGql = path.join(process.cwd(), \"schema.gql\");\n\n    fullPath = fs.existsSync(schemaJson)\n      ? schemaJson\n      : fs.existsSync(schemaGraphql)\n      ? schemaGraphql\n      : fs.existsSync(schemaGql)\n      ? schemaGql\n      : undefined;\n  } else {\n    fullPath = path.join(process.cwd(), schemaPath);\n  }\n\n  fs.accessSync(fullPath, fs.F_OK);\n\n  schema = fs.readFileSync(fullPath, \"utf-8\");\n\n  return schema;\n}\n\nexport default function compileDocument(source, opts) {\n  let schema = getSchema(opts.schema);\n  let doc = sortDocument(insertFields(schema, parse(source), opts.idFields));\n  let oprs = doc.definitions.filter((d) => d.kind === \"OperationDefinition\");\n  let frags = doc.definitions.filter((d) => d.kind === \"FragmentDefinition\");\n\n  if (oprs.length > 1) {\n    throw new Error(\"@grafoo/core/tag: only one operation definition is accepted per tag.\");\n  }\n\n  let grafooObj = {};\n\n  if (oprs.length) {\n    let printed = print(oprs[0]);\n    let compressed = compress(printed);\n\n    // Use compressed version to get same hash even if\n    // query has different whitespaces, newlines, etc\n    // Document is also sorted by \"sortDocument\" therefore\n    // selections, fields, etc order shouldn't matter either\n    if (opts.generateIds) {\n      grafooObj.id = md5Hash(compressed).toString();\n    }\n\n    grafooObj.query = opts.compress ? compressed : printed;\n\n    grafooObj.paths = oprs[0].selectionSet.selections.reduce(\n      (acc, s) =>\n        Object.assign(acc, {\n          // TODO: generate hashes as well\n          // based on compress(print(s))?\n          [compress(print(s))]: {\n            name: s.name.value,\n            args: s.arguments.map((a) => {\n              if (a.value && a.value.kind === \"Variable\") {\n                a = a.value;\n              }\n              return a.name.value;\n            }),\n          },\n        }),\n      {}\n    );\n  }\n\n  if (frags.length) {\n    grafooObj.frags = {};\n\n    for (let frag of frags) {\n      grafooObj.frags[frag.name.value] = opts.compress ? compress(print(frag)) : print(frag);\n    }\n  }\n\n  return grafooObj;\n}\n"
  },
  {
    "path": "packages/babel-plugin/src/index.js",
    "content": "import parseLiteral from \"babel-literal-to-ast\";\nimport compileDocument from \"./compile-document\";\n\nexport default function transform({ types: t }) {\n  return {\n    visitor: {\n      Program(programPath, { opts }) {\n        let tagIdentifiers = [];\n        let clientFactoryIdentifiers = [];\n\n        if (typeof opts.compress !== \"boolean\") {\n          opts.compress = process.env.NODE_ENV === \"production\";\n        }\n\n        if (typeof opts.generateIds !== \"boolean\") {\n          opts.generateIds = false;\n        }\n\n        if (!opts.idFields) {\n          throw new Error(\"@grafoo/babel-plugin: the `idFields` option is required.\");\n        }\n\n        if (\n          !Array.isArray(opts.idFields) ||\n          opts.idFields.some((field) => typeof field !== \"string\")\n        ) {\n          throw new Error(\n            \"@grafoo/babel-plugin: the `idFields` option must be declared as an array of strings.\"\n          );\n        }\n\n        programPath.traverse({\n          ImportDeclaration(path) {\n            let { source, specifiers } = path.node;\n\n            if (source.value === \"@grafoo/core\") {\n              let defaultSpecifier = specifiers.find((s) => t.isImportDefaultSpecifier(s));\n\n              clientFactoryIdentifiers.push(defaultSpecifier.local.name);\n            }\n\n            if (source.value === \"@grafoo/core/tag\") {\n              let defaultSpecifier = specifiers.find((specifier) =>\n                t.isImportDefaultSpecifier(specifier)\n              );\n\n              if (!defaultSpecifier) {\n                throw path.buildCodeFrameError(\"@grafoo/core/tag: no default import.\");\n              }\n\n              tagIdentifiers.push(defaultSpecifier.local.name);\n\n              path.remove();\n            }\n          },\n\n          CallExpression(path) {\n            let { arguments: args, callee } = path.node;\n\n            let idFieldsArrayAst = t.arrayExpression(\n              opts.idFields.map((field) => t.stringLiteral(field))\n            );\n\n            let clientObjectAst = t.objectProperty(t.identifier(\"idFields\"), idFieldsArrayAst);\n\n            if (clientFactoryIdentifiers.some((name) => t.isIdentifier(callee, { name }))) {\n              if (!args[1]) {\n                args[1] = t.objectExpression([clientObjectAst]);\n              }\n\n              if (t.isIdentifier(args[1])) {\n                let name = args[1].name;\n                let { init } = path.scope.bindings[name].path.node;\n\n                if (path.scope.hasBinding(name)) {\n                  if (t.isObjectExpression(init)) {\n                    let idFieldsProp = init.properties.find((arg) => arg.key.name === \"idFields\");\n\n                    if (idFieldsProp) {\n                      idFieldsProp.value = idFieldsArrayAst;\n                    } else {\n                      init.properties.push(clientObjectAst);\n                    }\n                  } else {\n                    throw path.buildCodeFrameError(\n                      callee.name +\n                        \" second argument must be of type object, instead got \" +\n                        args[1].type +\n                        \".\"\n                    );\n                  }\n                }\n              } else if (t.isObjectExpression(args[1])) {\n                let idFieldsProp = args[1].properties.find((arg) => arg.key.name === \"idFields\");\n\n                if (idFieldsProp) {\n                  idFieldsProp.value = idFieldsArrayAst;\n                } else {\n                  args[1].properties.push(clientObjectAst);\n                }\n              } else {\n                throw path.buildCodeFrameError(\n                  callee.name +\n                    \" second argument must be of type object, instead got \" +\n                    args[1].type +\n                    \".\"\n                );\n              }\n            }\n          },\n\n          TaggedTemplateExpression(path) {\n            if (tagIdentifiers.some((name) => t.isIdentifier(path.node.tag, { name }))) {\n              try {\n                let quasi = path.get(\"quasi\");\n\n                if (quasi.get(\"expressions\").length) {\n                  throw path.buildCodeFrameError(\n                    \"@grafoo/core/tag: interpolation is not supported in a graphql tagged template literal.\"\n                  );\n                }\n\n                let source = quasi.node.quasis.reduce((src, q) => src + q.value.raw, \"\");\n\n                path.replaceWith(parseLiteral(compileDocument(source, opts)));\n              } catch (error) {\n                if (error.code === \"ENOENT\") {\n                  throw new Error(\n                    \"Could not find a schema in the root directory! \" +\n                      \"Please use the `schema` option to specify your schema path, \" +\n                      \"or the `schemaUrl` to specify your graphql endpoint.\"\n                  );\n                }\n\n                throw path.buildCodeFrameError(error.message);\n              }\n            }\n          },\n        });\n      },\n    },\n  };\n}\n"
  },
  {
    "path": "packages/babel-plugin/src/insert-fields.js",
    "content": "import { TypeInfo, buildASTSchema, parse, visit, visitWithTypeInfo } from \"graphql\";\n\nfunction getType(typeInfo) {\n  let currentType = typeInfo.getType();\n  while (currentType.ofType) currentType = currentType.ofType;\n  return currentType;\n}\n\nfunction insertField(selections, value) {\n  selections.push({ kind: \"Field\", name: { kind: \"Name\", value } });\n}\n\nexport default function insertFields(schemaStr, documentAst, idFields) {\n  let typeInfo = new TypeInfo(buildASTSchema(parse(schemaStr)));\n\n  let isOperationDefinition = false;\n  let isFragment = false;\n\n  let visitor = {\n    OperationDefinition() {\n      isOperationDefinition = true;\n    },\n    InlineFragment() {\n      isFragment = true;\n    },\n    FragmentDefinition() {\n      isFragment = true;\n    },\n    SelectionSet({ selections }) {\n      if (isOperationDefinition) {\n        isOperationDefinition = false;\n\n        return;\n      }\n\n      let type = getType(typeInfo);\n\n      if (type.astNode.kind === \"UnionTypeDefinition\") {\n        return;\n      }\n\n      let typeFields = Object.keys(type.getFields());\n      let typeInterfaces = type.getInterfaces ? type.getInterfaces() : [];\n      let typeInterfacesFields = typeInterfaces.reduce(\n        (acc, next) => acc.concat(Object.keys(next.getFields())),\n        []\n      );\n\n      for (let field of idFields) {\n        if (selections.some((_) => _.name && _.name.value === field)) {\n          continue; // Skip already declared fields\n        }\n\n        let typeHasField = typeFields.some((_) => _ === field);\n        let typeInterfacesHasField = typeInterfacesFields.some((_) => _ === field);\n\n        if (\n          typeHasField ||\n          (field === \"__typename\" && !isFragment) ||\n          (typeInterfacesHasField && !isFragment)\n        ) {\n          insertField(selections, field);\n        }\n      }\n\n      isFragment = false;\n    },\n  };\n\n  return visit(documentAst, visitWithTypeInfo(typeInfo, visitor));\n}\n"
  },
  {
    "path": "packages/babel-plugin/src/sort-query.js",
    "content": "import { visit } from \"graphql\";\n\nfunction sort(array, fn) {\n  fn = fn || ((obj) => obj.name.value);\n\n  return (\n    array &&\n    array.sort((prev, next) => {\n      if (fn(prev) < fn(next)) return -1;\n      if (fn(prev) > fn(next)) return 1;\n      return 0;\n    })\n  );\n}\n\nexport default function sortQuery(document) {\n  return visit(document, {\n    Document(node) {\n      node.definitions = [\n        ...sort(node.definitions.filter((def) => def.kind === \"FragmentDefinition\")),\n        ...node.definitions.filter((def) => def.kind !== \"FragmentDefinition\"),\n      ];\n    },\n    OperationDefinition(node) {\n      sort(node.directives);\n      sort(node.variableDefinitions, (_) => _.variable.name.value);\n    },\n    SelectionSet(node) {\n      sort(node.selections, (_) => (_.alias || _.name || _.typeCondition.name).value);\n    },\n    Field(node) {\n      sort(node.directives);\n      sort(node.arguments);\n    },\n    InlineFragment(node) {\n      sort(node.directives);\n    },\n    FragmentSpread(node) {\n      sort(node.directives);\n    },\n    FragmentDefinition(node) {\n      sort(node.directives);\n    },\n    Directive(node) {\n      sort(node.arguments);\n    },\n  });\n}\n"
  },
  {
    "path": "packages/bindings/.babelrc",
    "content": "{\n  \"presets\": [\n    [\"@babel/preset-env\", { \"targets\": { \"node\": \"current\" } }],\n    \"@babel/preset-typescript\"\n  ],\n  \"plugins\": [[\"module:@grafoo/babel-plugin\", { \"schema\": \"schema.graphql\", \"idFields\": [\"id\"] }]]\n}\n"
  },
  {
    "path": "packages/bindings/.npmignore",
    "content": "coverage\n__tests__\n.rpt2_cache\n.babelrc\nschema.graphql\n"
  },
  {
    "path": "packages/bindings/__tests__/index.ts",
    "content": "import createBindings from \"../src\";\nimport graphql from \"@grafoo/core/tag\";\nimport createClient from \"@grafoo/core\";\nimport { GrafooClient, GrafooMutations, Variables } from \"@grafoo/types\";\nimport { mockQueryRequest } from \"@grafoo/test-utils\";\nimport createTransport from \"@grafoo/http-transport\";\n\ninterface Post {\n  title: string;\n  content: string;\n  id: string;\n  __typename: string;\n  author: Author;\n}\n\ninterface Author {\n  name: string;\n  id: string;\n  __typename: string;\n  posts?: Array<Post>;\n}\n\ninterface Authors {\n  authors: Author[];\n}\n\ninterface CreateAuthor {\n  createAuthor: {\n    name: string;\n    id: string;\n    __typename: string;\n    posts?: Array<Post>;\n  };\n}\n\ninterface DeleteAuthor {\n  deleteAuthor: {\n    name: string;\n    id: string;\n    __typename: string;\n    posts?: Array<Post>;\n  };\n}\n\ninterface UpdateAuthor {\n  updateAuthor: {\n    name: string;\n    id: string;\n    __typename: string;\n    posts?: Array<Post>;\n  };\n}\n\nlet AUTHORS = graphql`\n  query {\n    authors {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet AUTHOR = graphql`\n  query($id: ID!) {\n    author(id: $id) {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet POSTS_AND_AUTHORS = graphql`\n  query {\n    posts {\n      title\n      body\n      author {\n        name\n      }\n    }\n\n    authors {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet CREATE_AUTHOR = graphql`\n  mutation($name: String!) {\n    createAuthor(name: $name) {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet DELETE_AUTHOR = graphql`\n  mutation($id: ID!) {\n    deleteAuthor(id: $id) {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet UPDATE_AUTHOR = graphql`\n  mutation($id: ID!, $name: String) {\n    updateAuthor(id: $id, name: $name) {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\ndescribe(\"@grafoo/bindings\", () => {\n  let client: GrafooClient;\n  beforeEach(() => {\n    jest.resetAllMocks();\n    let transport = createTransport(\"https://some.graphql.api/\");\n    client = createClient(transport, { idFields: [\"id\"] });\n  });\n\n  it(\"should be evocable given the minimal props\", () => {\n    let bindings;\n    expect(() => (bindings = createBindings(client, {}, () => void 0))).not.toThrow();\n\n    Object.keys(bindings).forEach((fn) => {\n      expect(typeof bindings[fn]).toBe(\"function\");\n    });\n\n    expect(bindings.unbind()).toBeUndefined();\n  });\n\n  it(\"should not provide any data if no query or mutation is given\", () => {\n    let bindings = createBindings(client, {}, () => void 0);\n\n    let props = bindings.getState();\n\n    expect(props).toEqual({ client });\n  });\n\n  it(\"should execute a query\", async () => {\n    let { data } = await mockQueryRequest<Authors>(AUTHORS);\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings<Authors>(client, { query: AUTHORS }, renderFn);\n\n    expect(bindings.getState()).toMatchObject({ loaded: false, loading: true });\n\n    await bindings.load();\n\n    expect(bindings.getState()).toMatchObject({ ...data, loaded: true, loading: false });\n  });\n\n  it(\"should notify a loading state\", async () => {\n    let { data } = await mockQueryRequest<Authors>(AUTHORS);\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings<Authors>(client, { query: AUTHORS }, renderFn);\n\n    await bindings.load();\n\n    expect(renderFn).toHaveBeenCalledTimes(1);\n    expect(bindings.getState()).toMatchObject({ ...data, loaded: true, loading: false });\n\n    let reloadPromise = bindings.load();\n\n    expect(bindings.getState().loading).toBe(true);\n\n    await reloadPromise;\n\n    expect(bindings.getState().loading).toBe(false);\n  });\n\n  it(\"should provide the data if the query is already cached\", async () => {\n    let { data } = await mockQueryRequest<Authors>(AUTHORS);\n\n    client.write(AUTHORS, data);\n\n    let bindings = createBindings<Authors>(client, { query: AUTHORS }, () => void 0);\n\n    expect(bindings.getState()).toMatchObject({ ...data, loaded: true, loading: false });\n  });\n\n  it(\"should provide the data if a query is partialy cached\", async () => {\n    let { data } = await mockQueryRequest<Authors>(AUTHORS);\n\n    client.write(AUTHORS, data);\n\n    let bindings = createBindings<Authors>(client, { query: POSTS_AND_AUTHORS }, () => void 0);\n\n    expect(bindings.getState()).toMatchObject({ ...data, loaded: false, loading: true });\n  });\n\n  it(\"should trigger updater function if the cache has been updated\", async () => {\n    let { data } = await mockQueryRequest<Authors>(AUTHORS);\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings<Authors>(client, { query: AUTHORS }, renderFn);\n\n    client.write(AUTHORS, data);\n\n    expect(renderFn).toHaveBeenCalled();\n    expect(bindings.getState()).toMatchObject(data);\n  });\n\n  it(\"should provide the state for a cached query\", async () => {\n    let { data } = await mockQueryRequest<Authors>(AUTHORS);\n\n    client.write(AUTHORS, data);\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings<Authors>(client, { query: AUTHORS }, renderFn);\n\n    expect(bindings.getState()).toMatchObject(data);\n  });\n\n  it(\"should stop updating if unbind has been called\", async () => {\n    let { data } = await mockQueryRequest<Authors>(AUTHORS);\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings<Authors>(client, { query: AUTHORS }, renderFn);\n\n    await bindings.load();\n\n    bindings.unbind();\n\n    client.write(AUTHORS, {\n      authors: data.authors.map((a, i) => (!i ? { ...a, name: \"Homer\" } : a)),\n    });\n\n    expect(client.read<Authors>(AUTHORS).data.authors[0].name).toBe(\"Homer\");\n    expect(renderFn).toHaveBeenCalledTimes(1);\n    expect(bindings.getState()).toMatchObject(data);\n  });\n\n  it(\"should provide errors on bad request\", async () => {\n    let FailAuthors = { ...AUTHORS, query: AUTHORS.query.substr(1) };\n\n    let { errors } = await mockQueryRequest(FailAuthors);\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings(client, { query: FailAuthors }, renderFn);\n\n    await bindings.load();\n\n    expect(renderFn).toHaveBeenCalledTimes(1);\n    expect(bindings.getState()).toMatchObject({ errors });\n  });\n\n  it(\"should perform a simple mutation\", async () => {\n    interface Mutations {\n      createAuthor: CreateAuthor;\n    }\n\n    let mutations: GrafooMutations<Author, Mutations> = { createAuthor: { query: CREATE_AUTHOR } };\n\n    let bindings = createBindings(client, { mutations }, () => void 0);\n\n    let props = bindings.getState();\n\n    let variables = { name: \"Bart\" };\n\n    let { data } = await mockQueryRequest({ query: CREATE_AUTHOR.query, variables });\n\n    let { data: mutationData } = await props.createAuthor(variables);\n\n    expect(mutationData).toEqual(data);\n  });\n\n  it(\"should perform mutation with a cache update\", async () => {\n    await mockQueryRequest<Authors>(AUTHORS);\n\n    interface Mutations {\n      createAuthor: CreateAuthor;\n    }\n\n    let mutations: GrafooMutations<Authors, Mutations> = {\n      createAuthor: {\n        query: CREATE_AUTHOR,\n        update: ({ authors }, data) => ({\n          authors: [data.createAuthor, ...authors],\n        }),\n      },\n    };\n\n    let update = jest.spyOn(mutations.createAuthor, \"update\");\n\n    let bindings = createBindings(client, { query: AUTHORS, mutations }, () => void 0);\n\n    let props = bindings.getState();\n\n    expect(typeof props.createAuthor).toBe(\"function\");\n\n    await bindings.load();\n\n    let variables = { name: \"Homer\" };\n\n    let { data } = await mockQueryRequest<CreateAuthor>({ query: CREATE_AUTHOR.query, variables });\n\n    let { authors } = bindings.getState();\n\n    await props.createAuthor(variables);\n\n    expect(update).toHaveBeenCalledWith({ authors }, data);\n  });\n\n  it(\"should perform optimistic update\", async () => {\n    await mockQueryRequest(AUTHORS);\n\n    interface Mutations {\n      createAuthor: CreateAuthor;\n    }\n\n    let mutations: GrafooMutations<Authors, Mutations> = {\n      createAuthor: {\n        query: CREATE_AUTHOR,\n        optimisticUpdate: ({ authors }, variables: Author) => ({\n          authors: [{ ...variables, id: \"tempID\" }, ...authors],\n        }),\n        update: ({ authors }, data) => ({\n          authors: authors.map((p) => (p.id === \"tempID\" ? data.createAuthor : p)),\n        }),\n      },\n    };\n\n    let optimisticUpdate = jest.spyOn(mutations.createAuthor, \"optimisticUpdate\");\n    let update = jest.spyOn(mutations.createAuthor, \"update\");\n\n    let bindings = createBindings(client, { query: AUTHORS, mutations }, () => void 0);\n\n    let props = bindings.getState();\n\n    expect(typeof props.createAuthor).toBe(\"function\");\n\n    await bindings.load();\n\n    let variables = { name: \"Peter\" };\n\n    let { data } = await mockQueryRequest<CreateAuthor>({ query: CREATE_AUTHOR.query, variables });\n\n    let { authors } = bindings.getState();\n\n    let createAuthorPromise = props.createAuthor(variables);\n\n    expect(optimisticUpdate).toHaveBeenCalledWith({ authors }, variables);\n\n    let { authors: modifiedAuthors } = bindings.getState();\n\n    await createAuthorPromise;\n\n    expect(update).toHaveBeenCalledWith({ authors: modifiedAuthors }, data);\n  });\n\n  it(\"should update if query objects has less keys then nextObjects\", async () => {\n    let { query } = CREATE_AUTHOR;\n    let author = (await mockQueryRequest<CreateAuthor>({ query, variables: { name: \"gustav\" } }))\n      .data.createAuthor;\n    let { data } = await mockQueryRequest(AUTHORS);\n\n    client.write(AUTHORS, data);\n\n    interface Mutations {\n      removeAuthor: DeleteAuthor;\n    }\n\n    let mutations: GrafooMutations<Authors, Mutations> = {\n      removeAuthor: {\n        query: DELETE_AUTHOR,\n        optimisticUpdate: ({ authors }, { id }: Author) => ({\n          authors: authors.filter((author) => author.id !== id),\n        }),\n      },\n    };\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings(client, { query: AUTHORS, mutations }, renderFn);\n\n    let { removeAuthor } = bindings.getState();\n\n    let variables = { id: author.id };\n\n    await removeAuthor(variables);\n\n    expect(renderFn).toHaveBeenCalled();\n  });\n\n  it(\"should update if query objects is modified\", async () => {\n    let { query } = CREATE_AUTHOR;\n    let author = (\n      await mockQueryRequest<CreateAuthor>({\n        query,\n        variables: { name: \"sven\" },\n      })\n    ).data.createAuthor;\n    let { data } = await mockQueryRequest(AUTHORS);\n\n    client.write(AUTHORS, data);\n\n    interface Mutations {\n      updateAuthor: UpdateAuthor;\n    }\n\n    let mutations: GrafooMutations<Authors, Mutations> = {\n      updateAuthor: {\n        query: UPDATE_AUTHOR,\n        optimisticUpdate: ({ authors }, variables: Author) => ({\n          authors: authors.map((author) => (author.id === variables.id ? variables : author)),\n        }),\n      },\n    };\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings(client, { query: AUTHORS, mutations }, renderFn);\n\n    let { updateAuthor } = bindings.getState();\n\n    let variables = { ...author, name: \"johan\" };\n\n    await mockQueryRequest({ query: UPDATE_AUTHOR.query, variables });\n\n    await updateAuthor(variables);\n\n    expect(renderFn).toHaveBeenCalled();\n  });\n\n  it(\"should not update if query objects is not modified\", async () => {\n    let { data } = await mockQueryRequest(AUTHORS);\n\n    client.write(AUTHORS, data);\n\n    let renderFn = jest.fn();\n\n    createBindings(client, { query: AUTHORS }, renderFn);\n\n    client.write(AUTHORS, data);\n\n    expect(renderFn).not.toHaveBeenCalled();\n  });\n\n  it(\"should accept multiple mutations\", async () => {\n    let { data } = await mockQueryRequest(AUTHORS);\n    client.write(AUTHORS, data);\n\n    interface Mutations {\n      createAuthor: CreateAuthor;\n      updateAuthor: UpdateAuthor;\n      deleteAuthor: DeleteAuthor;\n    }\n\n    let mutations: GrafooMutations<Authors, Mutations> = {\n      createAuthor: {\n        query: CREATE_AUTHOR,\n        optimisticUpdate: ({ authors }, variables: Author) => ({\n          authors: [{ ...variables, id: \"tempID\" }, ...authors],\n        }),\n        update: ({ authors }, data: CreateAuthor) => ({\n          authors: authors.map((author) => (author.id === \"tempID\" ? data.createAuthor : author)),\n        }),\n      },\n      updateAuthor: {\n        query: UPDATE_AUTHOR,\n        optimisticUpdate: ({ authors }, variables: Author) => ({\n          authors: authors.map((author) => (author.id === variables.id ? variables : author)),\n        }),\n      },\n      deleteAuthor: {\n        query: DELETE_AUTHOR,\n        optimisticUpdate: ({ authors }, variables: Author) => ({\n          authors: authors.map((author) => (author.id === variables.id ? variables : author)),\n        }),\n      },\n    };\n\n    let renderFn = jest.fn();\n\n    let bindings = createBindings(client, { query: AUTHORS, mutations }, renderFn);\n    let props = bindings.getState();\n\n    try {\n      let variables: Variables = { name: \"mikel\" };\n      let { data } = await mockQueryRequest<CreateAuthor>({\n        query: CREATE_AUTHOR.query,\n        variables,\n      });\n      expect(await mockQueryRequest({ query: CREATE_AUTHOR.query, variables })).toEqual(\n        await props.createAuthor(variables)\n      );\n\n      variables = { ...data.createAuthor, name: \"miguel\" };\n      expect(await mockQueryRequest({ query: UPDATE_AUTHOR.query, variables })).toEqual(\n        await props.updateAuthor(variables)\n      );\n\n      variables = data.createAuthor;\n      expect(await mockQueryRequest({ query: DELETE_AUTHOR.query, variables })).toEqual(\n        await props.deleteAuthor(data.createAuthor)\n      );\n    } catch (err) {\n      console.error(err);\n    }\n  });\n\n  it(\"should update variables when new variables are passed\", async () => {\n    let {\n      data: { authors },\n    } = await mockQueryRequest<Authors>(AUTHORS);\n\n    let [author1, author2] = authors;\n    let author1Variables = { id: author1.id };\n    let author2Variables = { id: author2.id };\n\n    let bindings = createBindings<{ author: Author }>(\n      client,\n      { query: AUTHOR, variables: author1Variables },\n      () => {}\n    );\n\n    await mockQueryRequest({ query: AUTHOR.query, variables: author1Variables });\n    await bindings.load();\n    expect(bindings.getState().author).toMatchObject(author1);\n    expect(client.read<{ author: Author }>(AUTHOR, author1Variables).data.author).toEqual(author1);\n\n    await mockQueryRequest({ query: AUTHOR.query, variables: author2Variables });\n    await bindings.load(author2Variables);\n    expect(bindings.getState().author).toMatchObject(author2);\n    expect(client.read<{ author: Author }>(AUTHOR, author2Variables).data.author).toEqual(author2);\n  });\n});\n"
  },
  {
    "path": "packages/bindings/__tests__/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig\",\n  \"include\": [\".\"]\n}\n"
  },
  {
    "path": "packages/bindings/package.json",
    "content": "{\n  \"name\": \"@grafoo/bindings\",\n  \"version\": \"1.4.2\",\n  \"description\": \"grafoo client internal helper for building framework bindings\",\n  \"repository\": \"https://github.com/grafoojs/grafoo/tree/master/packages/bindings\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"author\": \"malbernaz<albernazmiguel@gmail.com>\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"babel\",\n    \"babel-plugin\",\n    \"graphql\",\n    \"graphql-client\",\n    \"grafoo\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"grafoo-bundle --input src/index.ts\",\n    \"test\": \"jest\",\n    \"test:coverage\": \"jest --coverage\"\n  },\n  \"jest\": {\n    \"transform\": {\n      \"^.+\\\\.(ts|tsx|js)$\": \"<rootDir>/../../scripts/jest-setup.js\"\n    },\n    \"resolver\": \"<rootDir>/../../scripts/resolver.js\",\n    \"transformIgnorePatterns\": [\n      \"node_modules/(?!(lowdb|steno|node-fetch|fetch-blob)/)\"\n    ]\n  },\n  \"dependencies\": {\n    \"@grafoo/types\": \"^1.4.2\"\n  },\n  \"gitHead\": \"0bc67d8b398884a1f387a1813e485d2c5318b974\"\n}\n"
  },
  {
    "path": "packages/bindings/readme.md",
    "content": "# `@grafoo/bindings`\n\n<p><i>Grafoo Bindings for Frameworks</i></p>\n\n<p>\n  <a href=https://circleci.com/gh/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/circleci/project/github/grafoojs/grafoo/master.svg?label=build\n      alt=build\n    />\n  </a>\n  <a href=https://codecov.io/github/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/codecov/c/github/grafoojs/grafoo/master.svg\n      alt=\"coverage\"\n    />\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/bindings>\n    <img\n      src=https://img.shields.io/npm/v/@grafoo/bindings.svg\n      alt=npm\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/bindings>\n    <img\n      src=https://img.shields.io/npm/dm/@grafoo/bindings.svg\n      alt=downloads\n    >\n  </a>\n  <a href=https://prettier.io>\n    <img\n      src=https://img.shields.io/badge/code_style-prettier-ff69b4.svg\n      alt=\"code style: prettier\"\n    />\n  </a>\n  <a href=https://lernajs.io>\n    <img\n      src=https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg\n      alt=\"mantained with: lerna\"\n    />\n  </a>\n  <a href=https://grafoo-slack.herokuapp.com>\n    <img\n      src=https://grafoo-slack.herokuapp.com/badge.svg\n      alt=\"slack\"\n    />\n  </a>\n</p>\n\nThis packages purpose is to standardize how view layer integrations are implemented for Grafoo. `@grafoo/bindings` has only a default export that is a `createBindings` factory function that returns an interface that provides data and notify for changes.\n\n## API\n\n### Arguments\n\n| Argument | type         | Description                                           |\n| -------- | ------------ | ----------------------------------------------------- |\n| client   | GrafooClient | a client nstance                                      |\n| props    | object       | a props object passed by the user (description below) |\n| updater  | function     | a callback to notify for data changes                 |\n\n#### Example\n\n```js\nimport createBindings from \"@grafoo/bindings\";\nimport createClient from \"@grafoo/core\";\n\nfunction fetchQuery(query, variables) {\n  const init = {\n    method: \"POST\",\n    body: JSON.stringify({ query, variables }),\n    headers: {\n      \"content-type\": \"application/json\"\n    }\n  };\n\n  return fetch(\"http://some.graphql.api\", init).then(res => res.json());\n}\n\nconst client = createClient(fetchQuery);\nconst props = {};\nconst updater = () => {};\n\nconst bindings = createBindings(client, props, updater);\n```\n\n### `props` argument\n\n| Name      | type    | Descrition                                                 |\n| --------- | ------- | ---------------------------------------------------------- |\n| query     | object  | the query created with `@grafoo/core/tag`'s template tag   |\n| variables | object  | GraphQL variables object for the query                     |\n| mutations | object  | an object where mutations are declared (description below) |\n| skip      | boolean | whether the client should skip the query request           |\n\n### Mutations\n\nThe `mutations` prop is a map of _mutation objects_ that are shaped like so:\n\n```js\nconst createPost = {\n  query: CREATE_POST_MUTATION,\n  optimisticUpdate: ({ allPosts }, variables) => ({\n    allPosts: [{ ...variables.postInput, id: \"tempID\" }, ...allPosts]\n  }),\n  update: ({ allPosts }, response) => ({\n    allPosts: allPosts.map(p => (p.id === \"tempID\" ? response.post : p))\n  })\n};\n\nconst mutations = { createPost };\n```\n\nA mutation object receives the following props:\n\n| Name             | Type     | Required | Descrition                                                          |\n| ---------------- | -------- | -------- | ------------------------------------------------------------------- |\n| query            | object   | true     | a mutation query created with `@grafoo/core/tag`                    |\n| update           | function | false    | updates the cache when a request is completed (description below)   |\n| optimisticUpdate | function | false    | updates the cache before a request is completed (description below) |\n\nEach mutation will generate a single function that accepts a GraphQL variables object as argument and return a promise that will resolve with the mutation data or reject with GraphQL `errors`.\n\n```ts\ntype MutationFn = (variables: Variables) => Promise<MutationData>;\n```\n\n### Mutation query dependency\n\n**Important** to notice that to update the cache `update` and `optimistUpdate` hooks depend on a `query` and it's `variables` object props (they need to be passed in the `props` object argument). If you need to perform a mutation but updating the cache is not strictly important you can just use the mutation promise resolved data or use the client instance directly.\n\n### `update`\n\n```ts\ntype UpdateFn = (query: QueryData, data: MutationData) => CacheUpdate;\n```\n\nThe mutation `update` function is resposible to update the cache when the request is completed. It receives as paremeters an object containing the data from the query it depends upon and the mutation result. `update` return type is an object that describes the changes to be made to the cache.\n\n### `optimisticUpdate`\n\n```ts\ntype OptimistcUpdateFn = (query: QueryData, variables: Variables) => CacheUpdate;\n```\n\nIn modern UIs it's to be expected that every user interaction occur in a fraction of seconds. `optimisticUpdate` responsability is to skip the mutation network roundtrip and update the cache instantaneously, making sure such interactions are as fast as they can be. `optimisticUpdate` as in `update` takes as first paremater the depedent query data. As second paremater it receives the variables object with which it's correpondent generated mutation function was called. And again it should return an object that describes the changes to be made to cache.\n\nIf you want to perform an optimitic update you have to make sure that the data you are inserting contains the field or fields to extract a unique identifier. For instance, say `@grafoo/babel-plugin` `idFields` option is set to insert a property `id`. Is to be expected that your update has that field mocked.\n\n### Bindings\n\nThe object returned by `createBindings` contains the following props.\n\n| Name    | type     | Descrition                                                   |\n| ------- | -------- | ------------------------------------------------------------ |\n| client  | object   | the client instance                                          |\n| load    | function | a method to execute a query with the `query` prop            |\n| loading | boolean  | whether the client is executing a query or not               |\n| loaded  | boolean  | whether the query data is already cached                     |\n| errors  | string[] | an array of GraphQL errors from a failed request to your API |\n\nThe remaining props are:\n\n- the data fetched by the client and shaped according to your `query`\n- mutation functions generated by the `mutations` object prop\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "packages/bindings/schema.graphql",
    "content": "type Query {\n  author(id: ID!): Author!\n  authors: [Author!]!\n  post(id: ID!): Post!\n  posts: [Post!]!\n}\n\ntype Mutation {\n  createAuthor(name: String!): Author!\n  updateAuthor(id: ID!, name: String): Author!\n  deleteAuthor(id: ID!): Author!\n  createPost(title: String!, body: String!, author: ID!): Post!\n  updatePost(id: ID!, title: String, body: String): Post!\n  deletePost(id: ID!): Post!\n}\n\ntype Author {\n  id: ID!\n  name: String!\n  posts: [Post!]\n}\n\ntype Post {\n  id: ID!\n  title: String!\n  body: String!\n  author: Author!\n}\n"
  },
  {
    "path": "packages/bindings/src/index.ts",
    "content": "import {\n  GrafooClient,\n  GrafooBindings,\n  GrafooBoundMutations,\n  GrafooConsumerProps,\n  ObjectsMap,\n  Variables,\n} from \"@grafoo/types\";\n\nexport default function createBindings<T = unknown, U = unknown>(\n  client: GrafooClient,\n  props: GrafooConsumerProps<T, U>,\n  updater: () => void\n): GrafooBindings<T, U> {\n  let { variables } = props;\n  let data: T;\n  let objects: ObjectsMap;\n  let boundMutations = {} as GrafooBoundMutations<U>;\n  let unbind = () => {};\n  let lockListenUpdate = 0;\n  let loaded = false;\n  let partial = false;\n\n  if (props.query) {\n    ({ data, objects, partial } = client.read<T>(props.query, variables));\n\n    loaded = !!data && !partial;\n\n    unbind = client.listen((nextObjects) => {\n      if (lockListenUpdate) return (lockListenUpdate = 0);\n\n      objects = objects || {};\n\n      for (let i in nextObjects) {\n        // object has been inserted\n        if (!(i in objects)) return performUpdate();\n\n        for (let j in nextObjects[i]) {\n          // object has been updated\n          if (nextObjects[i][j] !== objects[i][j]) return performUpdate();\n        }\n      }\n\n      for (let i in objects) {\n        // object has been removed\n        if (!(i in nextObjects)) return performUpdate();\n      }\n    });\n  }\n\n  let boundState = props.query ? { load, loaded, loading: !props.skip && !loaded } : {};\n\n  if (props.mutations) {\n    for (let key in props.mutations) {\n      let { update, optimisticUpdate, query: mutationQuery } = props.mutations[key];\n\n      boundMutations[key] = (mutationVariables) => {\n        if (props.query && optimisticUpdate) {\n          writeToCache(optimisticUpdate(data, mutationVariables));\n        }\n\n        return client\n          .execute<U[typeof key]>(mutationQuery, mutationVariables)\n          .then((mutationResponse) => {\n            if (props.query && update && mutationResponse.data) {\n              writeToCache(update(data, mutationResponse.data));\n            }\n\n            return mutationResponse;\n          });\n      };\n    }\n  }\n\n  function writeToCache(dataUpdate: T) {\n    client.write(props.query, variables, dataUpdate);\n  }\n\n  function performUpdate(boundStateUpdate?) {\n    ({ data, objects } = client.read<T>(props.query, variables));\n\n    Object.assign(boundState, boundStateUpdate);\n\n    updater();\n  }\n\n  function getState() {\n    return Object.assign({ client }, boundState, boundMutations, data);\n  }\n\n  function load(nextVariables?: Variables) {\n    if (nextVariables) {\n      variables = nextVariables;\n    }\n\n    if (!boundState.loading) {\n      Object.assign(boundState, { loading: true });\n\n      updater();\n    }\n\n    return client.execute<T>(props.query, variables).then(({ data, errors }) => {\n      if (data) {\n        lockListenUpdate = 1;\n\n        writeToCache(data);\n      }\n\n      performUpdate({ errors, loaded: !!data, loading: false });\n    });\n  }\n\n  return { getState, unbind, load };\n}\n"
  },
  {
    "path": "packages/bindings/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"strict\": false,\n    \"lib\": [\"esnext\", \"dom\"],\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"checkJs\": false,\n    \"downlevelIteration\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/bundle/cli.js",
    "content": "#!/usr/bin/env node\n\n/* eslint-disable no-console */\n\nvar mri = require(\"mri\");\nvar build = require(\".\");\n\nvar opts = mri(process.argv.slice(2));\n\nopts.skipCompression = !!opts[\"skip-compression\"];\nopts.rootPath = process.cwd();\n\nbuild(opts).catch(console.error);\n"
  },
  {
    "path": "packages/bundle/index.js",
    "content": "var fs = require(\"fs\");\nvar path = require(\"path\");\nvar rollup = require(\"rollup\").rollup;\nvar buble = require(\"rollup-plugin-buble\");\nvar fileSize = require(\"rollup-plugin-filesize\");\nvar nodeResolve = require(\"rollup-plugin-node-resolve\");\nvar terser = require(\"rollup-plugin-terser\").terser;\nvar typescript = require(\"rollup-plugin-typescript2\");\nvar ts = require(\"typescript\");\n\nmodule.exports = function build(opts) {\n  var pkg = JSON.parse(fs.readFileSync(path.join(opts.rootPath, \"package.json\"), \"utf-8\"));\n  var tsconfig = JSON.parse(fs.readFileSync(path.join(opts.rootPath, \"tsconfig.json\"), \"utf-8\"));\n  var peerDependencies = pkg.peerDependencies || {};\n\n  tsconfig.compilerOptions.target = \"esnext\";\n  tsconfig.compilerOptions.module = \"esnext\";\n  tsconfig.compilerOptions.declaration = true;\n  tsconfig.compilerOptions.outDir = path.join(opts.rootPath, \"dist\");\n\n  return rollup({\n    input: path.join(opts.rootPath, opts.input),\n    external: Object.keys(peerDependencies),\n    sourcemap: true,\n    plugins: [\n      nodeResolve(),\n      typescript({\n        typescript: ts,\n        tsconfigOverride: tsconfig,\n      }),\n      buble({\n        transforms: {\n          dangerousForOf: true,\n          dangerousTaggedTemplateString: true,\n        },\n      }),\n      !opts.skipCompression &&\n        terser({\n          output: { comments: false },\n          compress: { keep_infinity: true, pure_getters: true },\n          warnings: true,\n          toplevel: true,\n          mangle: {},\n        }),\n      fileSize(),\n    ].filter(Boolean),\n  }).then(function (bundle) {\n    return bundle.write({\n      file: path.join(opts.rootPath, \"dist/index.js\"),\n      sourcemap: true,\n      format: opts.format || \"esm\",\n      treeshake: {\n        propertyReadSideEffects: false,\n      },\n    });\n  });\n};\n"
  },
  {
    "path": "packages/bundle/package.json",
    "content": "{\n  \"name\": \"grafoo-bundle\",\n  \"version\": \"1.4.2\",\n  \"bin\": \"cli.js\",\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"mri\": \"^1.1.1\",\n    \"rollup\": \"^2.34.2\",\n    \"rollup-plugin-buble\": \"^0.19.2\",\n    \"rollup-plugin-filesize\": \"^9.1.0\",\n    \"rollup-plugin-node-resolve\": \"^5.2.0\",\n    \"rollup-plugin-terser\": \"^7.0.2\",\n    \"rollup-plugin-typescript2\": \"^0.30.0\",\n    \"typescript\": \"^4.1.2\"\n  },\n  \"gitHead\": \"0bc67d8b398884a1f387a1813e485d2c5318b974\"\n}\n"
  },
  {
    "path": "packages/bundle/readme.md",
    "content": "# `grafoo-bundle`\n\n**This is and internal cli tool for [Grafoo](https://github.com/grafoojs/grafoo) and it's not meant to be used for anything else**. Basicaly a wrapper around rollup with some configuration already set.\n\n## Usage\n\n```\n$ grafoo-bundle --input src/index.ts\n```\n\n## Options\n\n```sh\n--input            # the entrypoint\n--skip-compression # avoids minification\n```\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "packages/core/.babelrc",
    "content": "{\n  \"presets\": [\n    [\"@babel/preset-env\", { \"targets\": { \"node\": \"current\" } }],\n    \"@babel/preset-typescript\"\n  ],\n  \"plugins\": [\n    [\n      \"module:@grafoo/babel-plugin\",\n      { \"schema\": \"schema.graphql\", \"idFields\": [\"id\", \"__typename\"] }\n    ]\n  ]\n}\n"
  },
  {
    "path": "packages/core/.npmignore",
    "content": "coverage\n__tests__\n.rpt2_cache\n.babelrc\nschema.graphql\n"
  },
  {
    "path": "packages/core/__tests__/build-query-tree.ts",
    "content": "import buildQueryTree from \"../src/build-query-tree\";\n\nlet tree = {\n  posts: [\n    {\n      title: \"foo\",\n      id: \"1\",\n      author: {\n        name: \"miguel\",\n        id: \"2\",\n        posts: [\n          {\n            id: \"1\",\n            content: \"a post content\",\n            author: {\n              name: \"miguel\",\n              lastName: \"albernaz\",\n              id: \"2\",\n            },\n          },\n        ],\n      },\n    },\n    { title: \"bar\", id: \"3\", author: { name: \"vicente\", id: \"4\" } },\n    { title: \"baz\", id: \"5\", author: { name: \"laura\", id: \"6\" } },\n  ],\n};\n\nlet idFields = [\"id\"];\n\ndescribe(\"build-query-tree\", () => {\n  it(\"should update values of a resulting query tree\", () => {\n    let objects = {\n      \"1\": { title: \"foobar\", id: \"1\", content: \"a new post content\" },\n      \"2\": { name: \"miguel\", id: \"2\", lastName: \"coelho\" },\n    };\n\n    let { posts } = buildQueryTree(tree, objects, idFields);\n\n    expect(posts[0].title).toBe(\"foobar\");\n    expect(posts[0].content).toBe(\"a new post content\");\n    expect(posts[0].author.lastName).toBe(\"coelho\");\n  });\n\n  it(\"should add all properties of an object to its corresponding branch\", () => {\n    let objects = {\n      \"1\": { title: \"foo\", id: \"1\", content: \"a post content\" },\n      \"2\": { name: \"miguel\", id: \"2\", lastName: \"coelho\" },\n    };\n\n    let [post] = buildQueryTree(tree, objects, idFields).posts;\n\n    expect(post.content).toBeTruthy();\n    expect(post.author.lastName).toBeTruthy();\n    expect(post.author.posts[0].title).toBeTruthy();\n  });\n\n  it(\"should not remove a property from a branch\", () => {\n    let objects = {\n      \"1\": { id: \"1\" },\n      \"2\": { id: \"2\" },\n      \"3\": { id: \"3\" },\n      \"4\": { id: \"4\" },\n      \"5\": { id: \"5\" },\n      \"6\": { id: \"6\" },\n    };\n\n    let newTree = buildQueryTree(tree, objects, idFields);\n\n    expect(newTree).toEqual(tree);\n  });\n});\n"
  },
  {
    "path": "packages/core/__tests__/index.ts",
    "content": "import graphql from \"@grafoo/core/tag\";\nimport { executeQuery } from \"@grafoo/test-utils\";\nimport { GrafooClient, Variables } from \"@grafoo/types\";\nimport createClient from \"../src\";\n\ninterface Post {\n  title: string;\n  content: string;\n  id: string;\n  __typename: string;\n  author: Author;\n}\n\ninterface Author {\n  name: string;\n  id: string;\n  __typename: string;\n  posts?: Array<Post>;\n}\n\ninterface AuthorsQuery {\n  authors: Author[];\n}\n\ninterface PostQuery {\n  post: Post;\n}\n\ninterface PostsAndAuthorsQuery {\n  authors: Author[];\n  posts: Post[];\n}\n\ninterface PostsQuery {\n  posts: Post[];\n}\n\nlet AUTHORS = graphql`\n  query {\n    authors {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet SIMPLE_AUTHORS = graphql`\n  query {\n    authors {\n      name\n    }\n  }\n`;\n\nlet POSTS_AND_AUTHORS = graphql`\n  query {\n    posts {\n      title\n      body\n      author {\n        name\n      }\n    }\n\n    authors {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet POST = graphql`\n  query ($postId: ID!) {\n    post(id: $postId) {\n      title\n      body\n      author {\n        name\n      }\n    }\n  }\n`;\n\nlet POST_WITH_FRAGMENT = graphql`\n  query ($postId: ID!) {\n    post(id: $postId) {\n      title\n      body\n      author {\n        ...AuthorInfo\n      }\n    }\n  }\n\n  fragment AuthorInfo on Author {\n    name\n  }\n`;\n\nlet POSTS = graphql`\n  query {\n    posts {\n      title\n      body\n      author {\n        name\n      }\n    }\n  }\n`;\n\nfunction mockTrasport<T>(query: string, variables: Variables) {\n  return executeQuery<T>({ query, variables });\n}\n\ndescribe(\"@grafoo/core\", () => {\n  let client: GrafooClient;\n  beforeEach(() => {\n    client = createClient(mockTrasport, { idFields: [\"id\"] });\n  });\n\n  it(\"should be instantiable\", () => {\n    expect(() => createClient(mockTrasport, { idFields: [\"id\"] })).not.toThrow();\n    expect(typeof client.execute).toBe(\"function\");\n    expect(typeof client.listen).toBe(\"function\");\n    expect(typeof client.write).toBe(\"function\");\n    expect(typeof client.read).toBe(\"function\");\n    expect(typeof client.flush).toBe(\"function\");\n    expect(typeof client.reset).toBe(\"function\");\n  });\n\n  it(\"should perform query requests\", async () => {\n    let variables = { postId: \"2c969ce7-02ae-42b1-a94d-7d0a38804c85\" };\n\n    let { query, frags } = POST_WITH_FRAGMENT;\n    if (frags) for (let frag in frags) query += \" \" + frags[frag];\n\n    let data = await executeQuery({ query, variables });\n\n    expect(data).toEqual(await client.execute(POST_WITH_FRAGMENT, variables));\n  });\n\n  it(\"should perform query requests with fragments\", async () => {\n    let data = await executeQuery({ query: SIMPLE_AUTHORS.query });\n\n    expect(data).toEqual(await client.execute(SIMPLE_AUTHORS));\n  });\n\n  it(\"should write queries to the client\", async () => {\n    let data = await executeQuery<PostsAndAuthorsQuery>(POSTS_AND_AUTHORS);\n\n    client.write(POSTS_AND_AUTHORS, data);\n\n    let { authors, posts } = data.data;\n    let { objectsMap, pathsMap } = client.flush();\n\n    expect(authors).toEqual(\n      pathsMap[\"authors{__typename id name posts{__typename body id title}}\"].data.authors\n    );\n    expect(posts).toEqual(\n      pathsMap[\"posts{__typename author{__typename id name}body id title}\"].data.posts\n    );\n    expect(authors.every((author) => Boolean(objectsMap[author.id]))).toBe(true);\n    expect(posts.every((post) => Boolean(objectsMap[post.id]))).toBe(true);\n  });\n\n  it(\"should write queries partially to the client\", async () => {\n    let { data } = await executeQuery<PostsQuery>(POSTS);\n\n    expect(() => client.write(POSTS_AND_AUTHORS, data)).not.toThrow();\n    expect(() => client.read(POSTS)).not.toThrow();\n    expect(() => client.read(AUTHORS)).not.toThrow();\n  });\n\n  it(\"should read queries from the client\", async () => {\n    let { data } = await executeQuery<AuthorsQuery>(AUTHORS);\n\n    client.write(AUTHORS, data);\n\n    let result = client.read<AuthorsQuery>(AUTHORS);\n\n    let { authors } = data;\n\n    expect(authors).toEqual(result.data.authors);\n    expect(authors.every((author) => Boolean(result.objects[author.id]))).toBe(true);\n    expect(\n      authors.every((author) => author.posts.every((post) => Boolean(result.objects[post.id])))\n    ).toBe(true);\n  });\n\n  it(\"should handle queries with variables\", async () => {\n    let variables = { postId: \"2c969ce7-02ae-42b1-a94d-7d0a38804c85\" };\n    let { data } = await executeQuery<PostQuery>({ query: POST.query, variables });\n\n    client.write(POST, variables, data);\n\n    expect(client.read(POST, { postId: \"123\" })).toEqual({});\n    expect(client.read<PostQuery>(POST, variables).data.post.id).toBe(variables.postId);\n  });\n\n  it(\"should distinguish between calls to the same query with different variables\", async () => {\n    let v1 = { postId: \"2c969ce7-02ae-42b1-a94d-7d0a38804c85\" };\n    let v2 = { postId: \"77c483dd-6529-4c72-9bb6-bbfd69f65682\" };\n\n    let { data: d1 } = await executeQuery<PostQuery>({ query: POST.query, variables: v1 });\n    client.write(POST, v1, d1);\n\n    expect(client.read(POST, { postId: \"not found\" })).toEqual({});\n    expect(client.read<PostQuery>(POST, v1).data.post.id).toBe(v1.postId);\n\n    let d2 = await executeQuery<PostQuery>({ query: POST.query, variables: v2 });\n    client.write(POST, v2, d2);\n\n    expect(client.read<PostQuery>(POST, v1).data.post.id).toBe(v1.postId);\n    expect(client.read<PostQuery>(POST, v2).data.post.id).toBe(v2.postId);\n  });\n\n  it(\"should flag if a query result is partial\", async () => {\n    let { data } = await executeQuery<PostsQuery>({ query: POSTS.query });\n\n    client.write(POSTS, data);\n\n    expect(client.read<PostsAndAuthorsQuery>(POSTS_AND_AUTHORS).partial).toBe(true);\n  });\n\n  it(\"should remove unused objects from objectsMap\", async () => {\n    let { data } = await executeQuery<AuthorsQuery>(SIMPLE_AUTHORS);\n\n    client.write(SIMPLE_AUTHORS, data);\n\n    let authorToBeRemoved: Author = data.authors[0];\n\n    let ids = Object.keys(client.flush().objectsMap);\n\n    expect(ids.some((id) => id === authorToBeRemoved.id)).toBe(true);\n\n    client.write(SIMPLE_AUTHORS, {\n      authors: data.authors.filter((author) => author.id !== authorToBeRemoved.id)\n    });\n\n    let nextIds = Object.keys(client.flush().objectsMap);\n\n    expect(nextIds.length).toBe(ids.length - 1);\n    expect(nextIds.some((id) => id === authorToBeRemoved.id)).toBe(false);\n  });\n\n  it(\"should perform update to client\", async () => {\n    let variables = { postId: \"2c969ce7-02ae-42b1-a94d-7d0a38804c85\" };\n    let { data } = await executeQuery<PostQuery>({ query: POST.query, variables });\n\n    client.write(POST, variables, data);\n\n    let {\n      data: { post }\n    } = client.read<PostQuery>(POST, variables);\n\n    expect(post.title).toBe(\"Quam odit\");\n\n    client.write(POST, variables, { post: { ...post, title: \"updated title\" } });\n\n    expect(client.read<PostQuery>(POST, variables).data.post.title).toBe(\"updated title\");\n  });\n\n  it(\"should reflect updates on queries with shared objects\", async () => {\n    let variables = { postId: \"2c969ce7-02ae-42b1-a94d-7d0a38804c85\" };\n    let postData = (await executeQuery<PostQuery>({ query: POST.query, variables })).data;\n    let postsData = (await executeQuery<PostsQuery>({ query: POSTS.query, variables })).data;\n\n    client.write(POSTS, postsData);\n\n    let { posts } = client.read<PostsQuery>(POSTS).data;\n\n    expect(posts.find((p) => p.id === variables.postId).title).toBe(\"Quam odit\");\n\n    client.write(POST, variables, { post: { ...postData.post, title: \"updated title\" } });\n\n    let { posts: updatedPosts } = client.read<PostsQuery>(POSTS, variables).data;\n\n    expect(updatedPosts.find((p) => p.id === variables.postId).title).toBe(\"updated title\");\n  });\n\n  it(\"should merge objects in the client when removing or adding properties\", async () => {\n    let variables = { postId: \"2c969ce7-02ae-42b1-a94d-7d0a38804c85\" };\n    let data = (await executeQuery<PostQuery>({ query: POST.query, variables })).data;\n\n    client.write(POST, variables, data);\n\n    let post = JSON.parse(JSON.stringify(client.read<PostQuery>(POST, variables).data.post));\n\n    delete post.__typename;\n\n    post.foo = \"bar\";\n\n    client.write(POST, variables, { post });\n\n    expect(client.read<PostQuery>(POST, variables).data.post).toEqual({\n      __typename: \"Post\",\n      author: {\n        __typename: \"Author\",\n        id: \"a1d3a2bc-e503-4640-9178-23cbd36b542c\",\n        name: \"Murphy Abshire\"\n      },\n      body: \"Ducimus harum delectus consectetur.\",\n      id: \"2c969ce7-02ae-42b1-a94d-7d0a38804c85\",\n      title: \"Quam odit\",\n      foo: \"bar\"\n    });\n  });\n\n  it(\"should call client listeners on write with paths objects as arguments\", async () => {\n    let variables = { postId: \"2c969ce7-02ae-42b1-a94d-7d0a38804c85\" };\n    let data = (await executeQuery<PostQuery>({ query: POST.query, variables })).data;\n\n    let listener = jest.fn();\n    let listener2 = jest.fn();\n\n    let unlisten = client.listen(listener);\n    client.listen(listener2);\n\n    client.write(POST, variables, data);\n\n    expect(listener).toHaveBeenCalledWith(client.read(POST, variables).objects);\n\n    unlisten();\n    client.write(POST, variables, data);\n\n    expect(listener).toHaveBeenCalledTimes(1);\n    expect(listener2).toHaveBeenCalledTimes(2);\n\n    unlisten();\n    client.write(POST, variables, data);\n\n    expect(listener2).toHaveBeenCalledTimes(3);\n  });\n\n  it(\"should be able read from the client with a declared initialState\", async () => {\n    let { data } = await executeQuery(POSTS_AND_AUTHORS);\n\n    client.write(POSTS_AND_AUTHORS, data);\n\n    client = createClient(mockTrasport, { idFields: [\"id\"], initialState: client.flush() });\n\n    expect(client.read(POSTS_AND_AUTHORS).data).toEqual(data);\n  });\n\n  it(\"should allow cache to be cleared using reset()\", () => {\n    let data = { authors: [{ name: \"deleteme\" }] };\n    client.write(SIMPLE_AUTHORS, data);\n    expect(client.read(SIMPLE_AUTHORS).data).toEqual(data);\n    client.reset();\n    expect(client.read(SIMPLE_AUTHORS).data).toEqual(undefined);\n    expect(client.flush()).toEqual({\n      objectsMap: {},\n      pathsMap: {}\n    });\n  });\n\n  it(\"should accept `idFields` array in options\", async () => {\n    let { data } = await executeQuery(AUTHORS);\n\n    let client = createClient(mockTrasport, { idFields: [\"__typename\", \"id\"] });\n\n    client.write(AUTHORS, data);\n\n    let cachedIds = Object.keys(client.flush().objectsMap);\n\n    expect(cachedIds.every((key) => /(Post|Author)/.test(key))).toBe(true);\n  });\n});\n"
  },
  {
    "path": "packages/core/__tests__/map-objects.ts",
    "content": "import mapObjects from \"../src/map-objects\";\n\nlet tree = {\n  posts: [\n    {\n      title: \"foo\",\n      id: \"1\",\n      __typename: \"Post\",\n      author: {\n        name: \"miguel\",\n        id: \"2\",\n        __typename: \"Author\",\n        posts: [\n          {\n            title: \"foo\",\n            id: \"1\",\n            __typename: \"Post\",\n            content: \"a post content\",\n            author: {\n              name: \"miguel\",\n              lastName: \"albernaz\",\n              id: \"2\",\n              __typename: \"Author\",\n            },\n          },\n        ],\n      },\n    },\n    {\n      title: \"bar\",\n      id: \"3\",\n      __typename: \"Post\",\n      author: { name: \"vicente\", id: \"4\", __typename: \"Author\" },\n    },\n    {\n      title: \"baz\",\n      id: \"5\",\n      __typename: \"Post\",\n      author: { name: \"laura\", id: \"6\", __typename: \"Author\" },\n    },\n  ],\n};\n\nlet idFields = [\"id\"];\n\ndescribe(\"map-objects\", () => {\n  it(\"should return the correct map of objects\", () => {\n    let objects = mapObjects(tree, idFields);\n\n    let expected = {\n      \"1\": { title: \"foo\", id: \"1\", __typename: \"Post\", content: \"a post content\" },\n      \"2\": { name: \"miguel\", id: \"2\", __typename: \"Author\", lastName: \"albernaz\" },\n      \"3\": { title: \"bar\", __typename: \"Post\", id: \"3\" },\n      \"4\": { name: \"vicente\", id: \"4\", __typename: \"Author\" },\n      \"5\": { title: \"baz\", __typename: \"Post\", id: \"5\" },\n      \"6\": { name: \"laura\", id: \"6\", __typename: \"Author\" },\n    };\n\n    expect(objects).toEqual(expected);\n  });\n\n  it(\"should accept null values\", () => {\n    let result = {\n      data: {\n        me: {\n          id: \"5a3ab7e93f662a108d978a6e\",\n          username: \"malbernaz\",\n          email: \"albernazmiguel@gmail.com\",\n          name: null,\n          bio: null,\n        },\n      },\n    };\n\n    expect(() => mapObjects(result, idFields)).not.toThrow();\n  });\n\n  it(\"should build an object identifier based on the `idFields` cache option\", () => {\n    let idFields = [\"__typename\", \"id\"];\n\n    let objects = mapObjects(tree, idFields);\n\n    let expected = [\"Post1\", \"Author2\", \"Post3\", \"Author4\", \"Post5\", \"Author6\"];\n\n    expect(Object.keys(objects).every((obj) => expected.some((exp) => exp === obj))).toBe(true);\n  });\n});\n"
  },
  {
    "path": "packages/core/__tests__/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig\",\n  \"include\": [\".\"]\n}\n"
  },
  {
    "path": "packages/core/package.json",
    "content": "{\n  \"name\": \"@grafoo/core\",\n  \"version\": \"1.4.2\",\n  \"description\": \"grafoo client core\",\n  \"repository\": \"https://github.com/grafoojs/grafoo/tree/master/packages/core\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"author\": \"malbernaz<albernazmiguel@gmail.com>\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"babel\",\n    \"babel-plugin\",\n    \"graphql\",\n    \"graphql-client\",\n    \"grafoo\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"grafoo-bundle --input src/index.ts\",\n    \"test\": \"jest\",\n    \"test:coverage\": \"jest --coverage\"\n  },\n  \"jest\": {\n    \"transform\": {\n      \"^.+\\\\.(ts|tsx|js)$\": \"<rootDir>/../../scripts/jest-setup.js\"\n    },\n    \"resolver\": \"<rootDir>/../../scripts/resolver.js\",\n    \"transformIgnorePatterns\": [\n      \"node_modules/(?!(lowdb|steno|node-fetch|fetch-blob)/)\"\n    ]\n  },\n  \"dependencies\": {\n    \"@grafoo/types\": \"^1.4.2\"\n  },\n  \"gitHead\": \"0bc67d8b398884a1f387a1813e485d2c5318b974\"\n}\n"
  },
  {
    "path": "packages/core/readme.md",
    "content": "# `@grafoo/core`\n\n<p><i>Grafoo core</i></p>\n\n<p>\n  <a href=https://circleci.com/gh/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/circleci/project/github/grafoojs/grafoo/master.svg?label=build\n      alt=build\n    />\n  </a>\n  <a href=https://codecov.io/github/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/codecov/c/github/grafoojs/grafoo/master.svg\n      alt=\"coverage\"\n    />\n  </a>\n  <a href=https://github.com/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/npm/v/@grafoo/core.svg\n      alt=npm\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/core>\n    <img\n      src=https://img.shields.io/npm/dm/@grafoo/core.svg\n      alt=downloads\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/core>\n    <img\n      src=https://img.shields.io/bundlephobia/minzip/@grafoo/core.svg?label=size\n      alt=size\n    >\n  </a>\n  <a href=https://prettier.io>\n    <img\n      src=https://img.shields.io/badge/code_style-prettier-ff69b4.svg\n      alt=\"code style: prettier\"\n    />\n  </a>\n  <a href=https://lernajs.io>\n    <img\n      src=https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg\n      alt=\"mantained with: lerna\"\n    />\n  </a>\n  <a href=https://grafoo-slack.herokuapp.com>\n    <img\n      src=https://grafoo-slack.herokuapp.com/badge.svg\n      alt=\"slack\"\n    />\n  </a>\n</p>\n\n## Install\n\n```\n$ npm i @grafoo/core && npm i -D @grafoo/babel-plugin\n```\n\n## Setup\n\nAssuming you already have babel installed, the only additional step required to build an application with Grafoo is to configure [`@grafoo/babel-plugin`](https://github.com/grafoojs/grafoo/tree/master/packages/babel-plugin). The options it accepts are `idFields` - the fields Grafoo will take to build unique identifiers, and `schema`, which is a relative path to your schema file.\n\n```json\n{\n  \"plugins\": [\n    [\n      \"@grafoo/babel-plugin\",\n      {\n        \"schema\": \"schema.graphql\",\n        \"idFields\": [\"id\"]\n      }\n    ]\n  ]\n}\n```\n\n## API\n\n`@grafoo/core` consists of a module that exports as default function a factory to create the client intance and a submodule that exports that `graphql` template tag.\n\n### `graphql` template tag\n\nFrom `@grafoo/core/tag` is exported the `graphql` or `gql` tag that you'll use to create your queries. On build time every time you use that tag it will be replace with a special object that assists the client on the caching process. It is a dummy module and if you do not have `@grafoo/babel-plugin` it will thow you an error.\n\n#### Example\n\n```js\nimport gql from \"@grafoo/core/tag\";\n\nconst USER_QUERY = gql`\n  query($id: ID!) {\n    user(id: $id) {\n      name\n    }\n  }\n`;\n\n// will be transformed to this on build time\n\nconst USER_QUERY = {\n  query: \"query($id: ID!) { user(id: $id) { name id } }\"\n  paths: {\n    \"user(id:$id){name id}\": {\n      name: \"user\",\n      args: [\"id\"]\n    }\n  }\n}\n```\n\n### `createClient` factory\n\n`createClient` accepts as arguments a `transport` function to comunicate with your GraphQL API and an options object. This options are:\n\n| Option       | Type     | Required | Description                                                                           |\n| ------------ | -------- | -------- | ------------------------------------------------------------------------------------- |\n| idFields     | string[] | false    | fields Grafoo takes to build unique identifiers                                       |\n| initialState | object   | false    | a initial state to hydrate the cache. It can be produced by the `flush` client method |\n\n#### Example\n\n```js\nimport createClient from \"@grafoo/core\";\n\nfunction fetchQuery(query, variables) {\n  const init = {\n    method: \"POST\",\n    body: JSON.stringify({ query, variables }),\n    headers: {\n      \"content-type\": \"application/json\"\n    }\n  };\n\n  return fetch(\"http://some.graphql.api\", init).then(res => res.json());\n}\n\nconst client = createClient(fetchQuery);\n```\n\n### IdFields\n\n`IdFields` is homologous to the `@grafoo/babel-plugin` option with the same name. You don't have much to worry about it because it's **automatically inserted by `@grafoo/babel-plugin`** on every client instantiation. It is an array of fields that Grafoo will take to build unique identifiers.\n\nSay you want to consume a query like so:\n\n```graphql\n{\n  me {\n    name\n  }\n}\n```\n\nIf `idFields` is configured with `[\"id\"]`. This query will be transformed to this:\n\n```graphql\n{\n  me {\n    name\n    id\n  }\n}\n```\n\nThen the client, when caching this data, will use this `id` field to store it.\n\n#### Example\n\n```js\nconst client = createClient(fetchQuery, {\n  idFields: [\"id\", \"__typename\"]\n});\n```\n\n## `GrafooClient`\n\nthe `createClient` factory returns a client instance with some methods:\n\n| Name    | Description                                            |\n| ------- | ------------------------------------------------------ |\n| execute | executes queries                                       |\n| read    | reads queries from the cache                           |\n| write   | writes queries to the cache                            |\n| listen  | takes a listener callback and notify for cache changes |\n| flush   | dumps the internal state of the instance cache         |\n\n### `GrafooClient.execute`\n\nThis method receives as arguments a query object created with the `@grafoo/core/tag` template tag and optionally a GraphQL variables object. It returns a promise that will resolve with the data requested or reject with a list of GraphQL errors.\n\n#### Example\n\n```js\nconst variables = { id: 123 };\n\nclient.execute(USER_QUERY, variables).then(data => {\n  console.log(data); // { \"user\": { \"name\": \"John Doe\", \"id\": \"123\" } }\n});\n```\n\n### `GrafooClient.write`\n\nThe write method as the name implies writes to the cache. It takes as argumets the query object, an optional variables object and the data to be stored.\n\n#### Example\n\n```js\nclient.execute(USER_QUERY, variables).then(data => {\n  client.write(USER_QUERY, variables, data);\n});\n```\n\n### `GrafooClient.read`\n\nThe read method takes as arguments the query object and optionally a variables object. It returns an object with three properties: `data`, a tree structured object shaped according to your query tree, `objects` a flat structured object containing every node on your query indexed by a unique id created with the `idProps` option passed on client instantiation and a `partial` property that flags if the data is partially cached or not.\n\n#### Example\n\n```js\nclient.read(USER_QUERY, variables);\n// {\n//   \"data\": {\n//     \"user\": {\n//       \"name\": \"John Doe\",\n//       \"id\": \"123\"\n//     }\n//   },\n//   \"objects\": {\n//     \"123\": {\n//       \"name\": \"John Doe\",\n//       \"id\": \"123\"\n//     }\n//   },\n//   partial: false\n// }\n```\n\n### `GrafooClient.listen`\n\n`listen` takes a _listener_ callback as argument. Whenever the cache is updated that _listener_ is called with the objects that were inserted, modified or removed.\n\n#### Example\n\n```js\nfunction listener(objects) {\n  console.log(objects);\n}\n\nconst unlisten = client.listen();\n\nclient.write(USER_QUERY, variables, data);\n\nunlisten(); // detaches the listener from the client\n```\n\n### `GrafooClient.flush`\n\nThe `flush` method dumps all of the data inside the cache in it's raw state, producing a snapshot. It is to be used in mainly on the server producing, a initial state that can be passed as an option to `createClient` on client side.\n\n#### Example\n\n```js\n// server.js\napp.get(\"/\", (req, res) => {\n  res.send(`<script>_GRAFOO_INITIAL_STATE_=${JSON.stringify(client.flush())}_</script>`);\n});\n\n// client.js\nconst client = createClient(fetchQuery, {\n  initialState: window._GRAFOO_INITIAL_STATE_\n});\n```\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "packages/core/schema.graphql",
    "content": "type Query {\n  author(id: ID!): Author!\n  authors: [Author!]!\n  post(id: ID!): Post!\n  posts: [Post!]!\n}\n\ntype Mutation {\n  createAuthor(name: String!): Author!\n  updateAuthor(id: ID!, name: String): Author!\n  deleteAuthor(id: ID!): Author!\n  createPost(title: String!, body: String!, author: ID!): Post!\n  updatePost(id: ID!, title: String, body: String): Post!\n  deletePost(id: ID!): Post!\n}\n\ntype Author {\n  id: ID!\n  name: String!\n  posts: [Post!]\n}\n\ntype Post {\n  id: ID!\n  title: String!\n  body: String!\n  author: Author!\n}\n"
  },
  {
    "path": "packages/core/src/build-query-tree.ts",
    "content": "import { idFromProps, isNotNullObject } from \"./util\";\n\nexport default function buildQueryTree(tree, objects, idFields) {\n  // clone resulting query tree\n  let queryTree = tree;\n  let stack = [];\n\n  // populates stack with the properties of the query tree and the query tree it self\n  for (let i in queryTree) stack.push([i, queryTree]);\n\n  // will loop until the stack is empty\n  while (stack.length) {\n    // pops a stack entry extracting the current key of the tree's branch\n    // (eg: a node or an edge) and the branch it self\n    let [key, currentTree] = stack.pop();\n    // assigns nested branch\n    let branch = currentTree[key];\n    // get node identifier\n    let identifier = idFromProps(branch, idFields);\n    // possible node matching object\n    let branchObject = objects[identifier];\n\n    // iterates over the child branch properties\n    for (let i in Object.assign({}, branch, branchObject)) {\n      // assigns to the child branch all properties retrieved\n      // from the corresponding object retrieved from the objects cache\n      if (identifier && branchObject) branch[i] = branchObject[i] || branch[i];\n\n      // pushes properties of the child branch and the branch it self to the stack\n      if (isNotNullObject(branch[i])) stack.push([i, branch]);\n    }\n  }\n\n  return queryTree;\n}\n"
  },
  {
    "path": "packages/core/src/index.ts",
    "content": "import {\n  GrafooClient,\n  GrafooClientOptions,\n  GrafooObject,\n  Listener,\n  ObjectsMap,\n  Variables,\n  GrafooTransport,\n} from \"@grafoo/types\";\nimport buildQueryTree from \"./build-query-tree\";\nimport mapObjects from \"./map-objects\";\nimport { getPathId } from \"./util\";\n\nexport default function createClient(\n  transport: GrafooTransport,\n  options?: GrafooClientOptions\n): GrafooClient {\n  let { initialState, idFields } = options;\n  let { pathsMap, objectsMap } = initialState || { pathsMap: {}, objectsMap: {} };\n  let listeners: Listener[] = [];\n\n  function execute<T>({ query, frags, id }: GrafooObject, variables?: Variables) {\n    if (frags) for (let frag in frags) query += frags[frag];\n\n    return transport<T>(query, variables, id);\n  }\n\n  function listen(listener: Listener) {\n    listeners.push(listener);\n\n    return () => {\n      let index = listeners.indexOf(listener);\n\n      if (index < 0) return;\n\n      listeners.splice(index, 1);\n    };\n  }\n\n  function write<T>({ paths }: GrafooObject, variables: Variables, data?: T | { data: T }) {\n    if (!data) {\n      data = variables as typeof data;\n      variables = undefined;\n    }\n\n    let objects: ObjectsMap = {};\n\n    for (let i in paths) {\n      let { name, args } = paths[i];\n      let pathData = {\n        [name]: (data as { data: T }).data ? (data as { data: T }).data[name] : data[name],\n      };\n      let pathObjects = mapObjects(pathData, idFields);\n\n      Object.assign(objects, pathObjects);\n\n      pathsMap[getPathId(i, args, variables)] = {\n        data: pathData,\n        objects: Object.keys(pathObjects),\n      };\n    }\n\n    // assign new values to objects in objectsMap\n    for (let i in objects) {\n      objectsMap[i] = objects[i] = Object.assign({}, objectsMap[i], objects[i]);\n    }\n\n    // clean cache\n    let pathsObjects = [];\n    for (let i in pathsMap) pathsObjects = pathsObjects.concat(pathsMap[i].objects);\n    let allObjects = new Set(pathsObjects);\n    for (let i in objectsMap) if (!allObjects.has(i)) delete objectsMap[i];\n\n    // run listeners\n    for (let i in listeners) listeners[i](objects);\n  }\n\n  function read({ paths }: GrafooObject, variables?: Variables) {\n    let data = {};\n    let objects: ObjectsMap = {};\n    let partial = false;\n\n    for (let i in paths) {\n      let { name, args } = paths[i];\n      let currentPath = pathsMap[getPathId(i, args, variables)];\n\n      if (currentPath) {\n        data[name] = currentPath.data[name];\n\n        for (let i of currentPath.objects) objects[i] = objectsMap[i];\n      } else {\n        partial = true;\n      }\n    }\n\n    return Object.keys(data).length\n      ? { data: buildQueryTree(data, objectsMap, idFields), objects, partial }\n      : {};\n  }\n\n  function flush() {\n    return { objectsMap, pathsMap };\n  }\n\n  function reset() {\n    pathsMap = {};\n    objectsMap = {};\n  }\n\n  return { execute, listen, write, read, flush, reset };\n}\n"
  },
  {
    "path": "packages/core/src/map-objects.ts",
    "content": "import { isNotNullObject, idFromProps } from \"./util\";\n\nexport default function mapObjects(tree, idFields) {\n  // map in which objects will be stored\n  // having their extracted ids from props as key\n  let map = {};\n  let stack = [];\n\n  // populates the stack with the tree branches\n  for (let i in tree) stack.push(tree[i]);\n\n  // will run until the stack is empty\n  while (stack.length) {\n    // pops the current branch from the stack\n    let branch = stack.pop();\n    // next node to be traversed. nested branches will be removed\n    let filteredBranch = {};\n\n    // iterate over branch properties\n    // if the property is a branch it will be added to the stack\n    // else if it is not a branch it will be added to filtered branch\n    for (let i in branch) {\n      let branchVal = branch[i];\n      (isNotNullObject(branchVal) && stack.push(branchVal)) || (filteredBranch[i] = branchVal);\n    }\n\n    // node identifier\n    let identifier = idFromProps(branch, idFields);\n\n    // if branch is a node, assign the value of filtered branch to it\n    if (identifier) map[identifier] = Object.assign({}, map[identifier], filteredBranch);\n  }\n\n  return map;\n}\n"
  },
  {
    "path": "packages/core/src/util.ts",
    "content": "import { Variables } from \"@grafoo/types\";\n\nexport let idFromProps = (branch, idFields) => {\n  branch = branch || {};\n  let identifier = \"\";\n\n  for (let i = 0; i < idFields.length; i++) {\n    branch[idFields[i]] && (identifier += branch[idFields[i]]);\n  }\n\n  return identifier;\n};\n\nexport let isNotNullObject = (obj) => obj && typeof obj == \"object\";\n\nexport let getPathId = (path: string, args: string[], variables?: Variables) => {\n  variables = variables || {};\n  let finalPath = path;\n  let i = args.length;\n\n  while (i--) finalPath += \":\" + variables[args[i]];\n\n  return finalPath;\n};\n"
  },
  {
    "path": "packages/core/tag.d.ts",
    "content": "declare module \"@grafoo/core/tag\" {\n  import { GrafooObject } from \"@grafoo/types\";\n\n  export default function graphql(strs: TemplateStringsArray): GrafooObject;\n}\n"
  },
  {
    "path": "packages/core/tag.js",
    "content": "function graphql() {\n  throw new Error(\n    \"@grafoo/core/tag: if you are getting this error it means your queries are not being transpiled\"\n  );\n}\n\nmodule.exports = graphql;\nmodule.exports.default = graphql;\n"
  },
  {
    "path": "packages/core/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"strict\": false,\n    \"lib\": [\"esnext\", \"dom\"],\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"checkJs\": false,\n    \"downlevelIteration\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/http-transport/.babelrc",
    "content": "{\n  \"presets\": [\n    [\"@babel/preset-env\", { \"targets\": { \"node\": \"current\" } }],\n    \"@babel/preset-typescript\"\n  ]\n}\n"
  },
  {
    "path": "packages/http-transport/.npmignore",
    "content": "coverage\n__tests__\n.rpt2_cache\n.babelrc\nschema.graphql\n"
  },
  {
    "path": "packages/http-transport/__tests__/index.ts",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\n\nimport { GrafooTransport } from \"@grafoo/types\";\nimport createTransport from \"../src\";\n\njest.mock(\"node-fetch\", () => require(\"fetch-mock\").sandbox());\nlet fetchMock = require(\"node-fetch\");\nglobal.fetch = fetchMock;\n\nlet fakeAPI = \"http://fake-api.com/graphql\";\nlet query = \"{ hello }\";\n\ndescribe(\"@grafoo/http-transport\", () => {\n  let request: GrafooTransport;\n  beforeEach(() => {\n    request = createTransport(fakeAPI);\n    fetchMock.restore();\n  });\n\n  it(\"should perform a simple request\", async () => {\n    await mock(async () => {\n      await request(query);\n\n      let [, { body, headers, method }] = fetchMock.lastCall();\n\n      expect(method).toBe(\"POST\");\n      expect(body).toBe(JSON.stringify({ query }));\n      expect(headers).toEqual({ \"Content-Type\": \"application/json\" });\n    });\n  });\n\n  it(\"should perform a request with variables\", async () => {\n    await mock(async () => {\n      let variables = { some: \"variable\" };\n\n      await request(query, variables);\n\n      let [, { body }] = fetchMock.lastCall();\n\n      expect(JSON.parse(body as string).variables).toEqual(variables);\n    });\n  });\n\n  it(\"should accept fetchObjects as an object\", async () => {\n    request = createTransport(fakeAPI, { headers: { authorization: \"Bearer some-token\" } });\n\n    await mock(async () => {\n      await request(query);\n\n      let [, { headers }] = fetchMock.lastCall();\n\n      expect(headers).toEqual({\n        authorization: \"Bearer some-token\",\n        \"Content-Type\": \"application/json\"\n      });\n    });\n  });\n\n  it(\"should accept fetchObjects as a function\", async () => {\n    request = createTransport(fakeAPI, () => ({ headers: { authorization: \"Bearer some-token\" } }));\n\n    await mock(async () => {\n      await request(query);\n\n      let [, { headers }] = fetchMock.lastCall();\n\n      expect(headers).toEqual({\n        authorization: \"Bearer some-token\",\n        \"Content-Type\": \"application/json\"\n      });\n    });\n  });\n\n  it(\"should handle graphql errors\", async () => {\n    let response = { data: null, errors: [{ message: \"I AM ERROR!\" }] };\n\n    await mock(\n      async () => expect(request(query)).resolves.toMatchObject({ errors: response.errors }),\n      response\n    );\n  });\n});\n\nasync function mock(testFn, response?: any) {\n  fetchMock.mock(fakeAPI, response || { data: { hello: \"world\" } });\n\n  await testFn();\n}\n"
  },
  {
    "path": "packages/http-transport/__tests__/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig\",\n  \"include\": [\".\"]\n}\n"
  },
  {
    "path": "packages/http-transport/package.json",
    "content": "{\n  \"name\": \"@grafoo/http-transport\",\n  \"description\": \"grafoo client standard transport\",\n  \"version\": \"1.4.2\",\n  \"repository\": \"https://github.com/grafoojs/grafoo/tree/master/packages/transport\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"author\": \"malbernaz<albernazmiguel@gmail.com>\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"graphql\",\n    \"graphql-client\",\n    \"grafoo\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"grafoo-bundle --input src/index.ts\",\n    \"test\": \"jest\",\n    \"test:coverage\": \"jest --coverage\"\n  },\n  \"jest\": {\n    \"transform\": {\n      \"^.+\\\\.(ts|tsx|js)$\": \"<rootDir>/../../scripts/jest-setup.js\"\n    },\n    \"resolver\": \"<rootDir>/../../scripts/resolver.js\",\n    \"transformIgnorePatterns\": [\n      \"node_modules/(?!(lowdb|steno|node-fetch|fetch-blob)/)\"\n    ]\n  },\n  \"dependencies\": {\n    \"@grafoo/types\": \"^1.4.2\",\n    \"grafoo-bundle\": \"^1.4.2\"\n  },\n  \"gitHead\": \"0bc67d8b398884a1f387a1813e485d2c5318b974\"\n}\n"
  },
  {
    "path": "packages/http-transport/readme.md",
    "content": "# `@grafoo/http-transport`\n\n<p><i>A Simple HTTP Client for GraphQL Servers</i></p>\n\n<p>\n  <a href=https://circleci.com/gh/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/circleci/project/github/grafoojs/grafoo/master.svg?label=build\n      alt=build\n    />\n  </a>\n  <a href=https://codecov.io/github/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/codecov/c/github/grafoojs/grafoo/master.svg\n      alt=\"coverage\"\n    />\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/http-transport>\n    <img\n      src=https://img.shields.io/npm/v/@grafoo/http-transport.svg\n      alt=npm\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/http-transport>\n    <img\n      src=https://img.shields.io/npm/dm/@grafoo/http-transport.svg\n      alt=downloads\n    >\n  </a>\n  <a href=https://github.com/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/bundlephobia/minzip/@grafoo/http-transport.svg?label=size\n      alt=size\n    >\n  </a>\n  <a href=https://prettier.io>\n    <img\n      src=https://img.shields.io/badge/code_style-prettier-ff69b4.svg\n      alt=\"code style: prettier\"\n    />\n  </a>\n  <a href=https://lernajs.io>\n    <img\n      src=https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg\n      alt=\"mantained with: lerna\"\n    />\n  </a>\n  <a href=https://grafoo-slack.herokuapp.com>\n    <img\n      src=https://grafoo-slack.herokuapp.com/badge.svg\n      alt=\"slack\"\n    />\n  </a>\n</p>\n\n## Install\n\n```\n$ npm i @grafoo/http-transport\n```\n\n## Usage\n\n`@grafoo/http-transport` default export is a factory that accepts as arguments `uri` and `fetchOptions` (that can be an object or a function):\n\n```js\nimport createTransport from \"@grafoo/http-transport\";\n\nconst request = createTransport(\"http://some.graphql.api\", () => ({\n  headers: {\n    authorization: storage.getItem(\"authorization\")\n  }\n}));\n\nconst USER_QUERY = `\n  query($id: ID!) {\n    user(id: $id) {\n      name\n    }\n  }\n`;\n\nconst variables = { id: 123 };\n\nrequest(USER_QUERY, variables).then(({ data }) => {\n  console.log(data.user);\n});\n```\n\n## Warning\n\nAs this package uses `fetch` and `Object.assign` under the hood, make sure to install the proper polyfills if you want to use it in your project.\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "packages/http-transport/src/index.ts",
    "content": "import { GraphQlPayload, GrafooTransport, Variables } from \"@grafoo/types\";\n\nexport default function createTransport(\n  url: string,\n  options?: RequestInit | (() => RequestInit)\n): GrafooTransport {\n  return <T>(query: string, variables?: Variables): Promise<GraphQlPayload<T>> => {\n    options = typeof options == \"function\" ? options() : options || {};\n\n    return fetch(\n      url,\n      Object.assign(options, {\n        body: JSON.stringify({ query, variables }),\n        method: \"POST\",\n        headers: Object.assign({ \"Content-Type\": \"application/json\" }, options.headers),\n      })\n    ).then((response) => response.json());\n  };\n}\n"
  },
  {
    "path": "packages/http-transport/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"strict\": false,\n    \"lib\": [\"esnext\", \"dom\"],\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"checkJs\": false,\n    \"downlevelIteration\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/preact/.babelrc",
    "content": "{\n  \"presets\": [\n    [\"@babel/preset-env\", { \"targets\": { \"node\": 10 } }],\n    [\"@babel/preset-react\", { \"pragma\": \"h\" }],\n    [\"@babel/preset-typescript\", { \"jsxPragma\": \"h\" }]\n  ],\n  \"plugins\": [\n    [\"babel-plugin-jsx-pragmatic\", { \"module\": \"preact\", \"export\": \"h\", \"import\": \"h\" }],\n    [\n      \"module:@grafoo/babel-plugin\",\n      { \"schema\": \"schema.graphql\", \"idFields\": [\"id\", \"__typename\"] }\n    ]\n  ]\n}\n"
  },
  {
    "path": "packages/preact/.npmignore",
    "content": "coverage\n__tests__\n.rpt2_cache\n.babelrc\nschema.graphql\n"
  },
  {
    "path": "packages/preact/__tests__/index.tsx",
    "content": "/**\n * @jest-environment jsdom\n */\n\nimport createClient from \"@grafoo/core\";\nimport graphql from \"@grafoo/core/tag\";\nimport createTransport from \"@grafoo/http-transport\";\nimport { mockQueryRequest } from \"@grafoo/test-utils\";\nimport { GrafooClient } from \"@grafoo/types\";\nimport { h, FunctionalComponent, Component } from \"preact\";\nimport { render } from \"preact-render-spy\";\nimport { Consumer, Provider } from \"../src\";\n\ninterface Post {\n  title: string;\n  content: string;\n  id: string;\n  __typename: string;\n  author: Author;\n}\n\ninterface Author {\n  name: string;\n  id: string;\n  __typename: string;\n  posts?: Array<Post>;\n}\n\ninterface Authors {\n  authors: Author[];\n}\n\nlet AUTHOR = graphql`\n  query ($id: ID!) {\n    author(id: $id) {\n      name\n    }\n  }\n`;\n\nlet AUTHORS = graphql`\n  {\n    authors {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet CREATE_AUTHOR = graphql`\n  mutation ($name: String!) {\n    createAuthor(name: $name) {\n      name\n    }\n  }\n`;\n\nlet POSTS_AND_AUTHORS = graphql`\n  {\n    posts {\n      title\n      body\n      author {\n        name\n      }\n    }\n\n    authors {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\ndescribe(\"@grafoo/preact\", () => {\n  let client: GrafooClient;\n\n  beforeEach(() => {\n    jest.resetAllMocks();\n\n    let transport = createTransport(\"https://some.graphql.api/\");\n    client = createClient(transport, { idFields: [\"id\"] });\n  });\n\n  describe(\"<Provider />\", () => {\n    it(\"should provide the client in it's context\", (done) => {\n      let Comp = (_, context) => {\n        expect(context.client).toBe(client);\n\n        return null;\n      };\n\n      render(\n        <Provider client={client}>\n          <Comp />\n        </Provider>\n      );\n\n      done();\n    });\n  });\n\n  describe(\"<Consumer />\", () => {\n    it(\"should not crash if a query is not given as prop\", () => {\n      expect(() =>\n        render(\n          <Provider client={client}>\n            <Consumer>{() => null}</Consumer>\n          </Provider>\n        )\n      ).not.toThrow();\n    });\n\n    it(\"should not fetch a query if skip prop is set to true\", async () => {\n      await mockQueryRequest(AUTHORS);\n\n      let spy = jest.spyOn(window, \"fetch\");\n\n      render(\n        <Provider client={client}>\n          <Consumer query={AUTHORS} skip>\n            {() => null}\n          </Consumer>\n        </Provider>\n      );\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n\n    it(\"should trigger listen on client instance\", async () => {\n      await mockQueryRequest(AUTHORS);\n\n      let spy = jest.spyOn(client, \"listen\");\n\n      render(\n        <Provider client={client}>\n          <Consumer query={AUTHORS} skip>\n            {() => null}\n          </Consumer>\n        </Provider>\n      );\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it(\"should not crash on unmount\", () => {\n      let ctx = render(\n        <Provider client={client}>\n          <Consumer query={AUTHORS} skip>\n            {() => null}\n          </Consumer>\n        </Provider>\n      );\n\n      expect(() => ctx.render(null)).not.toThrow();\n    });\n\n    it(\"should execute render with default render argument\", () => {\n      let mockRender = jest.fn();\n\n      render(\n        <Provider client={client}>\n          <Consumer query={AUTHORS} skip>\n            {mockRender}\n          </Consumer>\n        </Provider>\n      );\n\n      let [[call]] = mockRender.mock.calls;\n\n      expect(call).toMatchObject({ loading: false, loaded: false });\n      expect(typeof call.load).toBe(\"function\");\n    });\n\n    it(\"should execute render with the right data if a query is specified\", (done) => {\n      mockQueryRequest<Author>(AUTHORS).then(({ data }) => {\n        let mockRender = createMockRenderFn(done, [\n          (props) => expect(props).toMatchObject({ loading: true, loaded: false }),\n          (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data })\n        ]);\n\n        render(\n          <Provider client={client}>\n            <Consumer query={AUTHORS}>{mockRender}</Consumer>\n          </Provider>\n        );\n      });\n    });\n\n    it(\"should render if skip changed value to true\", (done) => {\n      mockQueryRequest<Author>(AUTHORS).then(async ({ data }) => {\n        let mockRender = createMockRenderFn(done, [\n          (props) => expect(props).toMatchObject({ loading: false, loaded: false }),\n          (props) => expect(props).toMatchObject({ loading: true, loaded: false }),\n          (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data }),\n          (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data })\n        ]);\n\n        let App: FunctionalComponent<{ skip?: boolean }> = ({ skip = false }) => (\n          <Provider client={client}>\n            <Consumer query={AUTHORS} skip={skip}>\n              {mockRender}\n            </Consumer>\n          </Provider>\n        );\n\n        let ctx = render(<App skip />);\n\n        ctx.render(<App />);\n\n        await new Promise((resolve) => setTimeout(resolve, 10));\n\n        ctx.render(<App />);\n      });\n    });\n\n    it(\"should rerender if variables prop has changed\", (done) => {\n      mockQueryRequest<Authors>(AUTHORS).then(async ({ data }) => {\n        let mock = async (variables) => {\n          return (\n            await mockQueryRequest<{ author: Author }>({\n              query: AUTHOR.query,\n              variables\n            })\n          ).data.author;\n        };\n\n        let firstVariables = { id: data.authors[0].id };\n        let secondVariables = { id: data.authors[1].id };\n        let firstAuthor = await mock(firstVariables);\n        let secondAuthor;\n\n        let mockRender = createMockRenderFn(done, [\n          (props) => expect(props).toMatchObject({ loading: true, loaded: false }),\n          (props) => expect(props.author).toMatchObject(firstAuthor),\n          (props) =>\n            expect(props).toMatchObject({ loading: true, loaded: true, author: firstAuthor }),\n          (props) => expect(props.author).toMatchObject(secondAuthor)\n        ]);\n\n        class AuthorComponent extends Component {\n          constructor(props, context) {\n            super(props, context);\n\n            this.state = firstVariables;\n\n            setTimeout(async () => {\n              secondAuthor = await mock(secondVariables);\n\n              this.setState(secondVariables);\n            }, 100);\n          }\n\n          render(_, variables) {\n            return (\n              <Consumer query={AUTHOR} variables={variables}>\n                {mockRender}\n              </Consumer>\n            );\n          }\n        }\n\n        render(\n          <Provider client={client}>\n            <AuthorComponent />\n          </Provider>\n        );\n      });\n    });\n\n    it(\"should not trigger a network request if the query is already cached\", (done) => {\n      mockQueryRequest<Author>(AUTHORS).then(({ data }) => {\n        client.write(AUTHORS, data);\n\n        jest.resetAllMocks();\n\n        let spy = jest.spyOn(client, \"execute\");\n\n        let mockRender = createMockRenderFn(done, [\n          (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data })\n        ]);\n\n        render(\n          <Provider client={client}>\n            <Consumer query={AUTHORS}>{mockRender}</Consumer>\n          </Provider>\n        );\n\n        expect(spy).not.toHaveBeenCalled();\n      });\n    });\n\n    it(\"should handle simple mutations\", (done) => {\n      let { query } = CREATE_AUTHOR;\n      let variables = { name: \"Bart\" };\n\n      mockQueryRequest({ query, variables }).then(({ data }) => {\n        let mockRender = createMockRenderFn(done, [\n          (props) => {\n            props.createAuthor(variables).then((res) => {\n              expect(res.data).toEqual(data);\n            });\n          }\n        ]);\n\n        render(\n          <Provider client={client}>\n            <Consumer mutations={{ createAuthor: { query: CREATE_AUTHOR } }}>{mockRender}</Consumer>\n          </Provider>\n        );\n      });\n    });\n\n    it(\"should handle mutations with cache update\", (done) => {\n      mockQueryRequest<Authors>(AUTHORS).then(({ data }) => {\n        let mockRender = createMockRenderFn(done, [\n          (props) => {\n            expect(props).toMatchObject({ loading: true, loaded: false });\n            expect(typeof props.createAuthor).toBe(\"function\");\n          },\n          (props) => {\n            expect(props).toMatchObject({ loading: false, loaded: true, ...data });\n            let variables = { name: \"Homer\" };\n            mockQueryRequest({ query: CREATE_AUTHOR.query, variables }).then(() => {\n              props.createAuthor(variables);\n            });\n          },\n          (props) => {\n            expect(props.authors.length).toBe(data.authors.length + 1);\n            let newAuthor = props.authors.find((a) => a.id === \"tempID\");\n            expect(newAuthor).toMatchObject({ name: \"Homer\", id: \"tempID\" });\n          },\n          (props) => {\n            expect(props.authors.find((a) => a.id === \"tempID\")).toBeUndefined();\n            expect(props.authors.find((a) => a.name === \"Homer\")).toBeTruthy();\n          }\n        ]);\n\n        render(\n          <Provider client={client}>\n            <Consumer\n              query={AUTHORS}\n              mutations={{\n                createAuthor: {\n                  query: CREATE_AUTHOR,\n                  optimisticUpdate: ({ authors }, variables) => ({\n                    authors: [...authors, { ...variables, id: \"tempID\" }]\n                  }),\n                  update: ({ authors }, { createAuthor: author }) => ({\n                    authors: authors.map((a) => (a.id === \"tempID\" ? author : a))\n                  })\n                }\n              }}\n            >\n              {mockRender}\n            </Consumer>\n          </Provider>\n        );\n      });\n    });\n\n    it(\"should reflect updates that happen outside of the component\", (done) => {\n      mockQueryRequest<Authors>(AUTHORS).then(({ data }) => {\n        client.write(AUTHORS, data);\n\n        let mockRender = createMockRenderFn(done, [\n          (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data }),\n          (props) => expect(props.authors[0].name).toBe(\"Homer\")\n        ]);\n\n        render(\n          <Provider client={client}>\n            <Consumer query={AUTHORS}>{mockRender}</Consumer>\n          </Provider>\n        );\n\n        client.write(AUTHORS, {\n          authors: data.authors.map((a, i) => (!i ? { ...a, name: \"Homer\" } : a))\n        });\n      });\n    });\n\n    it(\"should not trigger a network request if a query field is cached\", (done) => {\n      mockQueryRequest<Authors>(POSTS_AND_AUTHORS).then(({ data }) => {\n        client.write(POSTS_AND_AUTHORS, data);\n\n        let spy = jest.spyOn(client, \"execute\");\n\n        let mockRender = createMockRenderFn(done, [\n          (props) => {\n            expect(props).toMatchObject({ authors: data.authors, loading: false, loaded: true });\n            expect(spy).not.toHaveBeenCalled();\n          }\n        ]);\n\n        render(\n          <Provider client={client}>\n            <Consumer query={AUTHORS}>{mockRender}</Consumer>\n          </Provider>\n        );\n      });\n    });\n  });\n});\n\nfunction createMockRenderFn(done, assertionsFns) {\n  let currentRender = 0;\n\n  return (props) => {\n    let assert = assertionsFns[currentRender];\n\n    if (assert) assertionsFns[currentRender](props);\n\n    if (currentRender++ === assertionsFns.length - 1) done();\n\n    return null;\n  };\n}\n"
  },
  {
    "path": "packages/preact/__tests__/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig\",\n  \"include\": [\".\"]\n}\n"
  },
  {
    "path": "packages/preact/package.json",
    "content": "{\n  \"name\": \"@grafoo/preact\",\n  \"version\": \"1.4.2\",\n  \"description\": \"grafoo client preact bindings\",\n  \"repository\": \"https://github.com/grafoojs/grafoo/tree/master/packages/preact\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"author\": \"malbernaz<albernazmiguel@gmail.com>\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"babel\",\n    \"babel-plugin\",\n    \"graphql\",\n    \"graphql-client\",\n    \"grafoo\",\n    \"preact\",\n    \"preactjs\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"grafoo-bundle --input src/index.ts\",\n    \"test\": \"jest\",\n    \"test:coverage\": \"jest --coverage\"\n  },\n  \"jest\": {\n    \"transform\": {\n      \"^.+\\\\.(ts|tsx|js)$\": \"<rootDir>/../../scripts/jest-setup.js\"\n    },\n    \"resolver\": \"<rootDir>/../../scripts/resolver.js\",\n    \"transformIgnorePatterns\": [\n      \"node_modules/(?!(lowdb|steno|node-fetch|fetch-blob)/)\"\n    ]\n  },\n  \"peerDependencies\": {\n    \"preact\": \">=8.3\"\n  },\n  \"dependencies\": {\n    \"@grafoo/bindings\": \"^1.4.2\",\n    \"@grafoo/types\": \"^1.4.2\"\n  },\n  \"gitHead\": \"0bc67d8b398884a1f387a1813e485d2c5318b974\"\n}\n"
  },
  {
    "path": "packages/preact/readme.md",
    "content": "# `@grafoo/preact`\n\n<p><i>Grafoo Preact Bindings</i></p>\n\n<p>\n  <a href=https://circleci.com/gh/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/circleci/project/github/grafoojs/grafoo/master.svg?label=build\n      alt=build\n    />\n  </a>\n  <a href=https://codecov.io/github/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/codecov/c/github/grafoojs/grafoo/master.svg\n      alt=\"coverage\"\n    />\n  </a>\n  <a href=https://github.com/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/npm/v/@grafoo/preact.svg\n      alt=npm\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/preact>\n    <img\n      src=https://img.shields.io/npm/dm/@grafoo/preact.svg\n      alt=downloads\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/preact>\n    <img\n      src=https://img.shields.io/bundlephobia/minzip/@grafoo/preact.svg?label=size\n      alt=size\n    >\n  </a>\n  <a href=https://prettier.io>\n    <img\n      src=https://img.shields.io/badge/code_style-prettier-ff69b4.svg\n      alt=\"code style: prettier\"\n    />\n  </a>\n  <a href=https://lernajs.io>\n    <img\n      src=https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg\n      alt=\"mantained with: lerna\"\n    />\n  </a>\n  <a href=https://grafoo-slack.herokuapp.com>\n    <img\n      src=https://grafoo-slack.herokuapp.com/badge.svg\n      alt=\"slack\"\n    />\n  </a>\n</p>\n\n## Install\n\n```\n$ npm i @grafoo/{core,react} && npm i -D @grafoo/babel-plugin\n```\n\n## API\n\nFor documentation please refer to [`@grafoo/react`](https://github.com/grafoojs/grafoo/tree/master/packages/react)'s page since both modules share the same API.\n\n## Example\n\n**`index.js`**\n\n```jsx\nimport { h, render } from \"preact\";\nimport createClient from \"@grafoo/core\";\nimport { Provider } from \"@grafoo/preact\";\n\nimport Posts from \"./Posts\";\n\nfunction fetchQuery(query, variables) {\n  const init = {\n    method: \"POST\",\n    body: JSON.stringify({ query, variables }),\n    headers: {\n      \"content-type\": \"application/json\"\n    }\n  };\n\n  return fetch(\"http://some.graphql.api\", init).then(res => res.json());\n}\n\nconst client = createClient(fetchQuery);\n\nrender(\n  <Provider client={client}>\n    <Posts />\n  </Provider>,\n  document.getElementById(\"mnt\")\n);\n```\n\n**`Posts.js`**\n\n```jsx\nimport { h } from \"preact\";\nimport gql from \"@grafoo/core/tag\";\nimport { Consumer } from \"@grafoo/preact\";\n\nconst ALL_POSTS = gql`\n  query getPosts($orderBy: PostOrderBy) {\n    allPosts(orderBy: $orderBy) {\n      title\n      content\n      createdAt\n      updatedAt\n    }\n  }\n`;\n\nexport default function Posts() {\n  return (\n    <Consumer query={ALL_POSTS} variables={{ orderBy: \"createdAt_DESC\" }}>\n      {({ client, load, loaded, loading, errors, allPosts }) => (\n        <h1>\n          <marquee>👆 do whatever you want with the variables above 👆</marquee>\n        </h1>\n      )}\n    </Consumer>\n  );\n}\n```\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "packages/preact/schema.graphql",
    "content": "type Query {\n  author(id: ID!): Author!\n  authors: [Author!]!\n  post(id: ID!): Post!\n  posts: [Post!]!\n}\n\ntype Mutation {\n  createAuthor(name: String!): Author!\n  updateAuthor(id: ID!, name: String): Author!\n  deleteAuthor(id: ID!): Author!\n  createPost(title: String!, body: String!, author: ID!): Post!\n  updatePost(id: ID!, title: String, body: String): Post!\n  deletePost(id: ID!): Post!\n}\n\ntype Author {\n  id: ID!\n  name: String!\n  posts: [Post!]\n}\n\ntype Post {\n  id: ID!\n  title: String!\n  body: String!\n  author: Author!\n}\n"
  },
  {
    "path": "packages/preact/src/consumer.ts",
    "content": "import createBindings from \"@grafoo/bindings\";\nimport {\n  Context,\n  GrafooBoundState,\n  GrafooBoundMutations,\n  GrafooConsumerProps,\n} from \"@grafoo/types\";\nimport { Component, VNode } from \"preact\";\n\n/**\n * T = Query\n * U = Mutations\n */\ntype GrafooRenderFn<T, U> = (renderProps: GrafooBoundState & T & GrafooBoundMutations<U>) => VNode;\n\n/**\n * T = Query\n * U = Mutations\n */\ntype GrafooPreactConsumerProps<T = unknown, U = unknown> = GrafooConsumerProps<T, U> & {\n  children?: GrafooRenderFn<T, U>;\n};\n\n/**\n * T = Query\n * U = Mutations\n */\nexport class Consumer<T = unknown, U = unknown> extends Component<GrafooPreactConsumerProps<T, U>> {\n  state: GrafooBoundState & T & GrafooBoundMutations<U>;\n\n  constructor(props: GrafooPreactConsumerProps<T, U>, context: Context) {\n    super(props, context);\n\n    let bindings = createBindings<T, U>(context.client, props, () => {\n      this.setState(bindings.getState());\n    });\n\n    this.state = bindings.getState();\n\n    this.componentDidMount = () => {\n      if (props.skip || !props.query || this.state.loaded) return;\n\n      this.state.load();\n    };\n\n    this.componentWillReceiveProps = (next) => {\n      if ((!this.state.loaded && !next.skip) || props.variables !== next.variables)\n        this.state.load(next.variables);\n    };\n\n    this.componentWillUnmount = () => {\n      bindings.unbind();\n    };\n  }\n\n  render(props, state): VNode {\n    return props.children[0](state);\n  }\n}\n"
  },
  {
    "path": "packages/preact/src/index.ts",
    "content": "export * from \"./provider\";\nexport * from \"./consumer\";\n"
  },
  {
    "path": "packages/preact/src/provider.ts",
    "content": "import { Context } from \"@grafoo/types\";\nimport { Component } from \"preact\";\n\ntype GrafooPreactProviderProps = Context & { children?: JSX.Element };\n\nexport class Provider extends Component<GrafooPreactProviderProps> {\n  getChildContext(): Context {\n    return { client: this.props.client };\n  }\n\n  render(props: GrafooPreactProviderProps): JSX.Element {\n    return props.children[0];\n  }\n}\n"
  },
  {
    "path": "packages/preact/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"strict\": false,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"checkJs\": false,\n    \"downlevelIteration\": true,\n    \"jsx\": \"react\",\n    \"jsxFactory\": \"h\",\n    \"lib\": [\"dom\", \"esnext\"],\n    \"types\": [\"preact\", \"jest\"]\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/react/.babelrc",
    "content": "{\n  \"presets\": [\n    [\"@babel/preset-env\", { \"targets\": { \"node\": 10 } }],\n    \"@babel/preset-react\",\n    \"@babel/preset-typescript\"\n  ],\n  \"plugins\": [\n    [\n      \"module:@grafoo/babel-plugin\",\n      { \"schema\": \"schema.graphql\", \"idFields\": [\"id\"] }\n    ]\n  ]\n}\n"
  },
  {
    "path": "packages/react/.npmignore",
    "content": "coverage\n__tests__\n.rpt2_cache\n.babelrc\nschema.graphql\n"
  },
  {
    "path": "packages/react/__tests__/index.tsx",
    "content": "/**\n * @jest-environment jsdom\n */\n\nimport createClient from \"@grafoo/core\";\nimport graphql from \"@grafoo/core/tag\";\nimport createTrasport from \"@grafoo/http-transport\";\nimport { mockQueryRequest } from \"@grafoo/test-utils\";\nimport { GrafooClient, Variables } from \"@grafoo/types\";\nimport * as React from \"react\";\nimport * as TestRenderer from \"react-test-renderer\";\nimport { Consumer, Provider } from \"../src\";\n\ninterface Post {\n  title: string;\n  content: string;\n  id: string;\n  __typename: string;\n  author: Author;\n}\n\ninterface Author {\n  name: string;\n  id: string;\n  __typename: string;\n  posts?: Array<Post>;\n}\n\ninterface Authors {\n  authors: Author[];\n}\n\nlet AUTHORS = graphql`\n  {\n    authors {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\nlet AUTHOR = graphql`\n  query ($id: ID!) {\n    author(id: $id) {\n      name\n    }\n  }\n`;\n\nlet CREATE_AUTHOR = graphql`\n  mutation ($name: String!) {\n    createAuthor(name: $name) {\n      name\n    }\n  }\n`;\n\nlet POSTS_AND_AUTHORS = graphql`\n  {\n    posts {\n      title\n      body\n      author {\n        name\n      }\n    }\n\n    authors {\n      name\n      posts {\n        title\n        body\n      }\n    }\n  }\n`;\n\ndescribe(\"@grafoo/react\", () => {\n  let client: GrafooClient;\n\n  beforeEach(() => {\n    jest.resetAllMocks();\n    let transport = createTrasport(\"https://some.graphql.api/\");\n    client = createClient(transport, { idFields: [\"id\"] });\n  });\n\n  it(\"should not crash if a query is not given as prop\", () => {\n    expect(() =>\n      TestRenderer.create(\n        <Provider client={client}>\n          <Consumer>{() => null}</Consumer>\n        </Provider>\n      )\n    ).not.toThrow();\n  });\n\n  it(\"should not fetch a query if skip prop is set to true\", async () => {\n    await mockQueryRequest(AUTHORS);\n\n    let spy = jest.spyOn(window, \"fetch\");\n\n    TestRenderer.create(\n      <Provider client={client}>\n        <Consumer query={AUTHORS} skip>\n          {() => null}\n        </Consumer>\n      </Provider>\n    );\n\n    expect(spy).not.toHaveBeenCalled();\n  });\n\n  it(\"should trigger listen on client instance\", async () => {\n    await mockQueryRequest(AUTHORS);\n\n    let spy = jest.spyOn(client, \"listen\");\n\n    TestRenderer.create(\n      <Provider client={client}>\n        <Consumer query={AUTHORS} skip>\n          {() => null}\n        </Consumer>\n      </Provider>\n    );\n\n    expect(spy).toHaveBeenCalled();\n  });\n\n  it(\"should not crash on unmount\", () => {\n    let testRenderer = TestRenderer.create(\n      <Provider client={client}>\n        <Consumer query={AUTHORS} skip>\n          {() => null}\n        </Consumer>\n      </Provider>\n    );\n\n    expect(() => testRenderer.unmount()).not.toThrow();\n  });\n\n  it(\"should execute render with default render argument\", () => {\n    let mockRender = jest.fn().mockReturnValue(null);\n\n    TestRenderer.create(\n      <Provider client={client}>\n        <Consumer query={AUTHORS} skip>\n          {mockRender}\n        </Consumer>\n      </Provider>\n    );\n\n    let [[call]] = mockRender.mock.calls;\n\n    expect(call).toMatchObject({ loading: false, loaded: false });\n    expect(typeof call.load).toBe(\"function\");\n  });\n\n  it(\"should execute render with the right data if a query is specified\", (done) => {\n    mockQueryRequest<Authors>(AUTHORS).then(({ data }) => {\n      let mockRender = createMockRenderFn(done, [\n        (props) => expect(props).toMatchObject({ loading: true, loaded: false }),\n        (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data })\n      ]);\n\n      TestRenderer.create(\n        <Provider client={client}>\n          <Consumer query={AUTHORS}>{mockRender}</Consumer>\n        </Provider>\n      );\n    });\n  });\n\n  it(\"should render if skip changed value to true\", (done) => {\n    mockQueryRequest<Authors>(AUTHORS).then(async ({ data }) => {\n      let mockRender = createMockRenderFn(done, [\n        (props) => expect(props).toMatchObject({ loading: false, loaded: false }),\n        (props) => expect(props).toMatchObject({ loading: true, loaded: false }),\n        (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data })\n      ]);\n\n      let App: React.FC<{ skip?: boolean }> = ({ skip = false }) => (\n        <Provider client={client}>\n          <Consumer query={AUTHORS} skip={skip}>\n            {mockRender}\n          </Consumer>\n        </Provider>\n      );\n\n      let ctx = TestRenderer.create(<App skip />);\n\n      ctx.update(<App />);\n      await new Promise((resolve) => setTimeout(resolve, 10));\n      ctx.update(<App />);\n    });\n  });\n\n  it(\"should rerender if variables prop has changed\", (done) => {\n    mockQueryRequest<Authors>(AUTHORS).then(async ({ data }) => {\n      let mock = async (variables: Variables) => {\n        return (\n          await mockQueryRequest<{ author: Author }>({\n            query: AUTHOR.query,\n            variables\n          })\n        ).data.author;\n      };\n\n      let firstVariables = { id: data.authors[0].id };\n      let secondVariables = { id: data.authors[1].id };\n      let firstAuthor = await mock(firstVariables);\n      let secondAuthor;\n\n      let mockRender = createMockRenderFn(done, [\n        (props) => expect(props).toMatchObject({ loading: true, loaded: false }),\n        (props) => expect(props.author).toMatchObject(firstAuthor),\n        (props) =>\n          expect(props).toMatchObject({ loading: true, loaded: true, author: firstAuthor }),\n        (props) => expect(props.author).toMatchObject(secondAuthor)\n      ]);\n\n      class AuthorComponent extends React.Component {\n        constructor(props, context) {\n          super(props, context);\n\n          this.state = firstVariables;\n\n          setTimeout(async () => {\n            secondAuthor = await mock(secondVariables);\n\n            this.setState(secondVariables);\n          }, 100);\n        }\n\n        render() {\n          return (\n            <Consumer query={AUTHOR} variables={this.state}>\n              {mockRender}\n            </Consumer>\n          );\n        }\n      }\n\n      TestRenderer.create(\n        <Provider client={client}>\n          <AuthorComponent />\n        </Provider>\n      );\n    });\n  });\n\n  it(\"should not trigger a network request if the query is already cached\", (done) => {\n    mockQueryRequest<Authors>(AUTHORS).then(({ data }) => {\n      client.write(AUTHORS, data);\n\n      jest.resetAllMocks();\n\n      let spy = jest.spyOn(client, \"execute\");\n\n      let mockRender = createMockRenderFn(done, [\n        (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data })\n      ]);\n\n      TestRenderer.create(\n        <Provider client={client}>\n          <Consumer query={AUTHORS}>{mockRender}</Consumer>\n        </Provider>\n      );\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n  });\n\n  it(\"should handle simple mutations\", (done) => {\n    let variables = { name: \"Bart\" };\n\n    mockQueryRequest({ query: CREATE_AUTHOR.query, variables }).then(({ data }) => {\n      let mockRender = createMockRenderFn(done, [\n        (props) => {\n          props.createAuthor(variables).then((res) => {\n            expect(res.data).toEqual(data);\n          });\n        }\n      ]);\n\n      let mutations = { createAuthor: { query: CREATE_AUTHOR } };\n\n      TestRenderer.create(\n        <Provider client={client}>\n          <Consumer mutations={mutations}>{mockRender}</Consumer>\n        </Provider>\n      );\n    });\n  });\n\n  it(\"should handle mutations with cache update\", (done) => {\n    mockQueryRequest<Authors>(AUTHORS).then(({ data }) => {\n      let mockRender = createMockRenderFn(done, [\n        (props) => {\n          expect(props).toMatchObject({ loading: true, loaded: false });\n          expect(typeof props.createAuthor).toBe(\"function\");\n        },\n        (props) => {\n          expect(props).toMatchObject({ loading: false, loaded: true, ...data });\n          let variables = { name: \"Homer\" };\n          mockQueryRequest({ query: CREATE_AUTHOR.query, variables }).then(() => {\n            props.createAuthor(variables);\n          });\n        },\n        (props) => {\n          expect(props.authors.length).toBe(data.authors.length + 1);\n          let newAuthor = props.authors.find((a) => a.id === \"tempID\");\n          expect(newAuthor).toMatchObject({ name: \"Homer\", id: \"tempID\" });\n        },\n        (props) => {\n          expect(props.authors.find((a) => a.id === \"tempID\")).toBeUndefined();\n          expect(props.authors.find((a) => a.name === \"Homer\")).toBeTruthy();\n        }\n      ]);\n\n      TestRenderer.create(\n        <Provider client={client}>\n          <Consumer\n            query={AUTHORS}\n            mutations={{\n              createAuthor: {\n                query: CREATE_AUTHOR,\n                optimisticUpdate: ({ authors }, variables) => ({\n                  authors: [...authors, { ...variables, id: \"tempID\" }]\n                }),\n                update: ({ authors }, data) => ({\n                  authors: authors.map((a) => (a.id === \"tempID\" ? (data as any).createAuthor : a))\n                })\n              }\n            }}\n          >\n            {mockRender}\n          </Consumer>\n        </Provider>\n      );\n    });\n  });\n\n  it(\"should reflect updates that happen outside of the component\", (done) => {\n    mockQueryRequest<Authors>(AUTHORS).then(({ data }) => {\n      client.write(AUTHORS, data);\n\n      let mockRender = createMockRenderFn(done, [\n        (props) => expect(props).toMatchObject({ loading: false, loaded: true, ...data }),\n        (props) => expect(props.authors[0].name).toBe(\"Homer\")\n      ]);\n\n      TestRenderer.create(\n        <Provider client={client}>\n          <Consumer query={AUTHORS}>{mockRender}</Consumer>\n        </Provider>\n      );\n\n      client.write(AUTHORS, {\n        authors: data.authors.map((a, i) => (!i ? { ...a, name: \"Homer\" } : a))\n      });\n    });\n  });\n\n  it(\"should not trigger a network request if a query field is cached\", (done) => {\n    mockQueryRequest<Authors>(POSTS_AND_AUTHORS).then(({ data }) => {\n      client.write(POSTS_AND_AUTHORS, data);\n\n      let spy = jest.spyOn(client, \"execute\");\n\n      let mockRender = createMockRenderFn(done, [\n        (props) => {\n          expect(props).toMatchObject({ authors: data.authors, loading: false, loaded: true });\n          expect(spy).not.toHaveBeenCalled();\n        }\n      ]);\n\n      TestRenderer.create(\n        <Provider client={client}>\n          <Consumer query={AUTHORS}>{mockRender}</Consumer>\n        </Provider>\n      );\n    });\n  });\n});\n\nfunction createMockRenderFn(done, assertionsFns) {\n  let currentRender = 0;\n\n  return (props) => {\n    let assert = assertionsFns[currentRender];\n\n    if (assert) assertionsFns[currentRender](props);\n\n    if (currentRender++ === assertionsFns.length - 1) done();\n\n    return null;\n  };\n}\n"
  },
  {
    "path": "packages/react/__tests__/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig\",\n  \"include\": [\".\"]\n}\n"
  },
  {
    "path": "packages/react/package.json",
    "content": "{\n  \"name\": \"@grafoo/react\",\n  \"version\": \"1.4.2\",\n  \"description\": \"grafoo client react bindings\",\n  \"repository\": \"https://github.com/grafoojs/grafoo/tree/master/packages/react\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"author\": \"malbernaz<albernazmiguel@gmail.com>\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"babel\",\n    \"babel-plugin\",\n    \"graphql\",\n    \"graphql-client\",\n    \"grafoo\",\n    \"react\",\n    \"reactjs\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"grafoo-bundle --input src/index.ts\",\n    \"test\": \"jest\",\n    \"test:coverage\": \"jest --coverage\",\n    \"tsc\": \"tsc --noEmit\"\n  },\n  \"jest\": {\n    \"transform\": {\n      \"^.+\\\\.(ts|tsx|js)$\": \"<rootDir>/../../scripts/jest-setup.js\"\n    },\n    \"resolver\": \"<rootDir>/../../scripts/resolver.js\",\n    \"transformIgnorePatterns\": [\n      \"node_modules/(?!(lowdb|steno|node-fetch|fetch-blob)/)\"\n    ]\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.4\"\n  },\n  \"dependencies\": {\n    \"@grafoo/bindings\": \"^1.4.2\",\n    \"@grafoo/types\": \"^1.4.2\"\n  },\n  \"gitHead\": \"0bc67d8b398884a1f387a1813e485d2c5318b974\"\n}\n"
  },
  {
    "path": "packages/react/readme.md",
    "content": "# `@grafoo/react`\n\n<p><i>Grafoo React Bindings</i></p>\n\n<p>\n  <a href=https://circleci.com/gh/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/circleci/project/github/grafoojs/grafoo/master.svg?label=build\n      alt=build\n    />\n  </a>\n  <a href=https://codecov.io/github/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/codecov/c/github/grafoojs/grafoo/master.svg\n      alt=\"coverage\"\n    />\n  </a>\n  <a href=https://github.com/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/npm/v/@grafoo/bindings.svg\n      alt=npm\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/react>\n    <img\n      src=https://img.shields.io/npm/dm/@grafoo/bindings.svg\n      alt=downloads\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/react>\n    <img\n      src=https://img.shields.io/bundlephobia/minzip/@grafoo/react.svg?label=size\n      alt=size\n    >\n  </a>\n  <a href=https://prettier.io>\n    <img\n      src=https://img.shields.io/badge/code_style-prettier-ff69b4.svg\n      alt=\"code style: prettier\"\n    />\n  </a>\n  <a href=https://lernajs.io>\n    <img\n      src=https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg\n      alt=\"mantained with: lerna\"\n    />\n  </a>\n  <a href=https://grafoo-slack.herokuapp.com>\n    <img\n      src=https://grafoo-slack.herokuapp.com/badge.svg\n      alt=\"slack\"\n    />\n  </a>\n</p>\n\n## Install\n\n```\n$ npm i @grafoo/{core,react} && npm i -D @grafoo/babel-plugin\n```\n\n## Setup\n\nAssuming you already have babel installed, the only additional step required to build an application with Grafoo is to configure [`@grafoo/babel-plugin`](https://github.com/grafoojs/grafoo/tree/master/packages/babel-plugin). The options it accepts are `idFields` - the fields Grafoo will take to build unique identifiers, and `schema`, which is a relative path to your schema file.\n\n```json\n{\n  \"plugins\": [\n    [\n      \"@grafoo/babel-plugin\",\n      {\n        \"schema\": \"schema.graphql\",\n        \"idFields\": [\"id\"]\n      }\n    ]\n  ]\n}\n```\n\n## API\n\n### `Provider`\n\n`Provider` receives a single `client` instance prop that will be consumed by the `Consumer` components.\n\n```jsx\nimport React from \"react\";\nimport createClient from \"@grafoo/core\";\nimport { Provider } from \"@grafoo/react\";\n\nfunction fetchQuery(query, variables) {\n  const init = {\n    method: \"POST\",\n    body: JSON.stringify({ query, variables }),\n    headers: {\n      \"content-type\": \"application/json\"\n    }\n  };\n\n  return fetch(\"http://some.graphql.api\", init).then(res => res.json());\n}\n\nconst client = createClient(fetchQuery);\n\nexport default function App() {\n  return (\n    <Provider client={client}>\n      <SomeComponent />\n    </Provider>\n  );\n}\n```\n\n### `Consumer`\n\n`Consumer` is the component that performs query requests to your GraphQL API. It accepts the following props:\n\n| Name      | Type     | Default | Required | Descrition                                                   |\n| --------- | -------- | ------- | -------- | ------------------------------------------------------------ |\n| query     | object   | -       | false    | a query created with `@grafoo/core/tag`                      |\n| variables | object   | -       | false    | a GraphQL variables object for the `query` prop              |\n| mutations | object   | -       | false    | a map of mutations (description below)                       |\n| skip      | boolean  | false   | false    | whether `Consumer` should skip the `query` request initially |\n| children  | function | -       | false    | a render function (description below)                        |\n\n### Render parameter\n\nThe `Consumer` render function takes as parameter an object with the following props:\n\n| Name    | type     | Descrition                                                   |\n| ------- | -------- | ------------------------------------------------------------ |\n| client  | object   | the client instance                                          |\n| load    | function | a method to execute a query with the `query` prop            |\n| loading | boolean  | whether the client is executing a query or not               |\n| loaded  | boolean  | whether the query data is already cached                     |\n| errors  | string[] | an array of GraphQL errors from a failed request to your API |\n\nThe remaining props are:\n\n- the data fetched by the client and shaped according to your `query`\n- mutation functions generated by the `mutations` object prop\n\n#### Example\n\n```jsx\nimport React from \"react\";\nimport gql from \"@grafoo/core/tag\";\nimport { Consumer } from \"@grafoo/react\";\n\nconst ALL_POSTS = gql`\n  query getPosts($orderBy: PostOrderBy) {\n    allPosts(orderBy: $orderBy) {\n      title\n      content\n      createdAt\n      updatedAt\n    }\n  }\n`;\n\nexport default function Posts() {\n  return (\n    <Consumer query={ALL_POSTS} variables={{ orderBy: \"createdAt_DESC\" }}>\n      {({ client, load, loaded, loading, errors, allPosts }) => (\n        <h1>\n          <marquee>👆 do whatever you want with the variables above 👆</marquee>\n        </h1>\n      )}\n    </Consumer>\n  );\n}\n```\n\n### Mutations\n\nThe `mutations` prop is a map of _mutation objects_ that are shaped like so:\n\n```js\nconst createPost = {\n  query: CREATE_POST_MUTATION,\n  optimisticUpdate: ({ allPosts }, variables) => ({\n    allPosts: [{ ...variables.postInput, id: \"tempID\" }, ...allPosts]\n  }),\n  update: ({ allPosts }, response) => ({\n    allPosts: allPosts.map(p => (p.id === \"tempID\" ? response.post : p))\n  })\n};\n\nconst mutations = { createPost };\n```\n\nA mutation object receives the following props:\n\n| Name             | Type     | Required | Descrition                                                          |\n| ---------------- | -------- | -------- | ------------------------------------------------------------------- |\n| query            | object   | true     | a mutation query created with `@grafoo/core/tag`                    |\n| update           | function | false    | updates the cache when a request is completed (description below)   |\n| optimisticUpdate | function | false    | updates the cache before a request is completed (description below) |\n\nEach mutation will generate a single function that accepts a GraphQL variables object as argument and return a promise that will resolve with the mutation data or reject with GraphQL `errors`.\n\n```ts\ntype MutationFn = (variables: Variables) => Promise<MutationResponse>;\n```\n\n### Mutation query dependency\n\n**Important** to notice that to update the cache `update` and `optimistUpdate` hooks depend on a `query` and it's `variables` object props (so they need be declared in the `Consumer`). If you need to perform a mutation but updating the cache is not strictly important you can just use the mutation promise resolved data or use the client instance directly.\n\n### `update`\n\n```ts\ntype UpdateFn = (query: QueryData, response: MutationResponse) => CacheUpdate;\n```\n\nThe mutation `update` function is resposible to update the cache when the request is completed. It receives as paremeters an object containing the data from the query it depends upon and the mutation response sent by the server. `update` return type is an object that describes the changes to be made to the cache.\n\n### `optimisticUpdate`\n\n```ts\ntype OptimistcUpdateFn = (query: QueryData, variables: Variables) => CacheUpdate;\n```\n\nIn modern UIs it's to be expected that every user interaction occur in a fraction of seconds. `optimisticUpdate` responsability is to skip the mutation network roundtrip and update the cache instantaneously, making sure such interactions are as fast as they can be. `optimisticUpdate` as in `update` takes as first paremater the depedent query data. As second paremater it receives the variables object with which it's correpondent generated mutation function was called. And again it should return an object that describes the changes to be made to cache.\n\nIf you want to perform an optimitic update you have to make sure that the data you are inserting contains the field or fields to extract a unique identifier. For instance, say `@grafoo/babel-plugin` `idFields` option is set to insert a property `id`. Is to be expected that your update has that field mocked.\n\n#### Example\n\n```jsx\nimport React from \"react\";\nimport gql from \"@grafoo/core/tag\";\nimport { Consumer } from \"@grafoo/react\";\n\nconst ALL_POSTS = gql`\n  query getPosts($orderBy: PostOrderBy) {\n    allPosts(orderBy: $orderBy) {\n      title\n      content\n      createdAt\n      updatedAt\n    }\n  }\n`;\n\nconst CREATE_POST = gql`\n  mutation createPost($content: String, $title: String, $authorId: ID) {\n    createPost(content: $content, title: $title, authorId: $authorId) {\n      title\n      content\n      createdAt\n      updatedAt\n    }\n  }\n`;\n\nconst mutations = {\n  createPost: {\n    query: CREATE_POST,\n    optimisticUpdate: ({ allPosts }, variables) => ({\n      allPosts: [{ ...variables, id: \"tempID\" }, ...allPosts]\n    }),\n    update: ({ allPosts }, data) => ({\n      allPosts: allPosts.map(p => (p.id === \"tempID\" ? data.createPost : p))\n    })\n  }\n};\n\nconst submit = mutate => event => {\n  event.preventDefault();\n\n  const { title, content } = event.target.elements;\n\n  mutate({ title: title.value, content: content.value }).then(mutationData => {\n    console.log(mutationData);\n  });\n};\n\nexport default function PostForm() {\n  return (\n    <Consumer query={ALL_POSTS} mutations={mutations}>\n      {({ createPost }) => (\n        <form onSubmit={submit(createPost)}>\n          <input name=\"title\" />\n          <textarea name=\"content\" />\n          <button>submit</button>\n        </form>\n      )}\n    </Consumer>\n  );\n}\n```\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "packages/react/schema.graphql",
    "content": "type Query {\n  author(id: ID!): Author!\n  authors: [Author!]!\n  post(id: ID!): Post!\n  posts: [Post!]!\n}\n\ntype Mutation {\n  createAuthor(name: String!): Author!\n  updateAuthor(id: ID!, name: String): Author!\n  deleteAuthor(id: ID!): Author!\n  createPost(title: String!, body: String!, author: ID!): Post!\n  updatePost(id: ID!, title: String, body: String): Post!\n  deletePost(id: ID!): Post!\n}\n\ntype Author {\n  id: ID!\n  name: String!\n  posts: [Post!]\n}\n\ntype Post {\n  id: ID!\n  title: String!\n  body: String!\n  author: Author!\n}\n"
  },
  {
    "path": "packages/react/src/index.ts",
    "content": "import createBindings from \"@grafoo/bindings\";\nimport {\n  Context,\n  GrafooConsumerProps,\n  GrafooBoundState,\n  GrafooBoundMutations,\n} from \"@grafoo/types\";\nimport { Component, createContext, createElement, ReactElement, ReactNode, FC } from \"react\";\n\n/**\n * T = Query\n * U = Mutations\n */\ntype GrafooRenderFn<T, U> = (\n  renderProps: GrafooBoundState & T & GrafooBoundMutations<U>\n) => ReactNode;\n\n/**\n * T = Query\n * U = Mutations\n */\ntype GrafooReactConsumerProps<T = unknown, U = unknown> = GrafooConsumerProps<T, U> & {\n  children: GrafooRenderFn<T, U>;\n};\n\n/**\n * T = Query\n * U = Mutations\n */\ninterface ConsumerType extends FC {\n  <T, U>(props: GrafooReactConsumerProps<T, U>): ReactElement | null;\n}\n\nlet ctx = createContext({});\n\nexport let Provider: FC<Context> = (props) =>\n  createElement(ctx.Provider, { value: props.client }, props.children);\n\nclass GrafooConsumer<T, U> extends Component<GrafooReactConsumerProps<T, U>> {\n  state: GrafooBoundState & T & GrafooBoundMutations<U>;\n\n  constructor(props) {\n    super(props);\n\n    let bindings = createBindings<T, U>(props.client, props, () => {\n      this.setState(bindings.getState());\n    });\n\n    this.state = bindings.getState();\n\n    this.componentDidMount = () => {\n      if (props.skip || !props.query || this.state.loaded) return;\n\n      bindings.load();\n    };\n\n    this.componentWillReceiveProps = (next) => {\n      if ((!this.state.loaded && !next.skip) || props.variables !== next.variables) {\n        bindings.load(next.variables);\n      }\n    };\n\n    this.componentWillUnmount = () => {\n      bindings.unbind();\n    };\n  }\n\n  render() {\n    return this.props.children(this.state);\n  }\n}\n\n/**\n * T = Query\n * U = Mutations\n */\nexport let Consumer: ConsumerType = <T, U>(props: GrafooReactConsumerProps<T, U>) =>\n  createElement(ctx.Consumer, null, (client) =>\n    createElement(GrafooConsumer, Object.assign({ client }, props))\n  );\n"
  },
  {
    "path": "packages/react/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"strict\": false,\n    \"lib\": [\"esnext\", \"dom\"],\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"checkJs\": false,\n    \"downlevelIteration\": true,\n    \"jsx\": \"react\"\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/test-utils/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"@grafoo/test-utils\",\n  \"version\": \"1.4.2\",\n  \"main\": \"dist/index.js\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\"\n  }\n}\n"
  },
  {
    "path": "packages/test-utils/schema.graphql",
    "content": "type Query {\n  author(id: ID!): Author!\n  authors: [Author!]!\n  post(id: ID!): Post!\n  posts: [Post!]!\n}\n\ntype Mutation {\n  createAuthor(name: String!): Author!\n  updateAuthor(id: ID!, name: String): Author!\n  deleteAuthor(id: ID!): Author!\n  createPost(title: String!, body: String!, author: ID!): Post!\n  updatePost(id: ID!, title: String, body: String): Post!\n  deletePost(id: ID!): Post!\n}\n\ntype Author {\n  id: ID!\n  name: String!\n  posts: [Post!]\n}\n\ntype Post {\n  id: ID!\n  title: String!\n  body: String!\n  author: Author!\n}\n"
  },
  {
    "path": "packages/test-utils/src/db.ts",
    "content": "import casual from \"casual\";\nimport { Low, Memory } from \"lowdb\";\n\ncasual.seed(666);\n\nlet times = (t: number, fn: (i: number) => void) => Array.from(Array(t), fn);\n\nexport default function setupDB() {\n  let db = new Low<{ posts: any[]; authors: any[] }>(new Memory());\n\n  db.data = { posts: [], authors: [] };\n\n  db.read;\n  times(2, () =>\n    db.data.authors.push({\n      id: casual.uuid,\n      name: casual.first_name + \" \" + casual.last_name,\n    })\n  );\n\n  db.data.authors.forEach(({ id }) => {\n    times(4, () =>\n      db.data.posts.push({\n        author: id,\n        id: casual.uuid,\n        title: casual.title,\n        body: casual.short_description,\n      })\n    );\n\n    let posts = db.data.posts.filter((post) => post.author === id).map((post) => post.id);\n\n    db.data.authors.find((author) => author.id === id).posts = posts;\n  });\n\n  db.write();\n\n  return db;\n}\n"
  },
  {
    "path": "packages/test-utils/src/index.ts",
    "content": "export * from \"./db\";\nexport * from \"./mock-server\";\n"
  },
  {
    "path": "packages/test-utils/src/mock-server.ts",
    "content": "import { GraphQlPayload } from \"@grafoo/types\";\nimport fetchMock from \"fetch-mock\";\nimport fs from \"fs\";\nimport { graphql } from \"graphql\";\nimport { makeExecutableSchema } from \"@graphql-tools/schema\";\nimport path from \"path\";\nimport { v4 as uuid } from \"uuid\";\nimport setupDB from \"./db\";\n\nlet db = setupDB();\n\nlet typeDefs = fs.readFileSync(path.join(__dirname, \"..\", \"schema.graphql\"), \"utf-8\");\n\nlet Query = {\n  author(_, args) {\n    return db.data.authors.find((author) => author.id === args.id);\n  },\n  authors() {\n    return db.data.authors;\n  },\n  post(_, args) {\n    return db.data.posts.find((author) => author.id === args.id);\n  },\n  posts() {\n    return db.data.posts;\n  }\n};\n\nlet Mutation = {\n  createAuthor(_, args) {\n    let newAuthor = Object.assign({}, args, { id: uuid() });\n\n    db.data.authors.push(newAuthor);\n\n    db.write();\n\n    return newAuthor;\n  },\n  updateAuthor(_, args) {\n    let author = Object.assign(\n      db.data.authors.find((author) => author.id === args.id),\n      args\n    );\n\n    db.write();\n\n    return author;\n  },\n  deleteAuthor(_, args) {\n    let author = db.data.authors.find(args);\n\n    db.data.authors = db.data.authors.filter((a) => a.id !== author.id);\n    db.data.posts = db.data.posts.filter((p) => p.author !== author.id);\n\n    db.write();\n\n    return author;\n  },\n  createPost(_, args) {\n    let newPost = Object.assign({}, args, { id: uuid() });\n\n    db.data.posts.push(newPost);\n\n    db.write();\n\n    return newPost;\n  },\n  updatePost(_, args) {\n    let post = Object.assign(\n      db.data.posts.find((author) => author.id === args.id),\n      args\n    );\n\n    db.write();\n\n    return post;\n  },\n  deletePost(_, args) {\n    let post = db.data.posts.find(args);\n\n    db.data.posts = db.data.posts.filter((p) => p.id !== args.id);\n\n    db.write();\n\n    return post;\n  }\n};\n\nlet Author = {\n  posts(author) {\n    let s = author.posts\n      ? author.posts.map((id) => db.data.posts.find((post) => post.id === id))\n      : null;\n\n    return s;\n  }\n};\n\nlet Post = {\n  author(post) {\n    return db.data.authors.find((author) => author.id === post.author);\n  }\n};\n\nlet resolvers = {\n  Query: Query,\n  Mutation: Mutation,\n  Author: Author,\n  Post: Post\n};\n\nlet schema = makeExecutableSchema({ typeDefs: typeDefs, resolvers: resolvers });\n\ninterface ExecuteQueryArg {\n  query: string;\n  variables?: {\n    [key: string]: unknown;\n  };\n}\n\nexport function executeQuery<T>({ query, variables }: ExecuteQueryArg): Promise<GraphQlPayload<T>> {\n  // @ts-ignore\n  return graphql({ schema: schema, source: query, variableValues: variables });\n}\n\nexport async function mockQueryRequest<T>(request: ExecuteQueryArg): Promise<GraphQlPayload<T>> {\n  fetchMock.reset();\n  fetchMock.restore();\n\n  let response = await executeQuery<T>(request);\n  fetchMock.post(\"*\", response);\n\n  return response;\n}\n"
  },
  {
    "path": "packages/test-utils/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"strict\": false,\n    \"lib\": [\"esnext\", \"dom\"],\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"checkJs\": false,\n    \"downlevelIteration\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"declaration\": true,\n    \"outDir\": \"./dist\",\n    \"module\": \"esnext\",\n    \"target\": \"esnext\"\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/types/index.d.ts",
    "content": "/**\n * GrafooTransport\n */\n\nexport interface GraphQlError {\n  message: string;\n  locations: { line: number; column: number }[];\n  path: string[];\n}\n\n/**\n * T = QueryData\n */\nexport interface GraphQlPayload<T> {\n  data: T;\n  errors?: GraphQlError[];\n}\n\nexport interface Variables {\n  [key: string]: any;\n}\n\n/**\n * T = QueryData\n */\nexport type GrafooTransport = <T>(\n  query: string,\n  variables?: Variables,\n  id?: string\n) => Promise<GraphQlPayload<T>>;\n\n/**\n * Core\n */\n\nexport interface ObjectsMap {\n  [key: string]: Record<string, unknown>;\n}\n\nexport interface PathsMap {\n  [key: string]: {\n    data: { [key: string]: any };\n    objects: string[];\n  };\n}\n\nexport type Listener = (objects: ObjectsMap) => void;\n\nexport interface InitialState {\n  objectsMap: ObjectsMap;\n  pathsMap: PathsMap;\n}\n\nexport interface GrafooClient {\n  execute: <T>(grafooObject: GrafooObject, variables?: Variables) => Promise<GraphQlPayload<T>>;\n  listen: (listener: Listener) => () => void;\n  write: {\n    <T>(grafooObject: GrafooObject, variables: Variables, data: T | { data: T }): void;\n    <T>(grafooObject: GrafooObject, data: T | { data: T }): void;\n  };\n  read: <T>(\n    grafooObject: GrafooObject,\n    variables?: Variables\n  ) => { data?: T; objects?: ObjectsMap; partial?: boolean };\n  flush: () => InitialState;\n  reset: () => void;\n}\n\nexport interface GrafooClientOptions {\n  initialState?: InitialState;\n  idFields?: Array<string>;\n}\n\nexport interface GrafooObject {\n  frags?: {\n    [key: string]: string;\n  };\n  paths: {\n    [key: string]: {\n      name: string;\n      args: string[];\n    };\n  };\n  query: string;\n  id?: string;\n}\n\n/**\n * Bindings\n */\n\n/**\n * T = Query\n * U = Mutations\n */\nexport interface GrafooBindings<T, U> {\n  getState(): GrafooBoundState & T & GrafooBoundMutations<U>;\n  unbind(): void;\n  load(variables?: Variables): void;\n}\n\nexport interface GrafooBoundState {\n  client: GrafooClient;\n  errors?: GraphQlError[];\n  load?: (variables?: Variables) => void;\n  loaded?: boolean;\n  loading?: boolean;\n}\n\n/**\n * T = Query\n * U = Mutations\n */\nexport type UpdateFn<T, U> = (props: T, data?: U) => T;\n\n/**\n * T = Query\n */\nexport type OptimisticUpdateFn<T> = (props: T, variables?: Variables) => T;\n\n/**\n * T = Query\n * U = Mutations\n * V = keyof Mutation\n */\nexport type GrafooMutations<T, U> = {\n  [V in keyof U]: {\n    query: GrafooObject;\n    optimisticUpdate?: OptimisticUpdateFn<T>;\n    update?: UpdateFn<T, U[V]>;\n  };\n};\n\nexport interface Context {\n  client: GrafooClient;\n}\n\n/**\n * T = Mutations\n * U = keyof Mutations\n */\nexport type GrafooBoundMutations<T> = {\n  [U in keyof T]: (variables?: Variables) => Promise<GraphQlPayload<T[U]>>;\n};\n\n/**\n * T = Query\n * U = Mutations\n */\nexport interface GrafooConsumerProps<T, U> {\n  query?: GrafooObject;\n  variables?: Variables;\n  mutations?: GrafooMutations<T, U>;\n  skip?: boolean;\n}\n"
  },
  {
    "path": "packages/types/package.json",
    "content": "{\n  \"name\": \"@grafoo/types\",\n  \"version\": \"1.4.2\",\n  \"description\": \"grafoo client typescript definitions\",\n  \"repository\": \"https://github.com/grafoojs/grafoo/tree/master/packages/types\",\n  \"types\": \"index.d.ts\",\n  \"author\": \"malbernaz<albernazmiguel@gmail.com>\",\n  \"license\": \"MIT\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"gitHead\": \"0bc67d8b398884a1f387a1813e485d2c5318b974\"\n}\n"
  },
  {
    "path": "packages/types/readme.md",
    "content": "# `@grafoo/types`\n\n<p><i>Grafoo Typescript Definitions</i></p>\n\n<p>\n  <a href=https://circleci.com/gh/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/circleci/project/github/grafoojs/grafoo/master.svg?label=build\n      alt=build\n    />\n  </a>\n  <a href=https://codecov.io/github/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/codecov/c/github/grafoojs/grafoo/master.svg\n      alt=\"coverage\"\n    />\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/types>\n    <img\n      src=https://img.shields.io/npm/v/@grafoo/types.svg\n      alt=npm\n    >\n  </a>\n  <a href=https://www.npmjs.com/package/@grafoo/types>\n    <img\n      src=https://img.shields.io/npm/dm/@grafoo/types.svg\n      alt=downloads\n    >\n  </a>\n  <a href=https://prettier.io>\n    <img\n      src=https://img.shields.io/badge/code_style-prettier-ff69b4.svg\n      alt=\"code style: prettier\"\n    />\n  </a>\n  <a href=https://lernajs.io>\n    <img\n      src=https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg\n      alt=\"mantained with: lerna\"\n    />\n  </a>\n  <a href=https://grafoo-slack.herokuapp.com>\n    <img\n      src=https://grafoo-slack.herokuapp.com/badge.svg\n      alt=\"slack\"\n    />\n  </a>\n</p>\n\n## Install\n\n```\n$ npm i @grafoo/types\n```\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "readme.md",
    "content": "<h1 align=center>\n  <br />\n  <img src=\"https://raw.githubusercontent.com/grafoojs/grafoo/master/logo.svg\" alt=Grafoo />\n  <br />\n  <br />\n</h1>\n\n<p align=center><i>A GraphQL Client and Toolkit</i></p>\n\n<p align=center>\n  <a href=https://circleci.com/gh/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/circleci/project/github/grafoojs/grafoo/master.svg?label=build\n      alt=build\n    />\n  </a>\n  <a href=https://codecov.io/github/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/codecov/c/github/grafoojs/grafoo/master.svg\n      alt=\"coverage\"\n    />\n  </a>\n  <a href=https://github.com/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/npm/v/@grafoo/babel-plugin.svg\n      alt=npm\n    >\n  </a>\n  <a href=https://github.com/grafoojs/grafoo>\n    <img\n      src=https://img.shields.io/npm/dm/@grafoo/babel-plugin.svg\n      alt=downloads\n    >\n  </a>\n  <a href=https://prettier.io>\n    <img\n      src=https://img.shields.io/badge/code_style-prettier-ff69b4.svg\n      alt=\"code style: prettier\"\n    />\n  </a>\n  <a href=https://lernajs.io>\n    <img\n      src=https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg\n      alt=\"mantained with: lerna\"\n    />\n  </a>\n  <a href=https://grafoo-slack.herokuapp.com>\n    <img\n      src=https://grafoo-slack.herokuapp.com/badge.svg\n      alt=\"slack\"\n    />\n  </a>\n</p>\n\nGrafoo is a GraphQL client that tries to be different by adopting a **simpler API**, without giving up of a **good caching strategy**.\n\n## Some useful information\n\n- **It's a multiple paradigm library**. So far we have **view layer integrations** for [react](https://github.com/grafoojs/grafoo/tree/master/packages/react) and [preact](https://github.com/grafoojs/grafoo/tree/master/packages/preact) and there are more to come.\n- **It's not just a HTTP client**. It comes with a sophisticated caching system under the hood to make sure your data is consistent across your app.\n- **It's build time dependent**. A important piece of Grafoo is it's **babel** plugin that compiles your queries based on the schema your app consumes.\n- **It's environment agnostic**. Apart from the browser you can run Grafoo on the **server** and even on **native** with react.\n\n## Why should I use this\n\nMany of the work that has been put into this project came from borrowed ideas and concepts that are present in the GraphQL clients we have today. Grafoo wants to stand apart from the others trying to be in that sweet spot between **simplicity** and **usability**. Moreover, most of the benefits this library brings to the table are related to the fact that it does a lot at build time. It's **fast**, because it spares runtime computation and it's really **small** (something like **~1.6kb** for core and react) because it does not ship with a GraphQL parser.\n\n## Example applications\n\nYou can refer to examples in [this repository](https://github.com/grafoojs/grafoo-examples).\n\n## Basic usage\n\n### Installation\n\nThe basic packages you'll have to install in order to use Grafoo are core and babel-plugin.\n\n```\n$ npm i @grafoo/core && npm i -D @grafoo/babel-plugin\n```\n\n### Configure babel\n\nIn `@grafoo/babel-plugin` the option `schema` is a path to a GraphQL schema in your file system relative to the root of your project and `idFields` is an array of strings that represent the fields that Grafoo will automatically insert on your queries to build unique identifiers in order to normalize the cache. **Both options are required**.\n\n```json\n{\n  \"plugins\": [\n    [\n      \"@grafoo/babel-plugin\",\n      {\n        \"schema\": \"schema.graphql\",\n        \"idFields\": [\"id\"]\n      }\n    ]\n  ]\n}\n```\n\n### Writing your app\n\nFrom `@grafoo/core` you will import the factory that creates the client instance and from submodule `@grafoo/core/tag` you'll import the `graphql` or `gql` tag that will be compiled at build time.\n\n```js\nimport createClient from \"@grafoo/core\";\nimport gql from \"@grafoo/core/tag\";\n\nfunction fetchQuery(query, variables) {\n  const init = {\n    method: \"POST\",\n    body: JSON.stringify({ query, variables }),\n    headers: {\n      \"content-type\": \"application/json\"\n    }\n  };\n\n  return fetch(\"http://some.graphql.api\", init).then(res => res.json());\n}\n\nconst client = createClient(fetchQuery);\n\nconst USER_QUERY = gql`\n  query($id: ID!) {\n    user(id: $id) {\n      name\n    }\n  }\n`;\n\nconst variables = { id: 123 };\n\nclient.execute(USER_QUERY, variables).then(data => {\n  // Write to cache\n  client.write(USER_QUERY, variables, data);\n\n  // Do whatever with returned data\n  console.log(data);\n\n  // Read from cache at a later stage\n  console.log(client.read(USER_QUERY, variables));\n});\n\n// If you wish to reset (clear) the cache:\nclient.reset();\n```\n\n### With a framework\n\nHere is how it would go for you to write a simple react app.\n\n#### `index.js`\n\n```jsx\nimport React from \"react\";\nimport ReactDom from \"react-dom\";\nimport createClient from \"@grafoo/core\";\nimport { Provider } from \"@grafoo/react\";\n\nimport Posts from \"./Posts\";\n\nfunction fetchQuery(query, variables) {\n  const init = {\n    method: \"POST\",\n    body: JSON.stringify({ query, variables }),\n    headers: {\n      \"content-type\": \"application/json\"\n    }\n  };\n\n  return fetch(\"http://some.graphql.api\", init).then(res => res.json());\n}\n\nconst client = createClient(fetchQuery);\n\nReactDom.render(\n  <Provider client={client}>\n    <Posts />\n  </Provider>,\n  document.getElementById(\"mnt\")\n);\n```\n\n#### `Posts.js`\n\n```jsx\nimport React from \"react\";\nimport gql from \"@grafoo/core/tag\";\nimport { Consumer } from \"@grafoo/react\";\n\nconst ALL_POSTS = gql`\n  query getPosts($orderBy: PostOrderBy) {\n    allPosts(orderBy: $orderBy) {\n      title\n      content\n      createdAt\n      updatedAt\n    }\n  }\n`;\n\nexport default function Posts() {\n  return (\n    <Consumer query={ALL_POSTS} variables={{ orderBy: \"createdAt_DESC\" }}>\n      {({ client, load, loaded, loading, errors, allPosts }) => (\n        <marquee>👆 do whatever you want with the variables above 👆</marquee>\n      )}\n    </Consumer>\n  );\n}\n```\n\n### Mutations\n\n```jsx\nimport React from \"react\";\nimport gql from \"@grafoo/core/tag\";\nimport { Consumer } from \"@grafoo/react\";\n\nconst ALL_POSTS = gql`\n  query getPosts($orderBy: PostOrderBy) {\n    allPosts(orderBy: $orderBy) {\n      title\n      content\n      createdAt\n      updatedAt\n    }\n  }\n`;\n\nconst CREATE_POST = gql`\n  mutation createPost($content: String, $title: String, $authorId: ID) {\n    createPost(content: $content, title: $title, authorId: $authorId) {\n      title\n      content\n      createdAt\n      updatedAt\n    }\n  }\n`;\n\nconst mutations = {\n  createPost: {\n    query: CREATE_POST,\n    optimisticUpdate: ({ allPosts }, variables) => ({\n      allPosts: [{ ...variables, id: \"tempID\" }, ...allPosts]\n    }),\n    update: ({ allPosts }, data) => ({\n      allPosts: allPosts.map(p => (p.id === \"tempID\" ? data.createPost : p))\n    })\n  }\n};\n\nconst submit = mutate => event => {\n  event.preventDefault();\n\n  const { title, content } = event.target.elements;\n\n  mutate({ title: title.value, content: content.value });\n};\n\nexport default function PostForm() {\n  return (\n    <Consumer query={ALL_POSTS} variables={{ orderBy: \"createdAt_DESC\" }} mutations={mutations}>\n      {({ createPost }) => (\n        <form onSubmit={submit(createPost)}>\n          <input name=\"title\" />\n          <textarea name=\"content\" />\n          <button>submit</button>\n        </form>\n      )}\n    </Consumer>\n  );\n}\n```\n\n## LICENSE\n\n[MIT](https://github.com/grafoojs/grafoo/blob/master/LICENSE)\n"
  },
  {
    "path": "scripts/build.js",
    "content": "let { readdirSync } = require(\"fs\");\nlet { join } = require(\"path\");\nlet { exec } = require(\"child_process\");\n\nlet pkgsRoot = join(__dirname, \"..\", \"packages\");\n\nlet withDeps = [\"react\", \"preact\"];\nlet noDeps = readdirSync(pkgsRoot).filter((x) => !withDeps.some((y) => y === x));\n\nlet command = exec(\n  [\n    `lerna run --scope \"@grafoo/*(${noDeps.join(\"|\")})\" build`,\n    `lerna run --scope \"@grafoo/*(${withDeps.join(\"|\")})\" build`,\n  ].join(\" && \")\n);\n\ncommand.stdout.pipe(process.stdout);\ncommand.stderr.pipe(process.stderr);\n"
  },
  {
    "path": "scripts/jest-setup.js",
    "content": "let fs = require(\"fs\");\nlet path = require(\"path\");\nlet { transform } = require(\"@babel/core\");\n\nlet config = Object.assign(\n  { sourceMap: \"inline\", ast: false },\n  JSON.parse(fs.readFileSync(path.join(process.cwd(), \".babelrc\"), \"utf-8\"))\n);\n\nmodule.exports.process = (src, path) =>\n  transform(src, Object.assign({}, config, { filename: path }));\n"
  },
  {
    "path": "scripts/resolver.js",
    "content": "let { resolve } = require(\"resolve.exports\");\n\nmodule.exports = (request, options) =>\n  options.defaultResolver(request, {\n    ...options,\n    packageFilter: (package) => ({\n      ...package,\n      main: package.main || resolve(package, \".\")\n    })\n  });\n"
  }
]