Generate GQL client (#259)

* Add GQL client generation

* Add subscription, events, eventsInRange in client gen

* Add mutation in client gen and return event tx info from indexer

* Capitalize class names for storage-mode entities
This commit is contained in:
prathamesh0 2021-10-04 11:04:06 +05:30 committed by GitHub
parent a9067c4374
commit d3971b5258
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 352 additions and 22 deletions

View File

@ -21,6 +21,7 @@
"dependencies": {
"@poanet/solidity-flattener": "https://github.com/vulcanize/solidity-flattener.git",
"@solidity-parser/parser": "^0.13.2",
"gql-generator": "https://github.com/vulcanize/gql-generator.git",
"graphql": "^15.5.0",
"graphql-compose": "^9.0.3",
"handlebars": "^4.7.7",

View File

@ -0,0 +1,79 @@
//
// Copyright 2021 Vulcanize, Inc.
//
import fs from 'fs';
import path from 'path';
import assert from 'assert';
import Handlebars from 'handlebars';
import { Writable } from 'stream';
import _ from 'lodash';
import { gqlGenerate } from 'gql-generator';
import { getTsForSol } from './utils/type-mappings';
import { Param } from './utils/types';
const TEMPLATE_FILE = './templates/client-template.handlebars';
export class Client {
_queries: Array<any>;
_templateString: string;
constructor () {
this._queries = [];
this._templateString = fs.readFileSync(path.resolve(__dirname, TEMPLATE_FILE)).toString();
}
/**
* Stores the query to be passed to the template.
* @param mode Code generation mode.
* @param name Name of the query.
* @param params Parameters to the query.
* @param returnType Return type for the query.
*/
addQuery (name: string, params: Array<Param>, returnType: string): void {
// Check if the query is already added.
if (this._queries.some(query => query.name === name)) {
return;
}
const queryObject = {
name,
params: _.cloneDeep(params),
returnType
};
queryObject.params = queryObject.params.map((param) => {
const tsParamType = getTsForSol(param.type);
assert(tsParamType);
param.type = tsParamType;
return param;
});
const tsReturnType = getTsForSol(returnType);
assert(tsReturnType);
queryObject.returnType = tsReturnType;
this._queries.push(queryObject);
}
/**
* Writes the client file generated from a template to a stream and export quries.
* @param outStream A writable output stream to write the client file to.
* @param schemaContent Content of the schema for generating the queries, mutations and subscriptions.
* @param gqlDir Directory to store the generated gql queries, mutations and subscriptions.
*/
exportClient (outStream: Writable, schemaContent: string, gqlDir: string): void {
this._exportGql(schemaContent, gqlDir);
const template = Handlebars.compile(this._templateString);
const client = template({
queries: this._queries
});
outStream.write(client);
}
_exportGql (schemaContent: string, gqlDir: string): void {
gqlGenerate(schemaContent, gqlDir);
}
}

View File

@ -36,11 +36,18 @@ export class Database {
}
const queryObject = {
name: name,
name,
entityName: '',
params: _.cloneDeep(params),
returnType: returnType
returnType
};
// eth_call mode: Capitalize first letter of entity name (balanceOf -> BalanceOf).
// storage mode: Capiltalize second letter of entity name (_balances -> _Balances).
queryObject.entityName = (name.charAt(0) === '_')
? `_${name.charAt(1).toUpperCase()}${name.slice(2)}`
: `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
queryObject.params = queryObject.params.map((param) => {
const tsParamType = getTsForSol(param.type);
assert(tsParamType);

View File

@ -38,12 +38,18 @@ export class Entity {
const entityObject: any = {
// Capitalize the first letter of name.
className: `${name.charAt(0).toUpperCase()}${name.slice(1)}`,
className: '',
indexOn: [],
columns: [],
imports: []
};
// eth_call mode: Capitalize first letter of entity name (balanceOf -> BalanceOf).
// storage mode: Capiltalize second letter of entity name (_balances -> _Balances).
entityObject.className = (name.charAt(0) === '_')
? `_${name.charAt(1).toUpperCase()}${name.slice(2)}`
: `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
entityObject.imports.push(
{
toImport: new Set(['Entity', 'PrimaryGeneratedColumn', 'Column', 'Index']),

View File

@ -136,7 +136,7 @@ function generateWatcher (data: string, visitor: Visitor, argv: any) {
let outStream = outputDir
? fs.createWriteStream(path.join(outputDir, 'src/schema.gql'))
: process.stdout;
visitor.exportSchema(outStream);
const schemaContent = visitor.exportSchema(outStream);
outStream = outputDir
? fs.createWriteStream(path.join(outputDir, 'src/resolvers.ts'))
@ -234,6 +234,11 @@ function generateWatcher (data: string, visitor: Visitor, argv: any) {
ignoreOutStream = process.stdout;
}
exportLint(rcOutStream, ignoreOutStream);
outStream = outputDir
? fs.createWriteStream(path.join(outputDir, 'src/client.ts'))
: process.stdout;
visitor.exportClient(outStream, schemaContent, path.join(outputDir, 'src/gql'));
}
main().catch(err => {

View File

@ -36,9 +36,9 @@ export class Resolvers {
}
const queryObject = {
name: name,
name,
params: _.cloneDeep(params),
returnType: returnType
returnType
};
queryObject.params = queryObject.params.map((param) => {

View File

@ -104,10 +104,12 @@ export class Schema {
* Writes schema to a stream.
* @param outStream A writable output stream to write the schema to.
*/
exportSchema (outStream: Writable): void {
exportSchema (outStream: Writable): string {
// Get schema as a string from GraphQLSchema.
const schemaString = printSchema(this.buildSchema());
outStream.write(schemaString);
return schemaString;
}
/**

View File

@ -0,0 +1,70 @@
//
// Copyright 2021 Vulcanize, Inc.
//
import { gql } from '@apollo/client/core';
import { GraphQLClient, GraphQLConfig } from '@vulcanize/ipld-eth-client';
import { queries, mutations, subscriptions } from './gql';
export class Client {
_config: GraphQLConfig;
_client: GraphQLClient;
constructor (config: GraphQLConfig) {
this._config = config;
this._client = new GraphQLClient(config);
}
{{#each queries as | query |}}
// eslint-disable-next-line camelcase
async get{{capitalize query.name tillIndex=1}} (blockHash: string, contractAddress: string
{{~#each query.params}}, {{this.name}}: {{this.type~}} {{/each}}): Promise<any> {
const { {{query.name}} } = await this._client.query(
gql(queries.{{query.name}}),
{ blockHash, contractAddress
{{~#each query.params}}, {{this.name~}} {{/each}} }
);
return {{query.name}};
}
{{/each}}
async getEvents (blockHash: string, contractAddress: string, name: string): Promise<any> {
const { events } = await this._client.query(
gql(queries.events),
{ blockHash, contractAddress, name }
);
return events;
}
async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise<any> {
const { eventsInRange } = await this._client.query(
gql(queries.eventsInRange),
{ fromBlockNumber, toBlockNumber }
);
return eventsInRange;
}
async watchContract (contractAddress: string, startingBlock?: number): Promise<any> {
const { watchContract } = await this._client.mutate(
gql(mutations.watchContract),
{ contractAddress, startingBlock }
);
return watchContract;
}
async watchEvents (onNext: (value: any) => void): Promise<ZenObservable.Subscription> {
return this._client.subscribe(
gql(subscriptions.onEvent),
({ data }) => {
onNext(data.onEvent);
}
);
}
}

View File

@ -12,8 +12,9 @@ import { Contract } from './entity/Contract';
import { Event } from './entity/Event';
import { SyncStatus } from './entity/SyncStatus';
import { BlockProgress } from './entity/BlockProgress';
{{#each queries as | query |}}
import { {{capitalize query.name tillIndex=1}} } from './entity/{{capitalize query.name tillIndex=1}}';
import { {{query.entityName}} } from './entity/{{query.entityName}}';
{{/each}}
export class Database {
@ -50,8 +51,8 @@ export class Database {
{{/if}}
async get{{capitalize query.name tillIndex=1}} ({ blockHash, contractAddress
{{~#each query.params}}, {{this.name~}} {{/each}} }: { blockHash: string, contractAddress: string
{{~#each query.params}}, {{this.name~}}: {{this.type~}} {{/each}} }): Promise<{{capitalize query.name tillIndex=1}} | undefined> {
return this._conn.getRepository({{capitalize query.name tillIndex=1}})
{{~#each query.params}}, {{this.name~}}: {{this.type~}} {{/each}} }): Promise<{{query.entityName}} | undefined> {
return this._conn.getRepository({{query.entityName}})
.findOne({
blockHash,
contractAddress{{#if query.params.length}},{{/if}}
@ -69,8 +70,8 @@ export class Database {
// eslint-disable-next-line @typescript-eslint/ban-types
{{/if}}
async save{{capitalize query.name tillIndex=1}} ({ blockHash, contractAddress
{{~#each query.params}}, {{this.name~}} {{/each}}, value, proof }: DeepPartial<{{capitalize query.name tillIndex=1}}>): Promise<{{capitalize query.name tillIndex=1}}> {
const repo = this._conn.getRepository({{capitalize query.name tillIndex=1}});
{{~#each query.params}}, {{this.name~}} {{/each}}, value, proof }: DeepPartial<{{query.entityName}}>): Promise<{{query.entityName}}> {
const repo = this._conn.getRepository({{query.entityName}});
const entity = repo.create({ blockHash, contractAddress
{{~#each query.params}}, {{this.name~}} {{/each}}, value, proof });
return repo.save(entity);
@ -194,7 +195,7 @@ export class Database {
_setPropColMaps (): void {
{{#each queries as | query |}}
this._propColMaps.{{capitalize query.name tillIndex=1}} = this._getPropertyColumnMapForEntity('{{capitalize query.name tillIndex=1}}');
this._propColMaps.{{query.entityName}} = this._getPropertyColumnMapForEntity('{{query.entityName}}');
{{/each}}
}
}

View File

@ -59,18 +59,24 @@ export const main = async (): Promise<any> => {
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
const cache = await getCache(cacheConfig);
const ethClient = new EthClient({
gqlEndpoint: gqlPostgraphileEndpoint,
gqlSubscriptionEndpoint: gqlPostgraphileEndpoint,
cache
});
const postgraphileClient = new EthClient({
gqlEndpoint: gqlPostgraphileEndpoint,
cache
});
const ethProvider = getDefaultProvider(rpcProviderEndpoint);
// Note: In-memory pubsub works fine for now, as each watcher is a single process anyway.
// Later: https://www.apollographql.com/docs/apollo-server/data/subscriptions/#production-pubsub-libraries
const pubsub = new PubSub();
const indexer = new Indexer(db, ethClient, ethProvider);
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
const { dbConnectionString, maxCompletionLagInSecs } = jobQueueConfig;
assert(dbConnectionString, 'Missing job queue db connection string');

View File

@ -37,6 +37,9 @@ export type ResultEvent = {
};
tx: {
hash: string;
from: string;
to: string;
index: number;
};
contract: string;
@ -51,19 +54,21 @@ export class Indexer {
_db: Database
_ethClient: EthClient
_ethProvider: BaseProvider
_postgraphileClient: EthClient;
_baseIndexer: BaseIndexer
_abi: JsonFragment[]
_storageLayout: StorageLayout
_contract: ethers.utils.Interface
constructor (db: Database, ethClient: EthClient, ethProvider: BaseProvider) {
constructor (db: Database, ethClient: EthClient, postgraphileClient: EthClient, ethProvider: BaseProvider) {
assert(db);
assert(ethClient);
this._db = db;
this._ethClient = ethClient;
this._ethProvider = ethProvider;
this._postgraphileClient = postgraphileClient;
this._baseIndexer = new BaseIndexer(this._db, this._ethClient);
const { abi, storageLayout } = artifacts;
@ -80,6 +85,7 @@ export class Indexer {
getResultEvent (event: Event): ResultEvent {
const block = event.block;
const eventFields = JSONbig.parse(event.eventInfo);
const { tx } = JSON.parse(event.extraInfo);
return {
block: {
@ -90,7 +96,10 @@ export class Indexer {
},
tx: {
hash: event.txHash
hash: event.txHash,
from: tx.src,
to: tx.dst,
index: tx.index
},
contract: event.contract,
@ -282,6 +291,23 @@ export class Indexer {
assert(blockHash);
let { block, logs } = await this._ethClient.getLogs({ blockHash });
const {
allEthHeaderCids: {
nodes: [
{
ethTransactionCidsByHeaderId: {
nodes: transactions
}
}
]
}
} = await this._postgraphileClient.getBlockWithTransactions({ blockHash });
const transactionMap = transactions.reduce((acc: {[key: string]: any}, transaction: {[key: string]: any}) => {
acc[transaction.txHash] = transaction;
return acc;
}, {});
const dbEvents: Array<DeepPartial<Event>> = [];
for (let li = 0; li < logs.length; li++) {
@ -305,7 +331,8 @@ export class Indexer {
if (status) {
let eventName = UNKNOWN_EVENT_NAME;
let eventInfo = {};
const extraInfo = { topics, data };
const tx = transactionMap[txHash];
const extraInfo = { topics, data, tx };
const contract = ethers.utils.getAddress(address);
const watchedContract = await this.isWatchedContract(contract);

View File

@ -94,14 +94,20 @@ export const main = async (): Promise<any> => {
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
const cache = await getCache(cacheConfig);
const ethClient = new EthClient({
gqlEndpoint: gqlApiEndpoint,
gqlSubscriptionEndpoint: gqlPostgraphileEndpoint,
cache
});
const postgraphileClient = new EthClient({
gqlEndpoint: gqlPostgraphileEndpoint,
cache
});
const ethProvider = getDefaultProvider(rpcProviderEndpoint);
const indexer = new Indexer(db, ethClient, ethProvider);
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
assert(jobQueueConfig, 'Missing job queue config');

View File

@ -56,15 +56,21 @@ export const main = async (): Promise<any> => {
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
const cache = await getCache(cacheConfig);
const ethClient = new EthClient({
gqlEndpoint: gqlApiEndpoint,
gqlSubscriptionEndpoint: gqlPostgraphileEndpoint,
cache
});
const postgraphileClient = new EthClient({
gqlEndpoint: gqlPostgraphileEndpoint,
cache
});
const ethProvider = getDefaultProvider(rpcProviderEndpoint);
const indexer = new Indexer(db, ethClient, ethProvider);
const indexer = new Indexer(db, ethClient, postgraphileClient, ethProvider);
// Note: In-memory pubsub works fine for now, as each watcher is a single process anyway.
// Later: https://www.apollographql.com/docs/apollo-server/data/subscriptions/#production-pubsub-libraries

View File

@ -4,3 +4,4 @@
declare module '@poanet/solidity-flattener';
declare module 'solc';
declare module 'gql-generator';

View File

@ -11,6 +11,7 @@ import { Entity } from './entity';
import { Indexer } from './indexer';
import { Resolvers } from './resolvers';
import { Schema } from './schema';
import { Client } from './client';
export class Visitor {
_schema: Schema;
@ -18,6 +19,7 @@ export class Visitor {
_indexer: Indexer;
_entity: Entity;
_database: Database;
_client: Client;
constructor () {
this._schema = new Schema();
@ -25,6 +27,7 @@ export class Visitor {
this._indexer = new Indexer();
this._entity = new Entity();
this._database = new Database();
this._client = new Client();
}
/**
@ -46,6 +49,7 @@ export class Visitor {
this._indexer.addQuery(MODE_ETH_CALL, name, params, returnType);
this._entity.addQuery(name, params, returnType);
this._database.addQuery(name, params, returnType);
this._client.addQuery(name, params, returnType);
}
}
@ -78,6 +82,7 @@ export class Visitor {
this._indexer.addQuery(MODE_STORAGE, name, params, returnType);
this._entity.addQuery(name, params, returnType);
this._database.addQuery(name, params, returnType);
this._client.addQuery(name, params, returnType);
}
/**
@ -97,9 +102,10 @@ export class Visitor {
/**
* Writes schema to a stream.
* @param outStream A writable output stream to write the schema to.
* @returns The schema string.
*/
exportSchema (outStream: Writable): void {
this._schema.exportSchema(outStream);
exportSchema (outStream: Writable): string {
return this._schema.exportSchema(outStream);
}
/**
@ -134,4 +140,14 @@ export class Visitor {
exportDatabase (outStream: Writable): void {
this._database.exportDatabase(outStream);
}
/**
* Writes the client file generated from a template to a stream and export quries.
* @param outStream A writable output stream to write the client file to.
* @param schemaContent Content of the schema for generating the queries, mutations and subscriptions.
* @param gqlDir Directory to store the generated gql queries, mutations and subscriptions.
*/
exportClient (outStream: Writable, schemaContent: string, gqlDir: string): void {
this._client.exportClient(outStream, schemaContent, gqlDir);
}
}

View File

@ -112,4 +112,10 @@ export class GraphQLClient {
return result;
}
async mutate (mutation: DocumentNode | TypedDocumentNode, variables: { [key: string]: any }): Promise<any> {
const { data: result } = await this._client.mutate({ mutation, variables });
return result;
}
}

View File

@ -3364,11 +3364,23 @@ array-includes@^3.1.3:
get-intrinsic "^1.1.1"
is-string "^1.0.5"
array-union@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
dependencies:
array-uniq "^1.0.1"
array-union@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
array-uniq@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
array-unique@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
@ -4865,7 +4877,7 @@ commander@3.0.2, commander@^3.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
commander@^2.20.3:
commander@^2.15.1, commander@^2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@ -5468,6 +5480,18 @@ defined@~1.0.0:
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
del@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
dependencies:
globby "^6.1.0"
is-path-cwd "^1.0.0"
is-path-in-cwd "^1.0.0"
p-map "^1.1.1"
pify "^3.0.0"
rimraf "^2.2.8"
delay@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d"
@ -7389,6 +7413,18 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.3:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-dirs@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d"
@ -7440,6 +7476,17 @@ globby@^11.0.1, globby@^11.0.2:
merge2 "^1.3.0"
slash "^3.0.0"
globby@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
dependencies:
array-union "^1.0.1"
glob "^7.0.3"
object-assign "^4.0.1"
pify "^2.0.0"
pinkie-promise "^2.0.0"
got@9.6.0, got@^9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
@ -7477,6 +7524,14 @@ got@^7.1.0:
url-parse-lax "^1.0.0"
url-to-options "^1.0.1"
"gql-generator@https://github.com/vulcanize/gql-generator.git":
version "1.0.13"
resolved "https://github.com/vulcanize/gql-generator.git#0b818b1d41bd383c860d1c88082b7b1f5831c272"
dependencies:
commander "^2.15.1"
del "^3.0.0"
graphql "^0.13.2"
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
@ -7556,6 +7611,13 @@ graphql-type-json@0.3.2:
resolved "https://registry.yarnpkg.com/graphql-type-json/-/graphql-type-json-0.3.2.tgz#f53a851dbfe07bd1c8157d24150064baab41e115"
integrity sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==
graphql@^0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.13.2.tgz#4c740ae3c222823e7004096f832e7b93b2108270"
integrity sha512-QZ5BL8ZO/B20VA8APauGBg3GyEgZ19eduvpLWoq5x7gMmWnHoy8rlQWPLmWgFvo1yNgjSEFMesmS4R6pPr7xog==
dependencies:
iterall "^1.2.1"
graphql@^14.0.2:
version "14.7.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.7.0.tgz#7fa79a80a69be4a31c27dda824dc04dac2035a72"
@ -8372,6 +8434,25 @@ is-object@^1.0.1:
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf"
integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==
is-path-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
is-path-in-cwd@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==
dependencies:
is-path-inside "^1.0.0"
is-path-inside@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
dependencies:
path-is-inside "^1.0.1"
is-path-inside@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
@ -10637,6 +10718,11 @@ p-map-series@^2.1.0:
resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2"
integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==
p-map@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
p-map@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
@ -10902,6 +10988,11 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
path-is-inside@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"