pax_global_header00006660000000000000000000000064147563340700014523gustar00rootroot0000000000000052 comment=92eac66857578715c77f18434a03f4b280c14d3f graphql-go-1.6.0/000077500000000000000000000000001475633407000135705ustar00rootroot00000000000000graphql-go-1.6.0/.github/000077500000000000000000000000001475633407000151305ustar00rootroot00000000000000graphql-go-1.6.0/.github/workflows/000077500000000000000000000000001475633407000171655ustar00rootroot00000000000000graphql-go-1.6.0/.github/workflows/codeql-analysis.yml000066400000000000000000000044361475633407000230070ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '16 17 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 graphql-go-1.6.0/.gitignore000066400000000000000000000001541475633407000155600ustar00rootroot00000000000000/.idea /.vscode /internal/validation/testdata/graphql-js /internal/validation/testdata/node_modules /vendor graphql-go-1.6.0/.golangci.yml000066400000000000000000000007351475633407000161610ustar00rootroot00000000000000run: timeout: 5m linters-settings: gofmt: simplify: true govet: check-shadowing: true enable-all: true disable: - fieldalignment - deepequalerrors # remove later linters: disable-all: true enable: - gofmt - gosimple - govet - ineffassign - exportloopref - staticcheck - unconvert - unused - misspell - goimports issues: exclude-rules: - linters: - unused path: "graphql_test.go"graphql-go-1.6.0/.semaphore/000077500000000000000000000000001475633407000156315ustar00rootroot00000000000000graphql-go-1.6.0/.semaphore/semaphore.yml000066400000000000000000000013621475633407000203410ustar00rootroot00000000000000version: v1.0 name: Go agent: machine: type: e1-standard-2 os_image: ubuntu2004 blocks: - name: Style Check task: jobs: - name: fmt commands: - sem-version go 1.20 - checkout - ./scripts/golangci_install.sh -b $(go env GOPATH)/bin v1.51.0 - export PATH=$(go env GOPATH)/bin:$PATH - golangci-lint run ./... - name: Test & Build task: prologue: commands: - sem-version go 1.20 - export PATH=$(go env GOPATH)/bin:$PATH - checkout - go version jobs: - name: Test commands: - go test ./... - name: Build commands: - go build -v . graphql-go-1.6.0/CHANGELOG.md000066400000000000000000000036111475633407000154020ustar00rootroot00000000000000CHANGELOG [v1.5.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.5.0) Release v1.5.0 * [FEATURE] Add specifiedBy directive in #532 * [IMPROVEMENT] In this release we improve validation for primitive values, directives, repeat directives, #515, #516, #525, #527 * [IMPROVEMENT] Fix minor unreachable code caused by t.Fatalf #530 * [BUG] Fix __type queries sometimes not returning data in #540 * [BUG] Allow deprecated directive on arguments by @pavelnikolov in #541 * [DOCS] Add array input example #536 [v1.4.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.4.0) Release v1.4.0 * [FEATURE] Add basic first step for Apollo Federation. This does NOT include full subgraph specification. This PR adds support only for `_service` schema level field. This library is long way from supporting the full sub-graph spec and we do not plan to implement that any time soon. [v1.3.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.3.0) Release v1.3.0 * [FEATURE] Support custom panic handler #468 * [FEATURE] Support interfaces implementing interfaces #471 * [BUG] Support parsing nanoseconds time properly #486 * [BUG] Fix a bug in maxDepth fragment spread logic #492 [v1.2.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.2.0) Release v1.2.0 * [DOCS] Added examples of how to add JSON map as input scalar type. The goal of this change was to improve documentation #467 [v1.1.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.1.0) Release v1.1.0 * [FEATURE] Add types package #437 * [FEATURE] Expose `packer.Unmarshaler` as `decode.Unmarshaler` to the public #450 * [FEATURE] Add location fields to type definitions #454 * [FEATURE] `errors.Errorf` preserves original error similar to `fmt.Errorf` #456 * [BUGFIX] Fix duplicated __typename in response (fixes #369) #443 [v1.0.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.0.0) Initial release graphql-go-1.6.0/CODE_OF_CONDUCT.md000066400000000000000000000056521475633407000163770ustar00rootroot00000000000000## Community Code of Conduct ### Contributor Code of Conduct As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in the GraphQL Go community a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. ## Scope This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. ## Our Standards Examples of behavior that contributes to a positive environment include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. ## Reporting For incidents occurring in the Graph Gophers community, contact @pavelnikolov in [the Gophers Slack](https://gophers.slack.com/) or alternatively you can contact me [at] pavelnikolov [dot] net. You can expect a response within few business days. ## Enforcement The Graph Gophers maintainers enforce code of conduct issues for the graphql-go project as well other projects under the graph-gophers github organization. We try to resolve incidents without punishment, but may remove people from the project at our discretion. ## Acknowledgements This Code of Conduct is adapted from the Contributor Covenant (http://contributor-covenant.org), version 2.0 available at http://contributor-covenant.org/version/2/0/code_of_conduct/ graphql-go-1.6.0/CONTRIBUTING.md000066400000000000000000000015071475633407000160240ustar00rootroot00000000000000## Contributing - With issues: - Use the search tool before opening a new issue. - Please provide source code and commit sha if you found a bug. - Review existing issues and provide feedback or react to them. - With pull requests: - Open your pull request against `master` - Your pull request should have no more than two commits, if not you should squash them. - It should pass all tests in the available continuous integrations systems such as TravisCI. - You should add/modify tests to cover your proposed code changes. - If your pull request contains a new feature, please document it well: * Consider adding Go executable examples * Comment all new exported types if outside of the `internal` package * (optional) Mention it in the README * Add a comment in the CHANGELOG.md explaining your feature graphql-go-1.6.0/LICENSE000066400000000000000000000024171475633407000146010ustar00rootroot00000000000000Copyright (c) 2016 Richard Musiol. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. graphql-go-1.6.0/README.md000066400000000000000000000221431475633407000150510ustar00rootroot00000000000000# graphql-go [![Sourcegraph](https://sourcegraph.com/github.com/graph-gophers/graphql-go/-/badge.svg)](https://sourcegraph.com/github.com/graph-gophers/graphql-go?badge) [![Build Status](https://graph-gophers.semaphoreci.com/badges/graphql-go/branches/master.svg?style=shields)](https://graph-gophers.semaphoreci.com/projects/graphql-go) [![Go Report](https://goreportcard.com/badge/github.com/graph-gophers/graphql-go)](https://goreportcard.com/report/github.com/graph-gophers/graphql-go) [![GoDoc](https://godoc.org/github.com/graph-gophers/graphql-go?status.svg)](https://godoc.org/github.com/graph-gophers/graphql-go)

The goal of this project is to provide full support of the [October 2021 GraphQL specification](https://spec.graphql.org/October2021/) with a set of idiomatic, easy to use Go packages. While still under development (`internal` APIs are almost certainly subject to change), this library is safe for production use. ## Features - minimal API - support for `context.Context` - support for the `OpenTelemetry` and `OpenTracing` standards - schema type-checking against resolvers - resolvers are matched to the schema based on method sets (can resolve a GraphQL schema with a Go interface or Go struct). - handles panics in resolvers - parallel execution of resolvers - subscriptions - [sample WS transport](https://github.com/graph-gophers/graphql-transport-ws) ## (Some) Documentation [![GoDoc](https://godoc.org/github.com/graph-gophers/graphql-go?status.svg)](https://godoc.org/github.com/graph-gophers/graphql-go) ### Getting started In order to run a simple GraphQL server locally create a `main.go` file with the following content: ```go package main import ( "log" "net/http" graphql "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/relay" ) type query struct{} func (query) Hello() string { return "Hello, world!" } func main() { s := ` type Query { hello: String! } ` schema := graphql.MustParseSchema(s, &query{}) http.Handle("/query", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":8080", nil)) } ``` Then run the file with `go run main.go`. To test: ```sh curl -XPOST -d '{"query": "{ hello }"}' localhost:8080/query ``` For more realistic usecases check our [examples section](https://github.com/graph-gophers/graphql-go/wiki/Examples). ### Resolvers A resolver must have one method or field for each field of the GraphQL type it resolves. The method or field name has to be [exported](https://golang.org/ref/spec#Exported_identifiers) and match the schema's field's name in a non-case-sensitive way. You can use struct fields as resolvers by using `SchemaOpt: UseFieldResolvers()`. For example, ``` opts := []graphql.SchemaOpt{graphql.UseFieldResolvers()} schema := graphql.MustParseSchema(s, &query{}, opts...) ``` When using `UseFieldResolvers` schema option, a struct field will be used *only* when: - there is no method for a struct field - a struct field does not implement an interface method - a struct field does not have arguments The method has up to two arguments: - Optional `context.Context` argument. - Mandatory `*struct { ... }` argument if the corresponding GraphQL field has arguments. The names of the struct fields have to be [exported](https://golang.org/ref/spec#Exported_identifiers) and have to match the names of the GraphQL arguments in a non-case-sensitive way. The method has up to two results: - The GraphQL field's value as determined by the resolver. - Optional `error` result. Example for a simple resolver method: ```go func (r *helloWorldResolver) Hello() string { return "Hello world!" } ``` The following signature is also allowed: ```go func (r *helloWorldResolver) Hello(ctx context.Context) (string, error) { return "Hello world!", nil } ``` ### Separate resolvers for different operations > **NOTE**: This feature is not in the stable release yet. In order to use it you need to run `go get github.com/graph-gophers/graphql-go@master` and in your `go.mod` file you will have something like: > ``` > v1.5.1-0.20230216224648-5aa631d05992 > ``` > It is expected to be released in `v1.6.0` soon. The GraphQL specification allows for fields with the same name defined in different query types. For example, the schema below is a valid schema definition: ```graphql schema { query: Query mutation: Mutation } type Query { hello: String! } type Mutation { hello: String! } ``` The above schema would result in name collision if we use a single resolver struct because fields from both operations correspond to methods in the root resolver (the same Go struct). In order to resolve this issue, the library allows resolvers for query, mutation and subscription operations to be separated using the `Query`, `Mutation` and `Subscription` methods of the root resolver. These special methods are optional and if defined return the resolver for each opeartion. For example, the following is a resolver corresponding to the schema definition above. Note that there is a field named `hello` in both the query and the mutation definitions: ```go type RootResolver struct{} type QueryResolver struct{} type MutationResolver struct{} func(r *RootResolver) Query() *QueryResolver { return &QueryResolver{} } func(r *RootResolver) Mutation() *MutationResolver { return &MutationResolver{} } func (*QueryResolver) Hello() string { return "Hello query!" } func (*MutationResolver) Hello() string { return "Hello mutation!" } schema := graphql.MustParseSchema(sdl, &RootResolver{}, nil) ... ``` ### Schema Options - `UseStringDescriptions()` enables the usage of double quoted and triple quoted. When this is not enabled, comments are parsed as descriptions instead. - `UseFieldResolvers()` specifies whether to use struct field resolvers. - `MaxDepth(n int)` specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking. - `MaxParallelism(n int)` specifies the maximum number of resolvers per request allowed to run in parallel. The default is 10. - `Tracer(tracer trace.Tracer)` is used to trace queries and fields. It defaults to `noop.Tracer`. - `Logger(logger log.Logger)` is used to log panics during query execution. It defaults to `exec.DefaultLogger`. - `PanicHandler(panicHandler errors.PanicHandler)` is used to transform panics into errors during query execution. It defaults to `errors.DefaultPanicHandler`. - `DisableIntrospection()` disables introspection queries. ### Custom Errors Errors returned by resolvers can include custom extensions by implementing the `ResolverError` interface: ```go type ResolverError interface { error Extensions() map[string]interface{} } ``` Example of a simple custom error: ```go type droidNotFoundError struct { Code string `json:"code"` Message string `json:"message"` } func (e droidNotFoundError) Error() string { return fmt.Sprintf("error [%s]: %s", e.Code, e.Message) } func (e droidNotFoundError) Extensions() map[string]interface{} { return map[string]interface{}{ "code": e.Code, "message": e.Message, } } ``` Which could produce a GraphQL error such as: ```go { "errors": [ { "message": "error [NotFound]: This is not the droid you are looking for", "path": [ "droid" ], "extensions": { "code": "NotFound", "message": "This is not the droid you are looking for" } } ], "data": null } ``` ### Tracing By default the library uses `noop.Tracer`. If you want to change that you can use the OpenTelemetry or the OpenTracing implementations, respectively: ```go // OpenTelemetry tracer package main import ( "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" otelgraphql "github.com/graph-gophers/graphql-go/trace/otel" "github.com/graph-gophers/graphql-go/trace/tracer" ) // ... _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(otelgraphql.DefaultTracer())) // ... ``` Alternatively you can pass an existing trace.Tracer instance: ```go tr := otel.Tracer("example") _, err = graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(&otelgraphql.Tracer{Tracer: tr})) ``` ```go // OpenTracing tracer package main import ( "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" "github.com/graph-gophers/graphql-go/trace/opentracing" "github.com/graph-gophers/graphql-go/trace/tracer" ) // ... _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(opentracing.Tracer{})) // ... ``` If you need to implement a custom tracer the library would accept any tracer which implements the interface below: ```go type Tracer interface { TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*errors.QueryError)) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*errors.QueryError)) TraceValidation(context.Context) func([]*errors.QueryError) } ``` ### [Examples](https://github.com/graph-gophers/graphql-go/wiki/Examples) graphql-go-1.6.0/SECURITY.md000066400000000000000000000013441475633407000153630ustar00rootroot00000000000000# Security Policy ## Supported Versions We always try to maintain the library secure and suggest our users to upgrade to the latest stable version. We realize that sometimes this is not possible. | Version | Supported | | ------- | ------------------ | | 1.x | :white_check_mark: | | < 1.0 | :x: | ## MaxDepth If you are using the `graphql.MaxDepth` schema option, make sure that you upgrade to version v1.3.0 or higher due to a bug causing security vulnerability in earlier versions. ## Reporting a Vulnerability If you find a security vulnerability with this library, please, DO NOT submit a pull request right away. Please, report the issue to @pavelnikolov in the Gophers Slack in a private message. graphql-go-1.6.0/ast/000077500000000000000000000000001475633407000143575ustar00rootroot00000000000000graphql-go-1.6.0/ast/argument.go000066400000000000000000000024431475633407000165330ustar00rootroot00000000000000package ast // Argument is a representation of the GraphQL Argument. // // https://spec.graphql.org/draft/#sec-Language.Arguments type Argument struct { Name Ident Value Value Directives DirectiveList } // ArgumentList is a collection of GraphQL Arguments. type ArgumentList []*Argument // Returns a Value in the ArgumentList by name. func (l ArgumentList) Get(name string) (Value, bool) { for _, arg := range l { if arg.Name.Name == name { return arg.Value, true } } return nil, false } // MustGet returns a Value in the ArgumentList by name. // MustGet will panic if the argument name is not found in the ArgumentList. func (l ArgumentList) MustGet(name string) Value { value, ok := l.Get(name) if !ok { panic("argument not found") } return value } type ArgumentsDefinition []*InputValueDefinition // Get returns an InputValueDefinition in the ArgumentsDefinition by name or nil if not found. func (a ArgumentsDefinition) Get(name string) *InputValueDefinition { for _, inputValue := range a { if inputValue.Name.Name == name { return inputValue } } return nil } // Names returns a slice of ArgumentsDefinition names. func (a ArgumentsDefinition) Names() []string { names := make([]string, len(a)) for i, f := range a { names[i] = f.Name.Name } return names } graphql-go-1.6.0/ast/directive.go000066400000000000000000000014561475633407000166720ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // Directive is a representation of the GraphQL Directive. // // http://spec.graphql.org/draft/#sec-Language.Directives type Directive struct { Name Ident Arguments ArgumentList } // DirectiveDefinition is a representation of the GraphQL DirectiveDefinition. // // http://spec.graphql.org/draft/#sec-Type-System.Directives type DirectiveDefinition struct { Name string Desc string Repeatable bool Locations []string Arguments ArgumentsDefinition Loc errors.Location } type DirectiveList []*Directive // Returns the Directive in the DirectiveList by name or nil if not found. func (l DirectiveList) Get(name string) *Directive { for _, d := range l { if d.Name.Name == name { return d } } return nil } graphql-go-1.6.0/ast/doc.go000066400000000000000000000003601475633407000154520ustar00rootroot00000000000000/* Package ast represents all types from the [GraphQL specification] in code. The names of the Go types, whenever possible, match 1:1 with the names from the specification. [GraphQL specification]: https://spec.graphql.org */ package ast graphql-go-1.6.0/ast/enum.go000066400000000000000000000020461475633407000156540ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // EnumTypeDefinition defines a set of possible enum values. // // Like scalar types, an EnumTypeDefinition also represents a leaf value in a GraphQL type system. // // http://spec.graphql.org/draft/#sec-Enums type EnumTypeDefinition struct { Name string EnumValuesDefinition []*EnumValueDefinition Desc string Directives DirectiveList Loc errors.Location } // EnumValueDefinition are unique values that may be serialized as a string: the name of the // represented value. // // http://spec.graphql.org/draft/#EnumValueDefinition type EnumValueDefinition struct { EnumValue string Directives DirectiveList Desc string Loc errors.Location } func (*EnumTypeDefinition) Kind() string { return "ENUM" } func (t *EnumTypeDefinition) String() string { return t.Name } func (t *EnumTypeDefinition) TypeName() string { return t.Name } func (t *EnumTypeDefinition) Description() string { return t.Desc } graphql-go-1.6.0/ast/extension.go000066400000000000000000000005261475633407000167250ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // Extension type defines a GraphQL type extension. // Schemas, Objects, Inputs and Scalars can be extended. // // https://spec.graphql.org/draft/#sec-Type-System-Extensions type Extension struct { Type NamedType Directives DirectiveList Loc errors.Location } graphql-go-1.6.0/ast/field.go000066400000000000000000000016561475633407000160010ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // FieldDefinition is a representation of a GraphQL FieldDefinition. // // http://spec.graphql.org/draft/#FieldDefinition type FieldDefinition struct { Name string Arguments ArgumentsDefinition Type Type Directives DirectiveList Desc string Loc errors.Location } // FieldsDefinition is a list of an ObjectTypeDefinition's Fields. // // https://spec.graphql.org/draft/#FieldsDefinition type FieldsDefinition []*FieldDefinition // Get returns a FieldDefinition in a FieldsDefinition by name or nil if not found. func (l FieldsDefinition) Get(name string) *FieldDefinition { for _, f := range l { if f.Name == name { return f } } return nil } // Names returns a slice of FieldDefinition names. func (l FieldsDefinition) Names() []string { names := make([]string, len(l)) for i, f := range l { names[i] = f.Name } return names } graphql-go-1.6.0/ast/fragment.go000066400000000000000000000022041475633407000165070ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" type Fragment struct { On TypeName Selections SelectionSet } // InlineFragment is a representation of the GraphQL InlineFragment. // // http://spec.graphql.org/draft/#InlineFragment type InlineFragment struct { Fragment Directives DirectiveList Loc errors.Location } // FragmentDefinition is a representation of the GraphQL FragmentDefinition. // // http://spec.graphql.org/draft/#FragmentDefinition type FragmentDefinition struct { Fragment Name Ident Directives DirectiveList Loc errors.Location } // FragmentSpread is a representation of the GraphQL FragmentSpread. // // http://spec.graphql.org/draft/#FragmentSpread type FragmentSpread struct { Name Ident Directives DirectiveList Loc errors.Location } type FragmentList []*FragmentDefinition // Returns a FragmentDefinition by name or nil if not found. func (l FragmentList) Get(name string) *FragmentDefinition { for _, f := range l { if f.Name.Name == name { return f } } return nil } func (InlineFragment) isSelection() {} func (FragmentSpread) isSelection() {} graphql-go-1.6.0/ast/input.go000066400000000000000000000024541475633407000160520ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // InputValueDefinition is a representation of the GraphQL InputValueDefinition. // // http://spec.graphql.org/draft/#InputValueDefinition type InputValueDefinition struct { Name Ident Type Type Default Value Desc string Directives DirectiveList Loc errors.Location TypeLoc errors.Location } type InputValueDefinitionList []*InputValueDefinition // Returns an InputValueDefinition by name or nil if not found. func (l InputValueDefinitionList) Get(name string) *InputValueDefinition { for _, v := range l { if v.Name.Name == name { return v } } return nil } // InputObject types define a set of input fields; the input fields are either scalars, enums, or // other input objects. // // This allows arguments to accept arbitrarily complex structs. // // http://spec.graphql.org/draft/#sec-Input-Objects type InputObject struct { Name string Desc string Values ArgumentsDefinition Directives DirectiveList Loc errors.Location } func (*InputObject) Kind() string { return "INPUT_OBJECT" } func (t *InputObject) String() string { return t.Name } func (t *InputObject) TypeName() string { return t.Name } func (t *InputObject) Description() string { return t.Desc } graphql-go-1.6.0/ast/interface.go000066400000000000000000000016761475633407000166600ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // InterfaceTypeDefinition recusrively defines list of named fields with their arguments via the // implementation chain of interfaces. // // GraphQL objects can then implement these interfaces which requires that the object type will // define all fields defined by those interfaces. // // http://spec.graphql.org/draft/#sec-Interfaces type InterfaceTypeDefinition struct { Name string PossibleTypes []*ObjectTypeDefinition Fields FieldsDefinition Desc string Directives DirectiveList Loc errors.Location Interfaces []*InterfaceTypeDefinition } func (*InterfaceTypeDefinition) Kind() string { return "INTERFACE" } func (t *InterfaceTypeDefinition) String() string { return t.Name } func (t *InterfaceTypeDefinition) TypeName() string { return t.Name } func (t *InterfaceTypeDefinition) Description() string { return t.Desc } graphql-go-1.6.0/ast/object.go000066400000000000000000000013651475633407000161610ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // ObjectTypeDefinition represents a GraphQL ObjectTypeDefinition. // // type FooObject { // foo: String // } // // https://spec.graphql.org/draft/#sec-Objects type ObjectTypeDefinition struct { Name string Interfaces []*InterfaceTypeDefinition Fields FieldsDefinition Desc string Directives DirectiveList InterfaceNames []string Loc errors.Location } func (*ObjectTypeDefinition) Kind() string { return "OBJECT" } func (t *ObjectTypeDefinition) String() string { return t.Name } func (t *ObjectTypeDefinition) TypeName() string { return t.Name } func (t *ObjectTypeDefinition) Description() string { return t.Desc } graphql-go-1.6.0/ast/query.go000066400000000000000000000027071475633407000160610ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // ExecutableDefinition represents a set of operations or fragments that can be executed // against a schema. // // http://spec.graphql.org/draft/#ExecutableDefinition type ExecutableDefinition struct { Operations OperationList Fragments FragmentList } // OperationDefinition represents a GraphQL Operation. // // https://spec.graphql.org/draft/#sec-Language.Operations type OperationDefinition struct { Type OperationType Name Ident Vars ArgumentsDefinition Selections SelectionSet Directives DirectiveList Loc errors.Location } type OperationType string // A Selection is a field requested in a GraphQL operation. // // http://spec.graphql.org/draft/#Selection type Selection interface { isSelection() } // A SelectionSet represents a collection of Selections // // http://spec.graphql.org/draft/#sec-Selection-Sets type SelectionSet []Selection // Field represents a field used in a query. type Field struct { Alias Ident Name Ident Arguments ArgumentList Directives DirectiveList SelectionSet SelectionSet SelectionSetLoc errors.Location } func (Field) isSelection() {} type OperationList []*OperationDefinition // Get returns an OperationDefinition by name or nil if not found. func (l OperationList) Get(name string) *OperationDefinition { for _, f := range l { if f.Name.Name == name { return f } } return nil } graphql-go-1.6.0/ast/scalar.go000066400000000000000000000013701475633407000161540ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // ScalarTypeDefinition types represent primitive leaf values (e.g. a string or an integer) in a GraphQL type // system. // // GraphQL responses take the form of a hierarchical tree; the leaves on these trees are GraphQL // scalars. // // http://spec.graphql.org/draft/#sec-Scalars type ScalarTypeDefinition struct { Name string Desc string Directives DirectiveList Loc errors.Location } func (*ScalarTypeDefinition) Kind() string { return "SCALAR" } func (t *ScalarTypeDefinition) String() string { return t.Name } func (t *ScalarTypeDefinition) TypeName() string { return t.Name } func (t *ScalarTypeDefinition) Description() string { return t.Desc } graphql-go-1.6.0/ast/schema.go000066400000000000000000000054701475633407000161540ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // Schema represents a GraphQL service's collective type system capabilities. // A schema is defined in terms of the types and directives it supports as well as the root // operation types for each kind of operation: `query`, `mutation`, and `subscription`. // // For a more formal definition, read the relevant section in the specification: // // http://spec.graphql.org/draft/#sec-Schema type Schema struct { // SchemaDefinition corresponds to the `schema` sdl keyword. SchemaDefinition // Types are the fundamental unit of any GraphQL schema. // There are six kinds of named type definitions in GraphQL, and two wrapping types. // // http://spec.graphql.org/draft/#sec-Types Types map[string]NamedType // Directives are used to annotate various parts of a GraphQL document as an indicator that they // should be evaluated differently by a validator, executor, or client tool such as a code // generator. // // http://spec.graphql.org/#sec-Type-System.Directives Directives map[string]*DirectiveDefinition Objects []*ObjectTypeDefinition Unions []*Union Enums []*EnumTypeDefinition Extensions []*Extension SchemaString string } func (s *Schema) Resolve(name string) Type { return s.Types[name] } // SchemaDefinition is an optional schema block. // If the schema definition is present it might contain a description and directives. It also contains a map of root operations. For example: // // schema { // query: Query // mutation: Mutation // subscription: Subscription // } // // type Query { // # query fields go here // } // // type Mutation { // # mutation fields go here // } // // type Subscription { // # subscription fields go here // } // // If the root operations have default names (i.e. Query, Mutation and Subscription), then the schema definition can be omitted. For example, this is equivalent to the above schema: // // type Query { // # query fields go here // } // // type Mutation { // # mutation fields go here // } // // type Subscription { // # subscription fields go here // } // // https://spec.graphql.org/October2021/#sec-Schema type SchemaDefinition struct { // Present is true if the schema definition is not omitted, false otherwise. For example, in the following schema // // type Query { // hello: String! // } // // the schema keyword is omitted since the default name for Query is used. In that case Present would be false. Present bool // RootOperationTypes determines the place in the type system where `query`, `mutation`, and // `subscription` operations begin. // // http://spec.graphql.org/draft/#sec-Root-Operation-Types RootOperationTypes map[string]NamedType EntryPointNames map[string]string Desc string Directives DirectiveList Loc errors.Location } graphql-go-1.6.0/ast/types.go000066400000000000000000000032051475633407000160520ustar00rootroot00000000000000package ast import ( "github.com/graph-gophers/graphql-go/errors" ) // TypeName is a base building block for GraphQL type references. type TypeName struct { Ident } // NamedType represents a type with a name. // // http://spec.graphql.org/draft/#NamedType type NamedType interface { Type TypeName() string Description() string } type Ident struct { Name string Loc errors.Location } type Type interface { // Kind returns one possible GraphQL type kind. A type kind must be // valid as defined by the GraphQL spec. // // https://spec.graphql.org/draft/#sec-Type-Kinds Kind() string // String serializes a Type into a GraphQL specification format type. // // http://spec.graphql.org/draft/#sec-Serialization-Format String() string } // List represents a GraphQL ListType. // // http://spec.graphql.org/draft/#ListType type List struct { // OfType represents the inner-type of a List type. // For example, the List type `[Foo]` has an OfType of Foo. OfType Type } // NonNull represents a GraphQL NonNullType. // // https://spec.graphql.org/draft/#NonNullType type NonNull struct { // OfType represents the inner-type of a NonNull type. // For example, the NonNull type `Foo!` has an OfType of Foo. OfType Type } func (*List) Kind() string { return "LIST" } func (*NonNull) Kind() string { return "NON_NULL" } func (*TypeName) Kind() string { panic("TypeName needs to be resolved to actual type") } func (t *List) String() string { return "[" + t.OfType.String() + "]" } func (t *NonNull) String() string { return t.OfType.String() + "!" } func (*TypeName) String() string { panic("TypeName needs to be resolved to actual type") } graphql-go-1.6.0/ast/union.go000066400000000000000000000015051475633407000160370ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // Union types represent objects that could be one of a list of GraphQL object types, but provides no // guaranteed fields between those types. // // They also differ from interfaces in that object types declare what interfaces they implement, but // are not aware of what unions contain them. // // http://spec.graphql.org/draft/#sec-Unions type Union struct { Name string UnionMemberTypes []*ObjectTypeDefinition Desc string Directives DirectiveList TypeNames []string Loc errors.Location } func (*Union) Kind() string { return "UNION" } func (t *Union) String() string { return t.Name } func (t *Union) TypeName() string { return t.Name } func (t *Union) Description() string { return t.Desc } graphql-go-1.6.0/ast/value.go000066400000000000000000000067331475633407000160330ustar00rootroot00000000000000package ast import ( "strconv" "strings" "text/scanner" "github.com/graph-gophers/graphql-go/errors" ) // Value represents a literal input or literal default value in the GraphQL Specification. // // http://spec.graphql.org/draft/#sec-Input-Values type Value interface { // Deserialize transforms a GraphQL specification format literal into a Go type. Deserialize(vars map[string]interface{}) interface{} // String serializes a Value into a GraphQL specification format literal. String() string Location() errors.Location } // PrimitiveValue represents one of the following GraphQL scalars: Int, Float, // String, or Boolean type PrimitiveValue struct { Type rune Text string Loc errors.Location } func (val *PrimitiveValue) Deserialize(vars map[string]interface{}) interface{} { switch val.Type { case scanner.Int: value, err := strconv.ParseInt(val.Text, 10, 32) if err != nil { panic(err) } return int32(value) case scanner.Float: value, err := strconv.ParseFloat(val.Text, 64) if err != nil { panic(err) } return value case scanner.String: value, err := strconv.Unquote(val.Text) if err != nil { panic(err) } return value case scanner.Ident: switch val.Text { case "true": return true case "false": return false default: return val.Text } default: panic("invalid literal value") } } func (val *PrimitiveValue) String() string { return val.Text } func (val *PrimitiveValue) Location() errors.Location { return val.Loc } // ListValue represents a literal list Value in the GraphQL specification. // // http://spec.graphql.org/draft/#sec-List-Value type ListValue struct { Values []Value Loc errors.Location } func (val *ListValue) Deserialize(vars map[string]interface{}) interface{} { entries := make([]interface{}, len(val.Values)) for i, entry := range val.Values { entries[i] = entry.Deserialize(vars) } return entries } func (val *ListValue) String() string { entries := make([]string, len(val.Values)) for i, entry := range val.Values { entries[i] = entry.String() } return "[" + strings.Join(entries, ", ") + "]" } func (val *ListValue) Location() errors.Location { return val.Loc } // ObjectValue represents a literal object Value in the GraphQL specification. // // http://spec.graphql.org/draft/#sec-Object-Value type ObjectValue struct { Fields []*ObjectField Loc errors.Location } // ObjectField represents field/value pairs in a literal ObjectValue. type ObjectField struct { Name Ident Value Value } func (val *ObjectValue) Deserialize(vars map[string]interface{}) interface{} { fields := make(map[string]interface{}, len(val.Fields)) for _, f := range val.Fields { fields[f.Name.Name] = f.Value.Deserialize(vars) } return fields } func (val *ObjectValue) String() string { entries := make([]string, 0, len(val.Fields)) for _, f := range val.Fields { entries = append(entries, f.Name.Name+": "+f.Value.String()) } return "{" + strings.Join(entries, ", ") + "}" } func (val *ObjectValue) Location() errors.Location { return val.Loc } // NullValue represents a literal `null` Value in the GraphQL specification. // // http://spec.graphql.org/draft/#sec-Null-Value type NullValue struct { Loc errors.Location } func (val *NullValue) Deserialize(vars map[string]interface{}) interface{} { return nil } func (val *NullValue) String() string { return "null" } func (val *NullValue) Location() errors.Location { return val.Loc } graphql-go-1.6.0/ast/variable.go000066400000000000000000000010151475633407000164700ustar00rootroot00000000000000package ast import "github.com/graph-gophers/graphql-go/errors" // Variable is used in GraphQL operations to parameterize an input value. // // http://spec.graphql.org/draft/#Variable type Variable struct { Name string Loc errors.Location } func (v Variable) Deserialize(vars map[string]interface{}) interface{} { return vars[v.Name] } func (v Variable) String() string { return "$" + v.Name } func (v *Variable) Location() errors.Location { return v.Loc } graphql-go-1.6.0/decode/000077500000000000000000000000001475633407000150135ustar00rootroot00000000000000graphql-go-1.6.0/decode/decode.go000066400000000000000000000007571475633407000165760ustar00rootroot00000000000000package decode // Unmarshaler defines the api of Go types mapped to custom GraphQL scalar types type Unmarshaler interface { // ImplementsGraphQLType maps the implementing custom Go type // to the GraphQL scalar type in the schema. ImplementsGraphQLType(name string) bool // UnmarshalGraphQL is the custom unmarshaler for the implementing type // // This function will be called whenever you use the // custom GraphQL scalar type as an input UnmarshalGraphQL(input interface{}) error } graphql-go-1.6.0/docs/000077500000000000000000000000001475633407000145205ustar00rootroot00000000000000graphql-go-1.6.0/docs/img/000077500000000000000000000000001475633407000152745ustar00rootroot00000000000000graphql-go-1.6.0/docs/img/logo.png000066400000000000000000001721771475633407000167610ustar00rootroot00000000000000‰PNG  IHDR,åéíĘŊsRGBŽÎé pHYs  šœ=iTXtXML:com.adobe.xmp 2018:04:05 21:04:08 2018-03-28T15:56:49-04:00 2018-04-04T21:59:28-04:00 Pixelmator 3.7 adobe:docid:photoshop:5563f4c0-3874-11e8-a9f6-8d6dce19aad7 xmp.did:7c134ef2-c9d9-a04a-8074-010bf2d714fe xmp.iid:3a473a35-1b61-7548-b8c1-d37321342166 xmp.iid:2cea00a6-7265-0d42-8fa8-49801ffc3443 Adobe Photoshop CC 2017 (Windows) 2018-03-28T15:56:49-04:00 xmp.iid:7c134ef2-c9d9-a04a-8074-010bf2d714fe created Adobe Photoshop CC 2017 (Windows) / 2018-03-28T16:01:58-04:00 xmp.iid:72410e7c-2c06-c54b-aa85-be4234c69957 saved Adobe Photoshop CC 2017 (Windows) / 2018-04-04T21:59:28-04:00 xmp.iid:3a473a35-1b61-7548-b8c1-d37321342166 saved converted from application/vnd.adobe.photoshop to image/png derived converted from application/vnd.adobe.photoshop to image/png Adobe Photoshop CC 2017 (Windows) / 2018-04-04T21:59:28-04:00 xmp.iid:2cea00a6-7265-0d42-8fa8-49801ffc3443 saved adobe:docid:photoshop:ea9698db-3874-11e8-a9f6-8d6dce19aad7 xmp.did:7c134ef2-c9d9-a04a-8074-010bf2d714fe 300 229 1 image/png 72 5 2 1 72 sRGB IEC61966-2.1 adobe:docid:photoshop:a6ba20b8-337c-11e8-9a61-a538f610f253 xmp.did:ac00a4cc-1034-4276-87d5-15fd99ebc41b 3 ë(ãQ@IDATxė]€Å­Ë‘Käœ3ˆ 9IŒßĸˆb΂Š‚ōF>bÎYTD%$į|䜹œķũ÷zo–ŲŲ™ŊŨģŊ#ü+=vBOOOOOuUõĢ*?Š 2éÂÂBŸíÛˇ‡¤¤¤4đķķë†ũQQQ#qŗ?ņÛ)++Ģvhhhķ°°°ČęÕĢû~øá‡Š?ūøca™4ĻĸԊ¸@zĀįyŽsâ1ȤęÕĢ×(55õŌėėėËōōō.ÉĪΝÆ…âœđOO>>ļî÷õõMSÛéīīŋ<((č¯Ę•+¯ÜŊ{wÎ;^ ŋ¸bģĸ*z ĸJŌ—_~y˜ĖH0›š`<Šd4`B…`@……j[;Î_ūņŧV׊kxļSŗfÍA`rž%iSÅ5=PŅ=āĐ&LŦRĨʝ`TÛÉhôLJc@dL¸Č埯Č4æÅ_­žųEŒĢBvčũŠŠ¨čˇ{Ēß%`&‹5ÆBIŠŒÆ…›ËĀX—ƸĀü *UĒôQ—.]ĒģŨŠ‚=PŅ=@-&&æ!0“d2+2*JSč¯˙ij#īÁm¨–ģĒUĢF5ąBÚĒŠ=PŅŽ{ gĪžŅXÕûVc$´My"Qą<¤˛Bا”ô„ģšÅäČym‘š™ũd…mËõģĒ8{aö@ÅLíæ{íÕĢWÕ 6ĖHOO`PũY]F&-Z´îŨģË%—\"͚5“Zĩj ¤%)((Ŧ"ĘĄC‡dĪž=˛lŲ2Yąb…9rÄĒ:uœ÷ŖR÷ˆˆxû—_~yŧoßžy./Ē8YŅ=đ˙Õ”ŦÂÃÃ˙Ą4E ɕ #|á=÷ÜS¸téŌÂĖĖLđ÷(>>žpæĖ™…×^{­R3ŅÃĻŌÕC0.%ŲĪõj¯XEü˙ŽO[ŅÖ=p÷ŨwāųŖÆŦ¨žĄ´Ķ؃>X¸oß>÷8”‹Rļ ¯žúj§{h÷՘ÛĻõĒǐ”­_ař ¨*ē‹—IF9@ĐņTĮrss@P§+¨ōŊķÎ;Jũ3ž,HʖÜI’ĩę„$d‹O /Ô:pŧ”),(˙Ąââ/­c$¨suņ õˇWņÅ_ČSO=%'Nœ°Ķ6¨ZRí„jšWĩjÕkOž<ųģvŽâˇĸ.Ô¨`X.Ū,\fúŸ>}úOŦŌîDģ•‘î¸ãyã76%ûМ­ ’Ŋō„älOnÄeIa]˛]Ôz^q/ņ÷•€•$¸gM‰¸¯­bb,ˇcĮšå–[d͚5ę2ũ?0Ä+ IëPũúõ{ĀæÚĻŋ¸bģĸÎÃĐ>›ķ°éeÛdÚ­Ā$V@Ēj†%999N7„ (oŊõ–’tÔÉüBIų`›¤~+…`n~>⓯›ŨœW …Ųų2¨žDOė,žĒZ0MšiÄM2˙¯ųNm äĮļqõ’āĒŽN…*TôĀŌn~IČĶzđ€ŧ”””4ŽĖŠĒ ‘F%pXH7ęUŋĉk$sūņ Â1Ŋ4eŧ¸˜ũÂŦ| ŧ¸ŠT~­ģøAe$Å''ĘĐndUėz‡ĢŠb€+…Āh …ú8ÛĄ@ÅNE\@=āw=‹×ĨuëÖõŽ;ö˜QUAØ˛ęîͧ|ũõ×B•Œ”ģ'Y]&ŲĢNŠO0ē´ˊõų@=Ė?š.YK+U2˙Xēøũ|L:î¯)ŗâ–IzA‹Ų‰L“ö6¨ŦÍÆŒķÅęÕĢ möŌ=pūö@Ã2ywĀH=đ/)šė€-ȝŋū*°oŠ+sw$JÜũK$oŠY™ÔW’CdZ…I9Šie.8"YąņR#0FĒFÉėÄUj™RĢ—LĩHm­Ŋ˙ūuŠíĐÎUüVôĀ…Ô6}æBzĸR> Vü"ŸAŠÅČŦXõøņãĨiĶĻę.yGŌ$ūņå’2j`đ~ÚĀ ąąnūedËu1ŊåōčÎNOI)ęarrō(J[N.°|F8ŸûŒ[ M+é‰÷"üĩDäŒÚ͛7¯„2ãû{īüĀöô}×t5>ú_ÍlWødŨēu4pĢjĮ¯’ôŸ÷9@<ŊŸ§å}üeKÆ~š2öiÅĀ´ë5[>ŌHm ŌÖÎyë—Lâúë¯X¸paH 1ø‹‚‰4DģF8‚)âĄvĀüĶáŧ}ōŌK/=öÃ?dá˜ŖŽ­]\Ėī•W^Š…ÖŨ`Wė…ŋf.k ]\ĸ ÆUáԟŽß@?öã=nĀÂÄRÜũâ\ÎUĐyÚ Ëđâāį÷>ĀģĖ$Ŧwß}Wîŋ˙~uEÖâc˙är|"øöĘšƒ}eôŪ7dFü‡Ö“ÉR**˙6Ŧ~îp˛„;”Rj׎Ũ jf0ĸ^¨ŋleĩq<UúãˇØšÁ$(ũe }GÁÄļ Ô¨QãDd=„ã.+ pwƌŊ õ^5÷o‚ú˛NŲūōŲųĢãy­}8–Š{o‡$ö îũā"pĖåŊy}[=PΟڹõđÆÖ 6,î1ë0kˇĄ]ˆaN6oŪŦlWųņYrúĻŋ$īT†2keĘë7Č7@%o”ëwž[֙oŽR ?PH  &MĀß.`ŗâĐ.ÍIÛí&ĸ_ÆáBˆįûÁĀûĸ/B´ßíJ, ’Ą ­‰đ øĻnŨēomŨēu¯‘y@Ũ œ:uęŋÁ(ÄũģđۜDøGâ>ßÍÚÅ{čËķíâx˜æĪx§oÃ3a‹ņŪ,[AįfT0,Ũ{ŠS§NĶãĮo€!HT˙!Œ1Bžúę+U:åũm’ōîf;¸SWEšlB†Ŧ‚¸ũ Ų›uĖ~O~ dZl;?XüÆąÃ8ļÛÁ øÛ°aÓ˗/7UÍđĖ>Ąû5ŒĸŋžiÛoäÅ 0W2Ž õŧŗsįÎcēûŋŒû÷ä­Đf;ƒ*i{ô Lc\¨—Ēę{°[Nš?~˛ĢĸĒ2ę3~ etƒķŠZĖæ1˜Cøá뙟Ą˙ūęQ s $sĖC„ž%ĸTí.=*ĩq`Xü5ĻÅ_ėWåTĨl*Ô*ū¤ÁҚ!˜ˇ‚Ql†Š´LcÔžŖPŲ‚ëëq¨“€éąpYÔŧhØ ŸâB\Œžū­.î?íϊË÷`œxôĒŅ ¯`ûŒ- Ŧn^Qo‰z BÂŌuf{õąrë ęŠ@MR‡čX˜•‹ŅŗHų…ųŌ4„ yŦIcL,Ą=>FŸøĢcbØ¯ĢgŌÖĩ–Ũļ—“ÛĄĩ×ęnXX&MšHŖFÔģá;âĩiii‚RklÛļmrøđaËē(‘ō×!éŊ„UâzX} "ΘUΟŨã K×˙ø`Ēčví›Āõ¤/ĩŸˇ+É~ülnä‚aĩ m(]+ĩ’•ŠÛŨnŠž‰é/"s—ČČ,ČÄšMæUN1@­2œÄÄDÄ"ąąą‚A}P;mųKŋHļҌ Â ‰üûß˙–ŪŊ{ âę+ĀŦYYƒj)ģví’?˙üSž˙ū{!3#ŪjąúÃ"Ã=ˆIæĻ5Ļ‚i™õÖŲ=æū(=ģí,—ģãŖ]†Ĩģņfˆ6*K–Ø  €2dĖ9T6@Q㍋Ų÷ƒ‹cö=õä̤˛üŸe‚\†J˛€Z[Ė•žŸ†qZn¸áų×ŋū%;v؛<Ē$..N,X |đüũ÷ß]KFÅEx@ÚļmëŅĩZa`ˇä÷ß—)SĻ\—´Ãŋ´™ņũ‡°Bī€á>‚1QĄ:ôŌŲŨŠ`XEũ•ĸ„@ĻgqŖÂY]ûČžÚ}n0,Fv¨öfo ė_K=?ʃĘŪŊ{•Ím˖-JÂā1¨;%i”<€…’ûîģOZļlé˛Zũr!`TY‰îė[J<¯Ŋöš ]œÚ§OyõÕWĨS§Næ÷Ž #Wč0Nw&áVDf4}útyņÅ‹NÅȰhCŖÔõđ0­ŠN…*œĩ°~ŗg­IgįÆĀ`ų ČЎƒ‘a1.;B+ÛOʡHĘ{[ΤAß;dXĄCęKĖänúÃÛ°ËŠ€@y ˛‰Q=Ŗ4FЇĪjE”¤^yåéׯŸS‘\ SßHyđ›}ŽAfá7ŖĒ:Q#Š~›ƒ{Õ.Úԍ¸'žxB^ũu§ēĩcĮŽ• &Cčč‰Îā™ JÎĻ8É;”&É؊a1Ϙ_å` ęTMšGI@Ëh hŧ‰ēKõđŽģîRąôõus›LšûV:°hũ°zh.’ŠR˙”gč†OyŪöÜģ>\ Ô?1Hŗu4ÆjDßA‚F:"Į? æxvîZÛQKÃon.aÃÛãgiž‡ö2&§NRŌØĮ,īŋ˙žS‰ëŽģNņ”ę˜F” 6‚A­Åß60ŠCø#ã/˛‡üâ€Â!6Kũr›L* ‘øë^pœhBS’ĨGĪžĐ(J8û™qÆFíp"˙TϤ}ŊK2~Û/ų§ÁgŒū–dHøŸÄ(ŽjÛž•`—j)!`ęĄW4ßp#RņmlˇŪzĢ˚5K;d˙Õ"ĖbLlėÖ­[¯E‹ĨŲOVlœĩ(zÍgíūĨžq=*Á°ÚęPā¨Â!ŌĀū°­Aƒ‹ā÷į‘„Aú!$’;ųčWаܭ ļ–J.Œî§FÎ?+.9ĻEƀhĻžQAâËđĘØg˜eŋ:aĐRĸ˜úÕ ŋj!ļ2‘ļ8Z]=ü°ŧķöÛÚŽú%ǟ>“|^–`c8ÖNDļax0Ē}ž°l2.NDƒT…SOâëSdÖĶkÕÛŠŽ‘‰’‘hT˜[ |6Ķ>‹•ŧÃi6ûĄ;Aq¯BE¤û”?$­°ëKØÕ ÅW×\U„t-sæĖŅn§~9¨ReÅxzx­ Tėœ•8o#‚bv~LęF0—&z‰ˆËõl{1S‹ī3¸§ėu§wáÔüßë¨TĨô4oŪ<šė˛Ë ~äČŠëįJ~\Ņ ¯/t6ˇŠÚáEøF )!*?GH šú„ âCx ø× —ĀŅÔžŠøĩŒ’×ôs ŋLɆv™Ėū˜+ĶžüZü!mĩŧ˙1 oÖŒ@öøˆc9` z_"Y{w9]?iŌ$yæ™gėĮ#iŌZÉZvÂŊՅ}Ę~‘ŲŖšâĪŋ~%‰¸ģ„^ÕĀ^Šļ,ƒ7mÚd?Æ ö敌 đwĮ‘C%e¸C­ßA8ü"뀱Öķ%HØ~GĀ5ō0œ(7 ’rVēOÁ†—‹ī"ŗM›6™X U…ņ|ÚČą_{Žnœ— ËŲ=ĄŌLGįˇveƒa§C¤Į˛ûkP{^G¤3oĶä`åĢībŧd#ÃzöŲg•Ą–LáôčEļ`}H(qŪ‡$˜Z!Eœüņķ÷“äđ\éŗîA9™•āđß~û­ >\[ļy‹ôėÚĒ ĩŨžŖôšģDüCX(m1V~ Áį y}Q!Û˝ųģŦ~ĨÃqî\qÅ_N;<"˙D†ÄŨģXIĩLÔá R¸pĨ[šKäÃí”JÉz î[qq‚ö,JY˜ĖŪCxÄm(M°éųc2Ą6Ņíęŋ6`RtF4û()â¸æKš‚{įãX:~™š< GņˇĪšßÉ&0Ā-?ūøŖs<đŌ4ڋמG_ŋˇB¸PŒ0pB›ËŦØO`<•!ÎOžķÎ;ŋ…čĪ–Ņ^…mv]ÉŲŗgÛĐīø(C×ŗitįĪųM´›6fíáĮO\f ĸšæ:B đǘĪüÕÎŦøŒÉÛˇJæQāŠ4}P1Š9øÕw˛kĘ[’¸a“Ā7ۚ0âNũ=Īéĸ/TŽÛˆ‹E÷†šņ÷ņƒá›É3ōĄ3°žØWz)Ã)[’ŌÎ胸Ŋl›ø´ŦŊķFŲōô#˛ėĒū’˛=֒i!Ą¤lÛŦŋĨÚÆИw¸ ÷ĀÉŲW6x7>6<2~? ‰/Ŧąe4Â}:Ē’jö>l5yaLTC4×!ÚņōøÅģđ*Ú6ÔIXŨ]‡‰w:˜Kg´Éëß-ß;ęõkæuŋ‹ĀŧVC˛|™šĮšÂŧŧūāeõ"¯šæš(tâģ`V– ĮÕŊ‹˜Ö ožųæ feŠËCīŸÃ˛ĨõD&6mÚ4uČ'Ė_ÂūÕØ:Ō*§m2ĢõéģåŊ3åׄe˛#퐤!Îģž‰q¨$ËņƒÁđBČqtÄãTË4âJšž|üQoҞ?íQY'åđ_Ų‹ä$ÆËÁ/?c?tf]š—ž)™Į9V´Ep.ûŧ -W1wgR™ģ?qq"㏒üęÕ f|äg­O”h°í80œž¤ô8ŠBšv§€TŦúū0’zlGy5™TĪą0“,ųíˇßV—FI¯ŧÚ`vëmVēœáų"zfWˆ¨Ībg:°IišĀIëQ`ktÁŦ. ē‡ ĘsFĻõķĪ?ËÚĩkÕeĄCëÀ “†ír$2˜™`R×ė/ĪúDîÜķš Üö„\ļíqžëyúāGōcÜbÅĐķR•äÅk(1Ž–¸ CĶmZ„@Ŋ5ĐüĄĐŪ<ŒŨál!#ōyŒŸ- čųŦęéĜß%7Į Ÿ6į–ÍKMÖWÛZØé”ˇ6!§cQ2§RŪ?āė/i3ö*¸ k'œƒ.?zâĮKĩ“V7`ĶŨ´LˆĖZÄ@`ßcrū ōZLĐdg—øü°ˇ…äũ$Ŋĩ`\ģtéRũl´ęœeXėÖ 3Ė|ÄeZõE|8=]Í0ô÷ã*ÂŖ¸ėGt~¤†w­f  ēV÷(_—Øõď—Kî$ßč Št[‹rU ŠúËI!؈‡ĨQ–ėfŸ”…ę÷ūÉY2fߛ2tûXéŋí1šzĮŗōāūŠōC3čßq\—_ĨJFú…É51=ĩjėŋˆKĨ\XØßz äæHNüiņe׀Wōiŋ–Š{vJâúu0Ė;všSˇY)ܙ*éŗ”/(L”|5yĘFa`FØ1åĒĢŽrh+ûAIĮ gjGŅĄdÉwúôéŽ{ŋ“Į`T]9ūÎ5b?@h¨ÆõL$ĢđÍŨŒc†iŠl[íĨ/ļ‘Ļs7e_ÃėRɝĒ/ŋüryX"ÎŌ°3Č{S§ĘT¤ŽĪÄęŽaļh {ÕDÜë~ DģˆÄ{cĐŧŒ—Ō\¯ķ´bî@ÎĐ %YƯR˙{ūđ§r$į´Ŋ V” Nä&¨ŋ5i;íÅĸGĢn`5i ÉĒUh} „ÔE ,SĮY˜ĐĩSāRûÅEĮ~˙EĒė |‚HPÕH‰ž¤‹d;rĻöžŪ•Ē—ö:sŦ˜­Ä;%y>Ō\,äú•ķ< C|ŪA0ËīvĢŦÛC† D;5ką/˜IœXfv˛¤ĮāHŪöŅĪ‹ę.Q5¯|Ô™+žŒÕ¯vĶŦ{8€ĸKr3|'”¸ęŖî/ņŊ\‚XûĪ|ë(f—¤b7Ž9į |7BgŸÎÎu‡¸ėüôĶOÛ3Ų4lØPÆÁÁõ@DeųqÂ<ą.ā­F5nÜx:Žoá92+Ėƒ!=Ęp#|áÆ6P- ŽFųĮ2”ÁVewÖ–Ņo¨_L=ū‹ü˙OŠî”\ūļdėCvV[Ud„fœŽŲa0ŖŠXšŠ4ôIHWR­ß@96k†CŲcŋ͐sæI­ĢBm‰åaøŲr ĀÆX>.[r9e{?—ĩÚļ>Āyo…Ģ‹ønēé&yî%ĩ!iÍŸß Á*į‘'Ĩڒ%XBŸkz9R0–‡ŖM) aRŽĮ*ĖĢōEa‰RĮāÁ6ķSŅ3%}YYIˆo°|uzžL:ōU™ÜŽL=ĄÂü<‰ôŦÄ/_"Í{B"ŖÄ3|.JĢ›#AUž—˜NÁ¤p<Œn¸ø…œq÷ŅÕ8JVgKŠ)]ŌŲ+Uīk# 4PĄq´öQĒ ä‚ßĻøcŒRˇ>ĒÕđ‘ã ŗĸ$EĶũ  äägŠĩ—ŋZH$ũ1NčFZ˜™'7ÅJÜü9’:ķ'9ē~- zî=û†ųđO‹pCˇÂŅŪļ‚aŧ‰—öų=œ„§:öf…‡KŌ ?ŧČV“^—úcî†]Ąũ$y[Ŧü3¨§ä$ž™)õuƒ9Å(×ôĐĄCMŅŅķ0™ļĘnp֗ŊũöÛåŖ>˛á„❖øüjI˙u™ŽdŅ@Nģ#‹Žƒ1=‡¨ĖķŒüBäjĪKĄ6vßā‰[ēDN/ž/ˆ† §ˇŪ/#ĢpRMõeĘ|ī•ļÉĶûËĩãn‘™?ũbŋ%™%lLfņę§aŋ*Ũq“‹cLi‡ŋSØ?‚1}e“°ú˜ÜŦYŗ$"ËQFÍ€ "jÅĪøØ‡Úoâb E*ØmˇŨf‡‹§ĸæDđ:ĸŗwų›TÄw¸ĪĶ)ŊėĄøōyŠžĄŸ` %Į 0Š\™—/—l]+ŗa öpŊn̐xÖxLü# 1šK ¸_iéœaX4:""ÂH6%_}ĀŪ}ÆlŠ1x f{Ėæ@kÅžôĒl›đ”i?qBīƒf¤ēfT/ĢbŧpāÂB.{“RĻmĩEl(èŖÄWĨä§Ë¤Ã_É'§fĢû^¨˙)Īn5Y. mŒđ4Βmš>7ÜwÂa—{øøtųlëxÛĀņB‰ÂbY gĨy‰Ā‹…”´šAŒģ‡‹ĢašՂvZúxꉕoEwŅ3čŒNĻÅ$3Rēær“žMáøŖcĪ'á [JĢ!÷c°1lX}„xÖe˙ü#īž7ąÄōl9Pƚ ¤cĨķ634æl]Úķ3ĒW=ŋĖ{Wā…ų‚+÷ƒdu7˜Å0ĢH}ũŽ;î ø)Ÿ|ō‰ b֊&<.í^ Ëįx˜5˛OĮÉßŨ/^Č\ŊæĀâEm2+ãũy_újƒ%Kā‰đmSę‹IīQ*"åōõ{F´%úÚ/ĖO^/“|#›ikēĀŠKĨ–ōKs[Ę2Ũ÷u֞:TåņĶ哸9eŌ†"fæ4֌7{đÁå…^P+—ús‡Á˜VI­gŠÅ_6:ŒÃ]1“aŠŋ\qQ­ŸmŖõĖi2ģj88í"`éXߧ+7Hėķãää‚â…'0åLØĶށGĘŧ35{gëŦÚ°0{Ôîé Ŧ\ЁŲō‰āŽŖĸEjŅFßy§Üūės2ķũ÷œŽ Œ>k@a ŠSEę\?BöŧûēSYíėfˊxd*ļ3ĢŦĨĮ% CB\ģÚöKf@ØAn< Ĩf` ÂĒĀ `üuŽÚ‰ ŠÔ>›ę—đįŌ”-ōŅÉ?åĪÄU¸Âųcú}Ÿ `ņĢQK|Ãl‹ĢęjL! …Y™Ē¨ ĀR:!ų‰qāŦÖũޝģŦļ ­Wû&ô˙šŖōž•Wz•e—'EcAJú\õ3fŒũ™Ôp2ĒŨøĨ$EfB|npqĘ^‹mÃ6î ‹vÉNcŽ}>%1FëÁĻÄth/Ũ~š%;^ųė|m’ē7Ckíc|ß}wlųJvôŦ1,@Z"‰å÷`Vm‹k:Rœ;„:ŲSE*Ŋ5M.jÖFb_|Vr“U‘mÚIŊˇ*uPĢ“fŸú7Ũ.û?œjׄra 8=‘YÁ ̌âßÄ-PŌU[Ä[ÕY:Wj!uĢ á”žÛ$"3ʆ—pR~ĒČ:!˙¤l–ŲIĢesú>uN_qÛAēJHŸAØĻƒøW¯‰öŲP\‘ÃCâf`—ÚŖˆũ‚ôTɇԙˇˇdoÛ šą[$÷Ɩ›ÆÖâÚTÜyöY÷J­å™:7K§JÍU_wMyį䒘¯[Ú,¯ŨgüøņvfEæņ5lLËa„JÃĀĄ7?ZO™”'@I ÆH"S¤‰Å˜Ö'HXŖ&˛ņáŅN€aU¸č|Suųö„ę4j ˆ­?_Ōm2Ûr'd:i qq.¤š†îܜ Ptģųæ›ef—×0–rĐōĀ Ø“bĄĸ­[%žØŠÚģ¯V‰áX+q—^9PN-üËá„ĻjKÖÚIŽ2xŨ7Ū¨åŸĖ”¸{IîždĶĀ}´7ŊtäkyãØZö_ÚgjÄHũ ę ŖZ%ŋPe§!îéDNŧÅßéÜ$™oØĸ­Tēõ^ ęØŽ0œrņā`¨îØY yųũIĻ*ĖL—œ=;$ķ¯Y’šhŽ˜ ŅUÁūĶ;â"‰ĀŗS’j\KúDļ“öaM•zŽ-&pZĄ'Áę´Ø>mÉ/#hUsŠŗš€ąž¯” Ǐ•+ąëĒ ŽUÃŋŠ0O)k+#ĸł߉˛\1Ļĸ’Åūā5ĘŠų eũũwIúūŊ.Ëcâ˙Їģņ­yxķj‹ëķĢJqĢÁČ`2’U/OĒ!sy`ŌK’üØXIŒãWôøö—„Ę8 ¨`¨˜ŧ˙ÃOŅÁŖ gĖwøÉ'ŸT'™Ō+îá,ŨEBáōōÉŠ9ōlžĒpæwwīhØ57Jĝ+õ¯0RZB˙*|˜XŪĄ}’ūĮO’šä/Dö< ĩŅ0xp/2ķ[Ē”ęŨŽVyZÖŪ}̜œ?ÛÕĨŸŖ]ŗū-wH›&Ãũ*ßâĢPxĮ—_“mĪÛĖ(fWX‹īŋ$PŦ\•œ ŸbÉ+rįJˆ†í0ëŦCY›ĨWwU0ÄĒÎdŊųæ2öņ'$Į öDwč$—ūŊÚVĖM†å™`ׄ ˛K˛ڊYŅ˜Á˜X+mE0ëŸc˙ØrLaø˜š,Ŗ#ŽôeägËMģ'ÉĒÔ˛5ĘúU¯%aWŨ aƒ¯_ŦŒRÚąuM´Ū„Ą] IŠG:…k$kųߒąaĩä ‹ģT3°˛ôŧX†Fw•NáÍĨ˛¤ZĨ4u>¨}VĪIŠų÷„rûžWæANbĖîCû’ˇ‰*$”žųæ—™ąĢ*?ûF‚*G`ĄiŦ9L’ˇlôvsėõÅtė*?úR*ĩhIÎ~Ø|Ÿˆ¯_Ŧēy8Â_;/‘7{TōuÔE¯ž#M°%ˇˆÂŖŸ~!ĢīåÄŦ˜‡’U׎]U]š;%nĖb)Hs0Ā4HÂ}ûŪFāŧĨnŨÛãB˜á7“ĐA×(¨‚_•ę6Fe`Ún×K[™WNKūö’ųĪ|I_ŗ.KÖvcũ•ũ#„+}—CŨëŅFĒD+æ”[gŠ13^ŽīkДëvLP6,}{GŽŠVõĮŧŊ ÍCĨ[ã‚>jĶękšFÚŧˆ‰rÄpIņĐŽhZa1CjՑī}*5(ÖüB5mĪ~YÔˇ“å¸Âę;mYí€Í:QĖ­-O—+ÃÂ Õ †ČÍP M><ņĻhā$­MųM¨æ'aØ;6ã{ÉŪ*ĻswŠ;l¸Ûę uėÄ‹eéõC%ÆQ= ÷õ×_Ģ4O<žIF#éÁūû\_–ÛœĮ#hŪ{'~3ž*Ņ>ž>°MøEňŊFØēūÚK@ƒ&âCņŸUIU“ĸúč xBūŽÍ’&•ąr‰d0ęĻP FˆtVęʘîŌŗRŠTJÕ&0)ŗåØôWå¤ų›wí]´]}ˇPîÛ{î9(ų/X°@˜ũģ<ˆ { ĨDgFQ’›R>6SŪŸĢ‹=_(ŅÛ;āÍÚFÕpÛÄIûŸņf§îÂXØõ^1-āÆÁreXđÉ ƒčf¨…ŦÚÖģwoåˇGėÕbčΝ‚Įa%DáKx´4wíÔõ3ļn—ĨW”´ãGnIQ\SA Ķs%îĄĨļl8&>‚ĐžüCÆüPIN•™hÚR›ˇķ RŒI،€B÷ƒzį[š*Ÿ"Ęé>!āŦøp& OpjOU?2)ŽđC擑.ģļJöŠE’ &•upģÍ-bTŌŽaÍ劘nŌ'ĸ4fŠĢ~´í”ÄŨČėĻdztAâīÉÜDY—ļKũ^ÖLëË—Åįcė°+cŸ–ÃŲŽöM‚–ɰh_-/‚3ž\}õÕĘ8_^÷tuŸ˜.˜°~ŸIŒQ‰vb&(YĐĩĩĖmŦ^%ąˆŽŌáŗĪ>sS?rŦĄ\  >ĐcŋÄīĮf8î1€ÚO?ũ¤ÂcL‡t< ‚†>Åšciķ=ÆĪ;~IJBŦķluß}÷ ŌFøPė3}Á$™Qčw q–VH˙ē a$ŋGBzöWŒĘŠ…”š Ą¨,ŔTȜYŦ=‹P‡ĩw߉¸ūk—:üÂ4QŋÜnXxpėl‡RÖ;åĘ°Ø 0ûaI÷/ø:ŲąôÍ$p“ú<Ãe<`2ži—č.Æ7e”LY}ũÕrԀng1„3f}IÉīl–Ô _Āa ǁëĶv+#{\1@B2ް̆KøMw‰$(=()#2´ÃaažÄ ī—\ĀŌ͓Ė]ۀņ„tæ&†p9™TT'i 0!ân‚@ÕL¯šYS1."ÛɨRašeĐ@2ŠyIk-WWŲߟ4yRš5e¸k¤tēŗ{ČŦ¸âûÜáOMÕüQŖFŲCbģWŖwK Á˜W Y}ļ)Ŧaéˇ|“-–™‹Á×'‰k7ȒŨ%ßĐ ˆÃã@ŋO)É3•;Ãĸ”=v:"{Ūí yæ+Ē|™îš‹i/Qâ´?íĖ<,ķ“Ö)/‚kyOˆ!œŠ~ŋXo”ÜRm ˛yK5e;hāO‡ÄG{äOņKœšÆPCČ~֙Æ öå—_š0Î*AS81û7Š1¨oąÍȂ‰Åāfą`XˆyW¯ØJ, ”;Ãb;žüōËôx`ÄįŸ~’Ö}Fįc­­‹/–/ŋúq°îTQwcšj0á ‡Ļ(ÛĻüGĢÂūKøÁ¨Ö*76QE •\H,&đ˜‰ļWäƒ(šŅO‚YõčįfÅ)Jƒ!$%HŪÖõ’žldŽ]!Ųq§Ü–6C üāĒčîŌ-ĸĩ !ÃËLŠ%Ņŋ“7Čo ËUŸšcësÕ¯lãcŪ“•p@WûFŠã[E˛sŗmÎįlœĢzĖÎŅā˙â‘M™íYSālØ­ĖÚĘc÷Üs2a vēU‘r9ž¸a  D”w ŗģîŠiâ׸Ĩî¨ã&ž÷ĘȊŅBŠCx9^ˆ=ë;;õūHWžĐgŋĶfU;ņQ+–-•/süä<žUX 2ĢĶŋū.Ëo&yŨ™ļ*†ŠkēMū Ā}aĒ|ÆaĒ3ã}„¸TX5Kįę_ôS˙‘ūCíņĻ\^`u’ošh…Ī7-0„- † _žŦĮÜfRá€!0dËU †ĐVj!Ģūķ. +|X҇ą|!˜ÔˆŨĩ Œ…’QYP-Ußģy’ôkĶCR˙Ü/ųq™6ž7ĶüØq˛)}¯C ŒŌÁĐ.Ī=÷œÃņsa‡YĮûôécˇŧÚTĨšô_ĩqú+›`;hšØš÷°œX¸@âeĩ˜tE[ãĪȕĀâYM%(ŲŦN¤O"nN_@LaÄĖčąFĮf-Z‰¯ÅB(lŒējŊŦs›ŗbŗ^{í5;ŗ*dfágVJî^`­Lā 4úN;ņkąĖŠõFÜņ„ ¸ĸ„’>˙"ã9ÔȂŨ6Ŧ”bRöēZ=æ­íÄ]›ÉåQ]¤d{Āj*C7U3ÆÛō)&^C`ƒIÛ°ĸļJ#†×)Āƚ¸Š8!öcōņŨ|M}‰ģw‰ÂĖŠvۜōĄ´åŌU“ĖŠˆÄĨ2vėX™0awˆH|ÆoŖ‘Ū,yŠCáRėä$'ĮwR‚Ģ›3,Îģ™™9’’œ"Dlndæ‹@%6KžU†ÅžÛącĮ`Ž&P:ÕĖž…@˙˛pņܲ• $fėoÂr•5wސLņ‡~XjVâuĶ?0{2 [Āf •GĄƒŽ–đŨėšdĨÁ(îÛeÃJ-_(™p4vuUßڋÚ"WøČ¨š†ÔVö#ڊ¸Ęį gc2)Ž ņ^„!0ęo‰Ëeqō&ˇō"ęÛëjÛ7ˆ’ _>Ûp+bm=a,Ǎ fŲfôåÎÖ6mēŒĶ†Ā—MhذĄZH"~ëÄ`Gr Ą9š‹CšŌî0rm€Į‘ž­LĢ"Ã:…w”›“‡ÆNPÉŪ¸—ŌļmÛ¯ą"ø4f‡ZfUoŲļUnÆ †leÔE­+`KΐõŖo“Ä];œ.>|¸’Ž´ɈžņĮSfEÉjuÚyōāûÅ~đAíģHÔC㝂ũËØB Á‡ᑃļhˆ5•Õ/ĪGæf!ud —cõŒŽÆ„!PŨŖA:ŗ°ô¸%ö̆°.}—Ė6‰!tözĶÉzC@ãÚoˆ˛ũŅš[éčKŸËŽ”ėŸz >Ÿēže˜l& %ųׅī<ŋŽR܉ēÞĮŠŒjD†…č˜2`Āíũ—“)ÃÄ0:-‰ę#ķüÕā1öÂe´A)ŠÉ(ūûß˙:܁+‰Ämá;R÷Ū{¯ėÚĩKaé9bdp{¸“Ÿ3ŠI×ķXVVŽœÃōÅxĪC@H{”[īáĒø9Á°€~OÆRįj0ŦkĖ{čđ•åŖ2ôãŪæÛžøP7>8FŽ-šītY¯^ŊT\+(RÚ÷{$õĶâäüČ*úÎ÷Â-ƒŌ„+âĮõøD…^gltKĸ2o‡!•Üõ+$s)ŒįX9ÉŨ]ĸęÂh ˇÜ>ŧŠDéĸ!xËxއ!+Eå<0Š­€!xٙļ>˙ē „Č˙€&D˙ˇV ‹nI*Ūŧ>„ ܕōŖ*;0+öÕE]dg yĮĐ 6^BB€h0ĢhéÕAžAžGČ”=AcXȑŠR]^Ãmäą´§€ã¸ĸ$FĻÅ\ô%:žeIŒ1mÚ4Ė“gžŸalæĒà ;qq‰ †G­ŌīžûŽ0FWY™ÛsđĀ0ô\`ĩBlumÔß—Ļ L@d˛ž’ķ×ëi ^(O¨^ŧŖœĢĢWĶËŖÁö@HažM?¨‚;Æ=#{ŋũBWŌļI EgôՁŦÅĮ$y ā ūčYÃėĀ•é´îŨW<|ÁˇR¤Ä<ûšø×Ŧkķķ3ŪYCĶHō°i¤Ņ‡Qr<đ˙b‹KC`íoŋRtä˧‰ĩjĐ :˜ŗ=AE …žG}įLAlŲnķåĄũSeEęv‡sN;˜"ī+ˆŖîđĄąm +Ütr‚ä¯Ų Œ†°vânš¤÷Šö¯$=ā`|°R=+ĩ•`Z¤ÎŊˤüŽé`ö)Y’˛If%ސÕ“ãĩ{ FČĨƒ$ę^@ŖfJ…ŪĢÔWA9CķĪĖ,{OŦzvũ?:t°íã“įtãģԗug›Q&§bíČ>iŋ„Œ@“°ėŨÜ`ûÉđhGš4i’Đ~ęmâ$Líák8íëiåļíōD\ŽÔ ”Ū˜Ė/âŧ–öÁ `“&M0"šõÖ[ÕJ(Aąž’?ü^#š6&ĸAĘĖĖ–Ŧ’lˊázæŠaÁ=į ˜háy˰đĸáŗëo3Pđ Ydģāú>™ÕŠ_˙” ãvZî§xΙQ›ŨōgH¸•RHüŽîíĄ~™^kėÁUJ-Ã-v/Wۘ™T} CČÛ?DĸÎ×,“,8YŖ™nQ˜Š†ĐJŕęŅV%Ē`ģ(I•Į¤5€î' áhNœ,KŲĒ˜ÔŠÔm^ƒ!0VWĸL÷매)ŋh,ø"æUfæŽ5ĒčWcR|AA!1áa˛ Y=Ņë)?>KōKQ`_}!lŗī¸Čg&0֕5%,bÕhüāäī†šJˇËÉ D‰ôíJķļ‹. +kī.„wŲ-Į/j-_ū3&ÍļGČŧ:ā/ŦhŽîׯŸ “ÆžÁˆ¨î’/BĖ.´cņĘ6==SIUTIžHž ‚§ķíˇŦßúvjU–\œ(w ƒÔˇUĢVÕĄOˇÁ‹m•ąæxØÚ8~ąU;)“|CCzņ*YsĪ-Ļî(üũûß˙Ve R_xz…š}‚”ÕK×ū!āãŋ¸•U™Fv†&.ÄĖA ¨/^HâJe/[GãEžECž&mQ*Ūyd•A††î˛€!P‚døæ•IÛĄî­ a“×+0ĘD`›öĀ ]!Aw?`t¨ (›”§õ9™T DD†KL4b‹G„KP(ÜyöĖÃõ—(HËI™w8M “aøĻw‚ŽøŦY…9ōãéErŌ#ņh=1 ¸BȓŠņ=oįŽËîvnm~øá‡ nđÆo¸UŪŨBT 阭_U.ĶHÛŗSĸÛˇVÚ2ņL ÆŋÚū]Ā´† zIMđNė\9'ƑzíŨĄ€]äD2Œé§ÎØÂ|đÎíˊ•`?ææŊ4#jK`āŦb~™]Ŗ?V. ‹L éĢÛÃ0ŗått ]1 ۈO´;ÜLŋFYU0gīAY{ûpKø‚æžPøBŌ¤5’Ŋæ”éŠ “GĐ%ã?GžŌš`ųË00Q@˛ûa–§mE…lĄ4…hųŒēāi0„Á˜ÍéhÜ,¤ŽaR’ō6 Rc"œ„!puī¯äĩN!SÜh˛e‘€ÆÍĒųr é}™ø×ĒÆÁÍČëX´zfvĄ+&h‚´ĪâcJŨŊSr2%˜đAƒäíAJ4ä†ÔÃRëÂ?OėŸ.3Š\mŪö™!Ķāx]å^VÍGhĢkĘ&€…ėĘũôS•NÉx õqŠŪŒJJûnˇ¤~f_ ËÍnˤ÷"Äqj~ņNĘĖҜüíG’›žfŧ­å>aũĄęÔÉDĄŅˆ<@ĩ„’”7˜oŦÁXßæŒŊp4^¯¤ŠMp4vîUËĻē<áWŖļ0Ŗtp—^*Ŗ4ƒԘęĘ.edRQŅ‘ŋ°°Pŧ›ZÎ׎gRĀšÄõkqĮnpĮų<$ŗUËÄēRėFĶ0$’}{æA˛ĩšœøn8ĄüŪ ŽÔGŨí<ŪdôPŽQÚĩŧAœ¨"Ķ]GOLĩeōYŲ‹Pø ãĸÄÅ8s´s ĮRW0.†\b^ú-Ž\šŌ~~ƒļФ7'bĩŨ_B‘gĀÔ> ŦaÎ֍úËļĄMmŲŊ{w*ŸĄ$äu†…åįšp4~:ęÍĀT÷øK}ۊ!šĶp2ÄĨnâfčØLĘ\tđ  -ā IH?>XĢðq¸C`ŦnøĮqEŅ9™9†0„s‰õ!@Ņ[LJC ˆ“’ÔŸpaj{ĢŅgԗņŒ†Ē7`ΞØŦĩøyÎ/@1)‹UcR{‚ąBYIbm%ÆsI‘AY2)­ŋ’ēËq…’x›6mTŠ‚älÉ;šîdp§‘}WÖa­&û/û…jqLV}Äã”~é9°!}ˇũZooP5ŧũöÛíā×ŌÖ¯EŅ×SĀw䯌ĨI\§ņ^ŪÂgĩם°3sԂųķeė¸q2}útĄ1Ũ‰đ'Ŋ1Q‚0ĀoĀÕp††ČĻDä|0…%ú@IDATÍÜŨÖ+î°_-ŗrŖ•ZĨŽŋ^cX¸>°S „_āT~RŌö;:$ķũRũ!‘YqÅטÉ(“Ã&0n3"S ÆĩP‰0qš|Í|gŒ(Kã{Y2,úų!ãšršĩĶĶcĻęĨ‡R‹Æ¸Ö‚/í û&H[CĄ"žķÎ;2tčPÕVļÛHĖ5pbōŗ ˇōĨ-bZ´_å=$ųÉM eĀ<´Øl›ņžÚžW™žŪĀ)¨c8PqgmPk73ûĨtDpņSdrT™x’Ys­ŒõžĐž}{Ue>Ī ãV ĶM‹EVĮĸ›ŅÎCéäYĀŒ, 1„o×đVjp3ƒL}¨ŒUå]âJÁҘDÂ?0žs…)ÅŠAáģûlD˜û×Ē+Aí:ØŲOwŪ7ŧ’ÍxŽ8dV¤IS|O”¤*“IŠ@H‰ęHą’”Eåā'ČĘ}P˛á`̧FëcƒD‡õÂ,ÜuNë|ŋdŪVų!iÛÚô~—đę=éëÖļ)eŅ›āõc?ĀaÜÚ Ŧ•/é/syî âģđŅ8OûÖ4¨‰ĢĀŧF:DėĸE‹„ƒˆô7ŗŽ'R=„/hp×Ū6õ9 ōCŌĩh´­}Xņß@’RŠ™ÎķIIIĪs “č°lE´5qubđāÁĘɉôõP'‚•‹/‚Kžt# ‚xŊ Eŧz5rįa%Š3 —‰éA*HÍQXĢŧđq2/ĐČūÖąŸÜ‚/˜ĩ™ąÍ;†Āč—ÔMTR Aá|ŧ0nøŅŅžæ ÆĘčĢ’bU yƗ:í­ŦŌŒĩí @A—tŌ Ņ ‡qĮ.E$Õŧ˜ĘˆŦ ģTH2ū` —†Iéû› +}ÛÔįØĄ€Ā؋åí33œįdt8;Ár%”v-úCöˁ‹ŅžØ+ˇ°bX,€ØŦ7!Ŋ•ōGÔLn^fZ ߛķqCß8°>ÂižŒ‹ŌÖA€ûĮ€iumĐ@Čd˙õ¯)0ŦņęBØv_|Žj/¨”t<ŸŊŪZ(ĀŠ]ģvmfIíWŦŋT ‹Ė ƒGÉŦȈ8Ø4įPVŽ'"tGŒĄV"EŅĒŅ|L†Áƒ͘Áī‰ļ 2ĒbėR|dL4žĮÄDᝄbRÁRxŠöįn;Š-‡%a˧bIj”ģĮŲāŽúËUüwÚûĢ= į@SĩõsbW˛dXĮ‘&ģf =WI~OŸv#Ė ÍI¤4DŖ|"ŪīdØļîĶĶËĪČp-˜–™¤Už*‰“ĮaE>7ÚHÖÚå–ˇX‡1å8#Y–6?Q*†…8<×Bu{YŗWY1+FMxöŲgœ3ĩæ >U>Ā€ų§aŧÃÚĢo82Ā„áŌ’_ÕģøOƒ ˙ô”ōÖfɘsĐžĀ|ÍĪúØí՟–`LŠÛ†5”p Um8)īĪ9–´hé€!lH߯Ô=ÂVØ[Ä|‡ģ™Ī•>ĶĄkIR0VXĻÂ!¯U=Ē|„!øÃĀĒ1¨’Ē|ˇĢsJōĻÄ ŌOËŖG~1/€3ą™ģ–ŧūúëJ֞’•sļ&Ā‘ æI@øSUÉB@ūĄŲ$ėP>đpöĢ"m+Kp_.ą3_€äė3mÆėƒ`V‡LŖ/đ^`wđ;QŦŨ§^P5 TDCĀŌ6}Ë"Ë0Ũm˜” jŽŠ†°ßmfĒõŸÕ/™T TžāŽ=“ĸÄĄ"ģ”n%ĮPĨbūq5—Ē™%* +EUfLǍ-˜[ū9Q’w:ŽŌļŠa°ōÃāŽHŖFƒ;§ë­Å0,Û‡°OäÅ P´KHs0v­ÔJ õXY?øoŧąTÕsb1CŠUļcąKU?/ϊHĻõēœVŊkjՒŸ°"?đb"”â("ĸ|bβ:ĒĨĸ!0)C7 P&e(KAœ´GqŊ+QۊJ†ØŦÔ=¨|ŨúH@ƒÆá`§Ÿ “Ũ`“–āf„‰&44DŲĨbbĸąí]ģ”ŽË]obJΆf–ÁYœ&ÍˁĪ §ŗSRbĨ*hi0Ūp’bÄÁX @Q(ÃÆĶjŸ*:#8˜1Ŧzõę æ´?Qĩ#V‰“§D Ģ´D˙ŋcĮŽ9Uãę`ĒV1™ûøc0­$p¯Ûš7—šŗgËŋÔf$`wÉ6ÎB?ŗ*õŠF‰–=īGV›>TÍÔ@Æš9sĻđ%“8;&S}ûãl( Œ?—Ä^"A-Z™5 ŽŅ‘ŲVÂņ_[kT–w= SŽū qyÉrQhc¤]īĻŌ¯3N8‰ŅŧC í+ æÁ ŒwÎ<|\ásecqlyņ{ŒŌré@ x5VøZAm†ąA1)•ĄúY‘&MĀŽėA1 ’ā‡IÃÆÄœlÚVUyõ8EĶļmæÍQ]ŖÁÆ~R. î†XėīĶ9‰Ęw°¸…‹ -†‚!Y9–s…R#ghqŅ(y2Ž#/pqHŖųĀ+dI›”'dD§{r­V–%Ėb[…ō{cņ&ņSä×ú´dâļ€úo3•Kûôu;0 „šā _rÕą´ä1ÂäTÆīg8(šō#Đ#CūđÃvf•ģ#Qâ]fŗ?˜„%Ö_ërö-ŖũÂeyœTËÕČdL™R”ŌÂcķ¸Õ -ŽNãy‚`į‹eR†%ˆsN#mPG%;ãĩíC Dh›āî}¸Ķŋ^C Nô?•ŧg¨TcRœå¸ĘG&Š@ž,Jug“Đ4Iܞ͊ \œŅ(w7 îČõDûãŊ'ģîåuĖčC†eEœäęUU¸:ǐ$:Ķŋđ N—0 cŽņ9öœÎ[ \‡ KJtV6âú64[¯{Æxēô$ās_ˇž|…P5áYœM |"°”gÁŦL–5=oĒG ƒß7XŠJWÆNãK`ĖÍPšŗ!NâŸ\Ž ęzėŒįÍ,ųœ5éÉOēZÖvä¯%ĒXÁ °^fƒ^­2ĮŦTÕI/'e`‚VbĨB:8šŪE(."JcT”Ļ`“Ē FňÚ*ßŲfRúŽ'Ú =ÖؘÛ*ˆz–l˜¸ÖN‡8 ē6A+OWTmr29ž“ Ė4˜ß-ĨpaqÁȊēwī.C† Q)¸ŦʏkīÅxܓ}Dču*RģŽ„•ÃŌnƌU› O‚Eį…ž=åÍ)¯Ë]ŖīŅN›ūbĸ˝8iz˛=bX@ĩčrC1ŲLüņĮíųÜōOfJÂøU3ØEÅŠ€%h¸'—ĐčZZ>ŁLí„!ŦHŪĻ€¨´MÔ€ķ¤]Ve}ZY…nAŧ ŽŨÅŋz-ĨĢy.MECšÃƒË é\ĻœžAn2|Ôv8ēsŖ…üÍ?•‰q„Ô^´węˆė‹ĀPw‰˜ļeâūģĘĨ–6'5P¤“Ąyôj Ų}hū˜ew‰ Ģ4ĸōŒ°`¤¨‹ÚK`L˜ËŦĖÆkJ˛OIk˜ÖŦ >u÷hYŗf­|đŅG–UOÁŅųCđŽ;wît6ŧY^i~Â#†ũû4 Š K[×ĒåjÎSO=eÛÅKIzmŊ2šëÀheĪ—_~ ! ™‚éÖCÃųüäu^…!¨ū€^Pŋ1‚āõˇ…niШ}ↀ™ĸ+„+Ōfm%MEEJ•ǐĻGđƒDÂīã\’ĻŒĪū/šH•…?=Ņ%‡éĢH”Ž1z1ĸ įfOˆ™ŽĢŌÛō’¨…5Ŗ•ûˇqËáÇ-Ëō„'ę Ë_Hí¤¤Ds‹Y›Ēõ†^ŌZ=ģŽ’Ö É*˜?Ūyįm9÷yũeY ėÜ ĐæwĐöYĨTJĢÛ=‡؃¯Ą-Äl–‡I-†zúO{%ķ¯ÃĻø(˧:‡NPŠ"ōÜC8 PįtrųÜJ•(Iķ™œ! i+!ō<č’nˆL2>Đ6ĖpSĀNŪKcR´%’9UŽi Ēß9-M™tUčđlLĖAp°fįQwÄ8ƒąĐ^m‡LÜJčŠ'´ ‘V)×ŦâŨȦ2m]dX@f+ĀŗvN˙˧šsįęģÍUķ’2,b¯^zé%§{øWАęKz9Ũ“¨ø0ŨLüÍŸKĪKûČģ­Œũē1cÆĐ/išUwŽģͰāBSļĢŽü@Œę ĨĢn¸AŨ/gSœ$ŋą Ģ{g—; 9Ûe4 ڙųũÎUXŠÕŪ…!=¤T<Á ş?%)`–ƒūY….|ųxšÆ¨'Ą› č4Ļs1í\—ĻĖŪ1۝ļÃŲān‡Ãāĸ<3ƒ;&.rÄåzļ°A=s+ŪVm°ej4ŽwĒÔBjúĮČ/ŋũ*˙ųĪųĀJŦČŦ܍֊]J#}I p"ŲŋŋĶå5]!aë•š:hŧ1™Ö÷°g…UĢ)_‚i 8H’Ą˛šĮ-ÔŲ Č~ôÛæÍ›˜•qį˜Û î7Ũ †sv0JXô5R!ąėœōÁv›øŽsPu§!gŖ ™S|ŅÂŰē‹ IÖĻíĒ€Ū$JSp6&“ BŒ)ŋĘÕđ%/E&UüŊl*¸bK…›TYITt:Ļ4-æœVû\õ#۞´É¨igXĀīå2鄓ÁŨöĢÃj‚qUŋŲšYP GTĩf4â3SŅå1]ä“}ŗU4NãJ!ė1ōÄO˜Uoy,::ēÄ .˜•Į˜7ō#möčĶ–ˇ-ŗāWø~€ĶÂî-]ēĘ4¤ģI.8&Í‹tUũáœ{Ęėŧ;ĮÜfXXŠíiĻŌžÅß$…^_ŋ>¸ĪF”g  $đghúŽqEˆ!…Ŋ A˙<ūu"ƒ B ÷`“ĻČđ­Ķ &ĨISėcĒ{UĢU~**… 7u.ÛĻô}`šÁž—›$ƒ„EUyIL:‘ΤHßŖ;îLĄ5kKNR{ž™Ö¤îTŅˆlˇ ÚGˆÃX-üėÔaz,úąŌធũZß{ī=§ˆŸ–ĪYt‚߉Џ˛úķŒ`ÂT]F͆eŪvDwh-^ž_õˇwšMĻEIëk€y‹Üš`|¸‡aŅî6¸&Ŋ āi‰ đn1,|8>PA.&Ã2Û4h`÷ĘŲžˆŒ0ŽģU­Õ3yũ¸‚!@’"‹Ō5É;TšōÅXæftoķyréeXíģž|Qg¤)¨~ÅQH¤âLU}ŠQ;ŅũJ’:īUQāuHÎéS’uÔҰM—.âųHôĖGŌ #>ž›6ÆŅļĶõģ™ûܓr|ņßöĸôü 'ĀwԊaqĩ˙暒cYņ¯¤Ô°aCåÄ_’ëßzë-SĮãˆVmĨŸņåjģ2k?™^Ĩ|˜.rī”7dõš5˛ ‘VĖRV5€mŸOšüÄJ3ģTs‹ŗĀc>ŒĒžÃĸģ‚=AÃ×zÜËļ•úU>zá'†° ’ÕŊ˛€!h eVcÆ=éË@xõU_x*MŅFČXSUąŌ§ųôŅ6Ĩũi÷ē~ipOƒÃs–!T Į” îH:!:Đ1' ĒėÅÜßķTí}‰$žĘaąīræŽęWXv#Ípãa02ŦŌíĖÖĖÉŨSZŽüFu”u(Úöåˇ$¨jÔY“ŽôĪB†•…šøgdí3áš÷†a–Z*Ĩ,`5˙‹Kœ—úJMļŨbXˆÁ\ Ģ ?&ŖũJ¯(˙đûŅŨäžårˆ+}q~rōOåhėŽúP’†ų!ĢWøBúEŧ)„ÍŅŌ¯ģˆ fŧO!$Ē `Ĩ”ķņynD7>›Ģ}.çB"ŗ|›6ļČÜV!e8…ëˆŅņÜx9¨…7n&M| éāDĒÁ(øâ3ŲxÖç“cĸUH}K;Ĩ/†švbĻĨ$fuí8ĸđĒøm “ä)!¸€J|jȲîõ7KõũÎ fĨ=™É>HYM/ŋJ.>mÍÆÚ)‡_¨¸!€HQĘēÉS)Ë-†…ĐĢ2ĀČŦØ ŊNÎhįŅžÁxԞWe]šõRk‰ÚŠ@xū5=ĸ‹uļ¸Q%Ē+HaĻܰMi÷eŌ>Õ Q]<#TxaM’ēPÔ>íYM!9&mvvĸĩ‡Bg02‡YŌ ÆĩwåÚbÜD Ē)Ė5Ú´ąTéÜMŽ-šooĐ)eĩC,wŽ ›Õ΋ÂIÄFķ4´6ņVĖæ4~üøIVdVÆ 3ŒZŋĄ´žđlĖZ}vāŽ„DyÅSĪČú›†:bŪ†!‘ÆG8ģĀŧ„ųQˇD8Ŧģ‹ŸÃâʇF…đϝ8롙õŠ[ŧĮŦ¤[]$Á{)iŠęíTÔĶúÜiĘØ9t¨fũP[V!WJXÃÆf§Õ10~ßëZНp‹aą<ÄŲųü52,áĶĶĶyĒXú1~‘L?1Kåø+ļp 0(˜Ũ5øûSŰÃåĨ‹n8y˙g B’d!éDúqGw˛ œqÉŲ‡,Ī&0FŨ›íxû”ėĸ`øŧʒ¨Ætī%• ߯a8ž¯„'D  [+ĮSģ0Ä+ƒMļ4D'fēÚ0‘„ÕŊ~„ôšŊPĒôė_KĢRįĮq*SAUÃĨÎu7Z6Lŋl~, čN¸Å°€‹ŠŨ|”ÍŪâ,eéę+vķĨ#_!”đĻ21Äs~ĖĨ\Ž˜GîRp§îvL•ģ×xŗghĒØÁ՛ˇ8įęĸKN:žķ  ip×bE)ƒģōŠ<Ķ|å ŠÔfiéë !ž!ÎŽ” |<“Ö4ôLĨØâ%"Yˆ•äΈĒ>´Ēe¤R‡ K¸\Ŗ–túøkéôéW\ŗ––QÂęΙË8QÔžö!†ĖŒČW e Á¯ĩˆ[taą üZˆÁķŒî×ŗbJTüĩ"¸‘#G*ß)ŠŊFâōņÄßÃx™e9@Œ×¸ģĪVQ´xfõ0NúŲ$ve ˙O3(˛<;ģmtîÜŲŪ VI'Žæ O@.âģëˆęFŨá7‹;ųOŠÁÕēz˜“ϰR9Ŗwpe[OÜgHėģöL)í^=:Lzļ­#ŨÛԑļā˛@+NņäU¸îđ‘ré_KĨūț”3ķšˆb/ūIĖKPęlĶJĸÚu0/€Ŗ˜´ģËeY č„KŖûmˇŨ lȗPųΌ¤bjdęŖw‡t͕WH×Ū—ĘĄũûŽÚ/û?0ŖŨXĨ¯’ˆN–r‡q×īŦ>D֧(Šŋ}U@)ũÁrŨ.„SĐ˙Ņžn@¸ķņéôL*„ŗŗYŌ č’ģ2ĀTęh7ĒŪĨģ„Ôˆ’<7ÖK8ÛGué&‘›JŌž]ę~ü‡~§ËRˇĘu•{;HQ„äŧrô[D†Øk/kÜčÚĒ–Ü5ôbš˛[Še ô—‹Šą‡ãeúĖõōÉė͒‹‡ļ Đē ¤ŅŨ÷a!@­R3uĨÚZTsNö ö•jũIÂęåĻíÃĒ…\‹—ā¤2=™ÂAĮéÄP KļÃĄ ö3vš;gÎҞzėĢSWšŊ÷Ĩø!Ķ‹‘ž85W%-V4^XĖ>}ÄūĶKŪhp¯rĢpUÜ7 qĪkՃĄÖz0šēŪ[įrr\^ŧu“sĨŧđŧÔlIŠŨęĐ"Fü`Xd’-éb`V)9oŠ‘ĸ;tvŠ :”‡Dë"ĩā k¤ßV Ęé6âŠ$ŖĪ~~zŽvČáˇ˜Ķ§O•%oß,ŖŽî`cVTcņ8ÆEĢË´'†ČĸˇFČ]›8\ĢßIA_,î×Uæwi-knģQR“đsūdô—œw۔Ģõš qÍĖYĩ6`˛opIæWãTā Éj¤+õĪŦfĻąž}äÍ2ķč)y’{Ĩž=¤öuΊ’6 ĄãŅ>āmbr‚‘€Q|ÖtŦT8ãëhŧĸū‘‘ÆÃåžĪåqZvšˇ§,oHƒ{.Đ홃;čí1°Š’N€?9Ũdvfv8Æ"aM[x”¯OÍĢū-žđ2ĐĶRøŸru™ŒŠÄĐDķ’×ĒØņúrÜnߤē,ŖēíĒö€ę |&Ŋ͍|avžt…Š8ëåa2íáABHĨ9åĨĻȑßÉâËzĘÁ¯žš ˜Vd›‹$¤nķ‡ĮQÄĘē‰*_ŠĄ´ÃBTÄP84߃„ķņ!õ2”ˇīŌéšOŸ>ĘYÕ~°hãāŅc2eû>)Ā­}ĐĐÆw?(žŽ/ŠƒoFü —í3Víö~Ü7úGvī›?'-¨ÍŒ âņá|ôļø``¯Yų˛8F›%Ŧ˙†…a@ƒ{.>N=1/€7=wŖL:Ą#ž.MŠ„ēÃˆāˆ•Z´r gЗį6Õ­ˆ$đô”ˆxīKR6I`Ąm%™š DÛÔt0Ÿ–ĢŲUq/ę Œgc†u–ūĻåzĨ1'!N֏šMŽüô+B ī~~îsĩ0°2ųöčmųP  ËvŒeœp`XuëÖíˆÜÃ?đ=:&bˑËÕÔ¸qcYĩj•RũŪC`˛@Úõ䋡ÁU`B#•Á­][„qu6ƒũ‰đLDę ˙ĸ¯×Ķmúœĩm ˜ÖĨÎÆõB ĸ„™ßIÚËO‰oĸéû›>ާˇõ¸|.2¸Zø˙@^¨žQŧlOí”åŲ°lĒE‹5fĶæjZƚGXdÜÜ7<@j_y­S—ĪJ^)QĄ “’“.ģa33R;0ĒN-kؘ•ņ¤Õ>83Göl&¯î+ūu×xY`âëī%ņË×Ā‘Ûxö<Ũ‡8lÆ´§6W™ą›hûfŋv†ųĒ5¨Ķ.ÅŠœ ĩėÎG–vÁ÷Ž |ĀđÁFĮ(W‚J͐6ŌGĻ_ˆŸÔŊé6§û…‹Å”.ņ/Nyx€6­ę1J=´J<´ø/Izú^ņ=yĶ#é”QÂʇꐟ_đm –9Ũ4•Ŋm7¸C˛RI'nGOnîČ8ä`įų¨f-á;ˆ‰† ÁĸZX jĄŋÁayuî.Iy­ŠTyĩ‡$Få˜ ėŪĒ6"w8NŌnß+ƒ;5rk1‘%6}̆ ģÃí۝K•ZØ‚ƒa2ŌÚžãAŠ™ļoöĢā5āÄü1tHkƒîjHa2nėS’}:ėTĩ¨ŦĪĸĩŌīŸ Ō÷ŸMˆôø°Ã*Š #g Ĩ.ũ–° cÍÃŅf¨Ŗ¸]ǟt`Ĩ!~lí›Õųí§mÛ$ņOŸX¤( 6Į‹wŸ’ž'T„vŦ žĐíų’¸u“ÃŖ c…¤Į͓NĚdyŽŧ KžZĐÆ=ūÛ '/:-o>+j‰ĪŖÍ°Š(ējYŋŠáˆģ0=Ė]ŗOŌiķrƒV-“S/ŋōGŨh™įE¸:ĖŅÆ’3! JĐ4l+/&įyȗ\ ūN¯‚ŗÕŗ(ãtŒM&ŋōĒ ūE‰Ã俆€dž‘m[#°X§ øĒĄõĒ ķ§i RjĖI„ZhöŒEŧ˛Ī,( yûDíã2C,g9(§ĮŨ+˙ĖĶâ23cķJc •PĖĘ.­|ng¸{9îâųōâ%ãČ!‡›2<‹C*īHš¤€yBm3–™aXLČÎw~˜öŊöĒl~é9'†Å†ÕĒQSĩ/92× °5ģNUĐØm‡Ũû á_—ív¯lQŠÃ?|åQųŗ]˜° _0Xõ‡męBK0ÂF[ėX-ȓŦÎû"LiS,'^oUĀÕņŲožŽ$˜û  bĖŲĢȜL ƒĒÖU×9:›$ÛH->€ž>*rã%9ÚĩnŠ:P>nü„T pæô9)IręÅĮ%īį/a;€ņ hÅȍęKU$ ëB'ä´];$+>ÎáQŠFEŲ0ƒŒ0KŒÎ`th÷r:˛EĄzį..pōûŸdĶ O›ōš{īŊW4{ZŌą™QōA–h•L8>!]Öí>aV­åąĶ īx<`–EΙ|Į k×ÉĒ›nÕÁ&mŪ‚ˇĸæP}oĐØ˛Ŋ`VMZZģŠŚ!˜á-NÎ|ôHg0}'JO•Cß~~ĻANΠņˇ@5 Ĉ\ÕcjxÚ*Ęę ŋ%ī58ēŗ|Ûlŧ4 Ž­?ĨļķĄžzį%ɜ6"(8q¯ ōƒLOĪ(Ņ7āÔøsø?¸TDh č¯'ŋĶ|TU–gƒdKƒ;'ļc@ĸë)¤F Ŧ]ĮmHƒ柌mģdŨŖcL%̉'*Đ3ƒ*’|ļÁŪaB'“Ō$@“b·üũdÉæÃr*É „ĢîęŦS'a|˙Į­īLwYšornĪK˔u÷Ü*GũoeÍíÃ%79Õ>īC.‘pØ­ŧ¨î˛eː$Áœ|ą"ø’qiE{õę%k׎•_~ųE-X mš6ÕNŲ3°oģÚ ä\ŊĒD4wnė˙*=ļ<(—m\Fî~I6Ŗå Ķ „ˆGD2˙˜nŪĸ¤u1œYėĄ{ĨÖN—đ“Š˙éKI›ZÚvGĀ(‚>„Š f1¯1î˙#ī+āĢ,Û˙¯õÆ6VtļÅAQė×xWąÁî TD1ąETPėNTBE@zÄ`c6֝gÛ˙ûŊĪžŗ'OŒūūׇqž¸ŸûÉûē¯ü^)cŽÆ€?#Ūá€j™ÁŦĪŅ*Z7{ļĖ›7Īģ‹ĩ, G-O› Õîp‰—tįŌgėülī6 œ”b}č¯Ú!ĖûŗŌ7ÜÛ2oö¯ąOÉÅŋĢjΗn|B^Îũ\͌ōéAŒ…vJß[å&TPઊ•Ë<čk=…(ėíä6#•B5ü?j|§ēPZ!Õ[2 OŠļĢ´4Ī„Ę 9MĨ”ÆQIƒ;ßŗ™â0jkˆ25Äg"õ9ų’ūė$ĶQö*ÂŊx)ŋAj&.—ČúPé•"="jĸ…kˇIMlŽĻë45ŗŦ644I%bąÚB,ÖQp¯=¨-ėâcȌĸģuFÅĸ‹ŧgŠîÖCâ÷Ö\‹‘ŋŅŨ{J8 ‚8&oG††ô›Y˜åFÛŠ…ŋũö›Ė˜1CƎ+ņŌīxū˙¤Œį!ˆ á( †ĮI)2ÕjÃ˛ģđ¯ŠĒj%vû´m”Ž~/_ePč $cŧî9V15§räZ?ô 2xuÚ÷‰ė*ãŗß˛ÄûÔädIÁŊ×IĘ=JȰ]ƒŸÅđ†ŧÜ|é×ųxV˙—ˆLŖ&;Sjvän‹Ė*ĩå›poj[†DĄąŽ(› á2­&nŋ2¸S˛Ī™5C*ˇåčz…‰’Q?ĩ*ĶųŲyōĪ]_HÖęu’Õ\ eˆ|/kD@ą‰2ļ•BRÚ&ĮŲœ0@‹?^htl„ôīž$Ųųöļ1Ķi ̍#ĩ„v6ōrC›Ŋa…Lk˙I“ĨÀ}¤.?OáčĢȁ–(~×I‰B(zxŒíŧh ūĀ–Ŧ¤a˜áÖBgŦWŗ¨†tˇ7N† Ļė Ā`v.čuĖ,áá`V˜ ƒ"\lŽ!‚đí@!‰ÄĐx¸–›láĖÛAĄvô&*>Šä…>7)ąŪŠ*Šv,ûâĸĶ)č3Qnö‘9Ēēž¤HōšC’o'ágŒŗÆhGÎB)̰°Xēté$1ąQíŲĩv›{ė—ÆÖĘM$kāLx֌ÜĒ,ŊIdŌ îfŲČä‰ÂLZ'ž ŧąeígMˇ´ģAĪđ’+u“súšĩ˛C‡÷n9 e'÷Y?¯‘㇂aA.Dˇ?~åH9ũūĨÅû‚$æŌ°Ŋ7}Tވ(ÄbŪ¨˜+‹fĐáæ%ō€˜p ö•ōĩĢŧ›õ ā;ŨaS$kFk#𞺺3ŧȸšu@nšå&&Ē˙Álĩ?&ĀKW­Į8-ņfÂã­ĄQIÉÛš‹á0Â}ü^žQđøŌˆLčȸ}žĐŅļÅ÷HũšpÃcĒŠ]ܕŨAô žšx¤Ėô ô‹˛zFŨĩĩRđüDŠ{9ˆô&šđ׎D[VÎVØËū¯>?ĒÖfŌFš]1,SüdˇĀžYė†1^Gņũ 9ÉoJĶZʖ,–˛ëuGÃc\XâĢŗ8ÄŖ>*4ÂŦ´NfĪM—å˛Ë mō˙‹p#ę)ŸMhhKŧvžT_ĪyÅÚĖčmψ°Ädé܇ēŨ,fŅ="YFą€€ëüõ¨šrŅÔ  z‰ ŗ>$‡ĄČĻ™š0Þ÷ēTM~PBëđfÚ҃H)̤¸TJJ€1Ūæ‡lžâŊ`‚Uņ*c„;¯JĢ’Ķ\博!e˜O´Oķü•#$Ōŋ~DU46JEzby–éZąÂzMúvNË5PßøĮšŦS{īv 7=¤ˇü9å2ųâąseĖÉH\ ¸jDŠ’“ū˙ŠéæxxWÉ ĩ mPC"''g0Û_ķrėä‰'žđš@‹C ôđ¯Ūø 8ëoG3Ū‹„šūīTÛǃû°ë‡—đŸÄĄvģŧÛ8C_ŧq’Đ.ÆŌãÎwé=DЇŊ#;ÉûĮËi6ũķŧ%?| ââ*ČmwâömyČ/l§Üz[{f ŧFęĒÍ į':Ck҉*€!ۆŦ¨Úd8Ž+‰ ņoĪÁyÉ ˙X`9~4 |2 'â$b76æ,Íj1žŌ‹Ž $­8 6üwä`™1ūLųëÕ1r`j:ąÔ‰ē`BO=æhK‰SûŊy;™Ž/įV$lëļÆnŰh܂-aTÃåN7Jƒ0Ŗ€Š"rŸô†~NąŊĨĩÂ0cŌŨŠ'–ëšO¸ąUõ9‡ĸro„'øĸRP¯Ûüœŧ™˙âĩ"”‘ŨW{îŖĘ|×ûŨ!×tļĻąMŊĨR c|HFzģy98ĘË+¤ĨÜ˙/HYĘāƒw•)%‡AÉ^ ŦĖ Ûĸnŧ; ÷˜}ö÷0ĒΛ“#eĻrb´[uÔQļL^sU͙eëf͚%´m1qÔ¨Q|Ũ*,Ģ‘˛*ĢWĶĐČi…¤-ūíĶ'E^ģī yvŒ1ičp9bÆûˆe‚$ÖÎéšv×v0,ŨŧŦ”†aĮ˛ĩķ(†Åë\L9rēŽG"ĒŖ5õ …’i_JYˆ ʖ… ėÜU\&87|ĩëv”JØaÃUßú˙ž ¤//å)a rbĮCõ‡Ų.šážŦ×åҜ÷Ô{gTŊ?âšéAœÔëJxĮ¨H|ķ1Õ[6Ká}×Iķßŋĩä š[ŋÎwëļ\Ā'78Ŋ×ā;ŨCGĀ™+5pËĶū§'"~DŠˆ¤0°Lųy|îŘ´2 ę) vĐØ^iūÎ[ ÛUŊŠĒãžxŪΝ­ZjČ{īŊ'?ü°\tŅE2bÄ2dˆĒĘŦŋ.G„š`Âō!ķîĀ׉ũ…dčÁŊ’!yaP™ČgUŦßû5ļ׎R ‹‹÷¤ž9\%ž˙Dģ]†§ŊmÛļ?áâŊ‹"°ŊķÎ;J÷įūsđ­QĘ ”ƒĶ€Äe8$æÔŗö‡Hõa#%4ÜøÂWŽSFW_Ė…^DĒm‹ōÄ/æ~*×o~^å§1JŪ‘)RÚēšë92ĩīí’fĩÖ!z:˙ĄÛ¤ņģO<ÕA|¨×ūÎĮũdXuĩu’“ŗũßΰđ9•Ŧ°ÜÉ4˛3¸Ķ~Åtœ|8`ôÄĸ‘HŖ ŨŅ\ZnB†`{;P‡QÂchƒžXg“Ūq3ąž`|ŧņŊ—Bē*`šk>Ūī:¤ ĸ‘ÆÅz¸ž}}q1*čāfƒkúã÷ēeH‰dĀĄ€Åv"HYÖ84ļp&hMžļc0iII‰ˇ´ÆúÁā/AŲ˛đuŧ{ĸÄ]vƒD˙éxĮépŪĖ0xņ}H4 šę‰I¯sPčŌcŠGöë!Úzõô}é—iĪēi@Ųõų՚ͧĪI>ZŲĩŌ¯e&wmäO~XęŪŌâA´<^ķ!>×9qäIJ}Ė!>ûØ+vâeІ™4ĄVt"Ä\–ß îf ģ<„~KzĩœŦbKĻų´^ėxÆÁádØ{Ž` ļq…š´šŊMÛÉÂmV ĩN´_<Ŗ¨ˆ0éeœ°=ģąķ˙ávÂ:ÄáĪ8čoQ Æ͞Ķ2ĸ0ŗ7ĖīvØŗ6ë;Đ/öŲg^üĻSĄV3ÁÔ×ÕIâ4â/ŋI’Æ?#য়įŅË1Ã4#û>æ¨cõ§RËŦ°Cœv'"3é+'u<ĖŠ‰íöŋ*Ō•qiå ԃȰ‡ÃĀ?DØ=‰fâė\8}ŠTŋ0Q\n\ŗIũ5ˇd}KÖÖ¯jŠ ŠēQ*×§n•X‚$5î¨Q…'Ė9„T WVY?ä!xĪ–/×ĐŊgBIM\•Ƥ0ÚŖgÁ………˛zĩ5ߑąbZ€Ģū€’J¨š;)Mëûû˙aC]Ļą qŌŨŋQômŲaûÚĶĶĶsáÁyUw°aqΜ9˛aÃĩVoôâG:Wmų^+Ęá ‚ŋšŽu(éŖĨ˛(áÄz…=VÂLUv–VmT‘ÎLÅq"ÎÂ˙Aü”V@ĀŠy;ķĢEĻHb ÄĶ>‘]”¤uJĮ#,‡p>,ūæ)GŠĢ ę lm%džĩP 7oō¨*˙ļąAĩŒ ąå™†G@cģ&ą¸ŗ+¤ŠĖŨtsÄTßlSå9I΁¸÷ŲĻR¯™45ėŒ= žŋüō‹ųĩnV šą†wķuÛėo#ÆC%*kœ'f]ėuûqOÄc§yč†` mÎY6ŅØ–aą0Ŧo1̐›XˆÁp;Ēí<å ¨†î¤VšŠcÄAhKn éŨWbö;Ȱģ â'¨…ž°úžŅ} Į˛ÂˆöĢ7=+oåpâ4mZ‘ûF˙;årDĮÛQų_ŋI QL37딑̤¤L˛ˇüûėY4Ö"{ŋÁBĀēP@”¤ŠĻšŠNĐY排tĀ"ë)ĄņysĶôûĖËd–.´7“ŪFut/Oâĩž āMôĢŪezÍTĸr Í[Û°Ž1QR^+%VKØqW&ëÆXÎŧkÁŗoLSÎĮŸIæôwĨŽx^-æbŽū8XÜĻ0&ũ…čߑ~ģ#Ã翚k20PVék˜…ÎŖ­JgÁĮۆ<Š’#ÃBëæĐ0‰>æ$īqÚ% *¨cN†uĒ…\QĀš:\;$¨_2Å{˛Ļʤ­3Ąâē’Ô(ÕŅ0üTīkd|KlŠĘX'…÷\-͋딑L+//ˆ¨NėøÖ‚ēåŨԘ3kؚ•–Ā̓j””‡Đ4‰QRĻą=ˇÁ˜oŨŊ—DâŪ?á9EÚØI¨öitx”ž2A>ŦZĩJžũö[y˙ũ÷åŪ{īU™ˇŪzĢ˟?_;ĖûkĮ`ŧ;ƒY€]%8[5D0 müÛ Q0Ae̓wËâËÎE 1ô; XYČËÄ;áĢŪž}‡ÔĩĄg Ī3=!ŦN˜0ÁA˛‹Ã^l^vĮr'°įˇĸ ›Ÿč}ĩ’ļˆæā‹m"]r{ˇķ¤[DŠŒÛō:ŧÆY˛Ižų[ĮKØÉgíTbVfŽĘãLJJ€7ËוíûđÚapOˇ\Ė~ûyTąfĮē3ËĄ7°e+‘am‚ēNõ[Oq}ûKh‡i4nÖ7ņ.ķ}6'Ĩx×ĩ¤Ŗi‹Ō žÂ´°Ž˛ÖŨē íôĶíãîŧļ,4šB1Ėû^GxÄÚ-…ÆÎã;ô…“Ö •æ‚wˆ:đywACJļuĨ˛õŗVß]ҟŋ#p8K‡ė/eEՒO(Ĩ`Œß-×é“Ī@jŖt7F )\~āŗ“–ãhhĶÔH]WÖE¨›Ōĩ‡Äl””(Í07ĐWxƒ YƒpHlkŋAlyŋđš 05tŠĐiÄ RŽ“w€­EÆe&7‚cķŸ/õīOķÄĄų`Úæcĩu>wŠĘYRVVų¯´šj›Ĩ>ôÄûĐĒä4Ąč„;UžM SāZLRfęxđ!^ÕÂŧOŋÎĮ[VZ/ÕqVg%(bûĻĘ!Iƒ´Õ=÷ Æ÷ķ˛-ļįwWWIūĪ /ŊR 4ˆŦW¨¤.ãcŗ=~wo¤T]”ÔÚo[(„Î'\+ųĀÖ­šøŽ›š4ųä5°UA†ŗUB-Úē×~ %,^d Ԍ¯-úh”ĩ65ūĄIŦxa–=ž†ėIÎvŠ4ĻŽüŽ2Â~ô†‰˛ ^Ē`<ˆ#㔏=ėčA,xãŠyųņ–â­ŽŽãõq°ŗ,ØÆ ™ Ą´ |ĪąīöŪÁ™ļž¨‘æÖ?ŊmÃrįTJsÄ%ĶlËØ7s•gßaŠĻđņ[ۖŊ]\=û Âøœ—/_IŋErî-ĮĒ]ZĐŋÉņÎąDw†{/G<×ī̎Ú’ņōdTƒ)ŋŽ8H~~€üqƉ˛ö‘‰R´čO¨Æ0K€y™Úļ펍¸dVŦ­¨'jVE…€ė)-k“tÅž|2,§ũžY>C-ŽËdTüËÎÎUé&žÔA­“f€˙‡ 9 _F蘍5۟ŧ ĨÁ vŠ.žĐņ€=~úcÍËjrĀ´Uq`zk Â ˆę! 끏ņfâX+úâИx—¸*ĘÚäA$Ķℱ@čuŨ[™–2¸# Œ{”––ĻĐøl0Ę8,=qB"2Ízb ÁTköWt‚ĪƒpĶe%ĨŪŖD´TÁŅú‚ŧ5t^ŦĶGœ$C#kģƒúíŨÎŦ'bĮŽÕôŨ_›€“ÕØ5ÖÔHUVĻäĪûYŌXœ4GIÆËS01z—ŋíŖ˙öÜÕ š~fĄ‰G`OĖŲBĻ †ĀH܏9ņ Ÿˇ÷oŠS‡ĖąōGdTdn›7gËVDlL4Τt–˜CŽ4ˆöīJūBŽ8UGÂÂv& ‹5Ô˙ĖãYüāōŒ§eFÁ´"đ¨qS~ˆöŗąMëw§ĒĐc×ŧlá\)}Å[ˇme8°p }?dZŦe¸n]<¯¨žíüHô‡íÖeÎúå(KO¯˛žhp×>Ȇ 0m~(:bŨH–'О"S:I,1¸—ÂĢJéŖ) QĻ`d>7Æj”8ŧˇLKģCŽërĻ Û_aüŸČė[ģž+ãē_(3ú“ˇúß-l`Œē0a9‘O;™Ũ/ž÷÷€¯,nĻdébYq×M2wäá˛ņ…—ëRĩ[ë$zÕRķ…cV‹O4Ā NĻ Ö-Õ(ļĸlWxf%t"|ļÜÛįgŽĐ†vôå—_ÂcĩCí2~ŽžÖ@uĩ@Š„°#¯ĀûÚõĨ>XŠíēQG{2Ŋ…æŗ˙Xúˇ! A…N†ŠŨ‚ƒūØŦ&=ú˛t~îm@ŧöÔīR˄›š3ķ5ybëûĘHŦ'ãÆžés­ÜĶũÛë­L_%…㎑UËÚöĀwC'ÕÃŊŅ{ČO§2Ũ: Ú§ˆĻ2,ŗũ Ī-Ģ.W…5č_F\ß~ē1Sûq6° ‰–’Č#FčģQË|đ¤0Å#ô°daŌķc=.—O?‚ŋ‰*\åa䎎Ãģ;#é( ß!qĄ& 3ОĻŨūWQIgŨæ|™ŋ"Û[-X få8x2O.™oMãĒVņO>†‰Ū|īâD´í˯eíÄĮ¤`žĩš%ā¸A¤Ķq'ĒŽ˜;œrÉUR„ ģ"ÆvgƒĘnRå“aÁ¸œÎÖđN,ĄącĮJ5âkܰK7ĩđ~¤ä;ÅEe˛%•JĄ¯jŗŠŨ…ŠÆŨ[ŗP„1Z4Αā- =đP‰HíäYoųŸP1,žę[-lã€‘E)'bjPÔ°‘Č5"ÉOŊ.ö=Đr 듷$wfŊ&Lōeü×öoj+>úįŌn€š ũÃDĩšÛ$˙žëĨqÎWđ~‡ Ât˜w•LKIą,ĨÚ­†¨3/÷ˇ;čā3įōzĩhŽģÁ}Ôq>s=Åī0ģ˙›#ÃrCē⹔yáaa2/dCĘ@IDATddČĖ™3U÷Ą]b$|X'ŠA03'šFü1QžUŸĩōsœ´č…nwBõKŸ/ĩ­Ũŋa‚¸Ū#īīüeȝ\vÃ2oÔPŲōîLeÛĸjŪ^ÄOxÛįŸČŸž)é“”…įž,Eūi ģĀķgPčao(Cg}.CŋøYš‡Ÿh â…t¨ÔFį ŗåMļĩ>˛á \ĸ­›™SxÚŠ§Ę7ßũ`ŋ&$4zH7ed#ļ ˆŪ>™Ŧ˛ÍՕR„¤á‚ëΗ‚›.’ē5ËÁ´Č1Â%;ŒÄDDpđ%a‘QôŽė,Ãã0i] ëŲG:üī2y‘Pš auœôĒ$Œ8ŪÚ[füŒ ĶÉB¸šˆ€=ˆõJ5œ1`œtü˛™Ē*ĨāŠ¤~6ĒŅûĒ“4ÍmíÖ90ųGĩ;}m†°(k]ØuģsÛđe5–WKĨŽä;d°(!ɍ 9M%˜H-RJŗŦF‹™:ÃŨÄÃĖMÔ:EëLÎŅš›ÄŽ´ÆöŊøâ‹Ūx˜SzŲ\‡§{>[Æé‘y™i“ŸyÂļĐJ—Ŧȑ?ļz-ĩn8nĻL™"t0UhŅĸE2cÆ ÆL^“/"üđ’k.•Åc.@ˆ4œā­ļŨķ´ÛŋjU§iW+ü1†&ĻHŪŅ1Azüī,Šé=HĒ—¤ŋæædš°|ž!u 9OVōɰ‹ÕĖ÷—}1yˆ ~ö´“eáÉ#d=Ęׯ‡ˆ¸6ŽũZO÷.ĩ.ē%(†Ņ¸cģTbĐjb,%‡(0ķĢ™[ö˛oøĒŠC†vē đžų::œs‰¸8û’A’`Ėn‚0yŽÉg_d97›0ęžé<Ŧî ôōņ ‡¨â­ƒza&Úy Ļ>+5¯Mîd­­÷—nĘõroEÎŊØGæáÚį ‰ęŅÛ{ōPœbaųŋxīÃã÷—äđxÃąúWlœDy ^ËĮ­íķh—}Ķũ’zíŪ" ÚnūūƒüÆķÖOË܃X'ÄĻ!qú!™pž;ĩĖąRôŲ,Šxbœ¸{Ķče~ ŊƒŠ¸vÍF¸+ĶâãܝD†Uš9šŊņŲ2œA‹áS)9ÔßtÄIˆ•jr€ĸĄ§”…ŠėŅ˰x_dRüãWXP"ëŌ7*†E&Į:šuīŋ!Ĩ?~ĨīN-ŗ€Ģ†‰Õ¸ĩ ŒÍö95ĢRpÂŦ˜r ö šx 2Lũb b¯˛,‡ķŽ?ŪöģccČ1Į#oŧņ†,\¸Páv1™Ü‰¨&.:īt$ oRš}NíŲî†÷ĩf Ä÷Rƒ’fLܡ’GUgî°á]Ú:üü2,HYõ]ģvŊ L+ÛŠsũöf ´Ē¯?’âI÷xÔ,_#^‚ˆAouI!Ņ1Đģ[>>ææøŽ;ėXũ)Ôō7HÕą[ZR-ėŽÎãâ‡XŽÕ6„ÃVÚŠ žtë,O‰Ģ v´ˆ ¯’Ôq“$<ÆjËE`)k"Î*øEÅjâA¤m„UyŪíŸ\œz‚v)†ßŌųs<Äø0l<—†Æ6+¨üŖ´•žž!ˇ@õÁoÛ×ë°éĒ͛ƒUeSE‹ŋbĮŠa™đXå™ éųđÎę)2QRU”7ī÷Â÷OijÛļ˛~}ĻŦZ™.7f)†­ž’čį~'EĶ_ąN´ ŊôŌK^Áڅ´˙ŲY*%išĖD˜äJZ'JĒx=ö˜ų4~׉”ĘPŸūYÁ?Û@IkņeŖÁpP.ĖųVíõlįĻ1Ũ+P´´Œ8ؔŒŪ,™H!+CQāP~ģ&†Å¯ąÛvÄ÷)Ō(bˇ4ô?ēZ‰q$­ĶÁ´6؝Än[͜¯%ļŖĐolÉÜÁ˜Ī””Ū‘äÉo‚ijŒĪSsõßWĸúô5ČĐ–¨÷ ėŧ÷Îxīa=úp*0ôkˇBĩ5äđ’ōÔ4‰´‰™Ɛ‡ģˇLUĻ@=ˆ”īīqąb\vļ°¨Į÷ß Mŋ˙ėIœ6ŋxķ…8Ŧ“qqÆ# =ˇ ”BU¤„BÆÕÆnΆ͜aĢP‡0ÃĐßNĢÁ=—E' ú›9Ų—]•į°Ūũ$kK Đ땍ŽHŦ Ĩ}‰÷Į?īLÎA• ØjHųú|TíbPķNX5‡ÔT^/ĨO.ķĄzŽb@ō‰Ā[ŗķôžüų2™ôÆ<)­†úKÆE>í/2\ĒsũËâÍrá„/äĢŪ’)_.ķtjķ?UA 'ĖfˇßMĮ{ŦØŧđ mۖųuŲÍ×b˜%vÄ9$ĒK2*;_ė=*õ˜Q¨?Då9ō;â_VÖvä JÃĘ%ĘF­$)~hašŠĐ–ŠxĩxŸļzgP|ļ´´tĘ2}ŒœĀHÄIėƒAāÛ÷Oëo”8Ār”!Ī‘4•ŒR•:æĘĪ•jÄ+é‰.čŗāGÜ$;"ŖL€ë2Ŗv›,¯2ļũīĻTBûã }r6HFĶŖOPƒĄĖÄLŒÂ_[ŊEŽ…}Š1;N×Ĩ?Ž÷p8J‰íĶG”­´xĸ!6×ūūĢDN6 aĘÕß"}ęû dYЌQ*BĘL)TÆFH,´)…cö×SôįԆŖÚėY˙ÜãŌ¨ŗSôéĶGæåųę)Õ|ˇŪ%Ũ‡Ŧ: ‘Š;žBVžˇ{Œ ;í|)‹Jw‹ŠÁ{ŅîĮې °å¸J‹¤dÂmÂjŨfēâŠ+dōäɞ͈P/yt‰Ôũ‘‹p ßCžFéĒÍĢYYŊŲĐ-™æ¯ËŗåÃ_×Ę"¤×ŦÚ¸C–¤o—…+sdæĢäžiså…O˙–ÕYB”R'âĩąf‚í}9dŗÃ˙û_0Äf­\iiQ‘žā‰¨Â3Ô$YšZ6đĶë4r”$ ĤË)§Ë ģĐ DÖhæsČĘچúŽÅRųæ‹R ˚_žEčĘV¤ÚāP3īŠGD€áū‹˛ø,ĒaF3’īˇdlĢրû^USSķÃôéĶ?‡g‹zfG\h ūø]ˆ‘ÆĀ{8nM”yb”+YpĐĸ0 Ģü6ŒVæBø‘3“†c=,‹ėÉJT1å“ĸ–1'œ.a]{€I:D†ƒĐŽ9*FĸđđÃ*JĨvũÃnŽŌ÷”'ŖÁŋlUdHūˆ*â>ŅŊÕ1‹*֨ úcšđĢ˙.áUåq(>2zÛČ´Ø¯öęáIcD8Q4***=H|^Pŋ]æ$S¯ ¯Œo-ĐWĮhō–ÉæwßÔ߆yä‘rųå—Ģm5?æHŨâĪ\K+ĒÆ•M5ōrîįRĒKc …]3îÂ+%Që!žîė‚Ĩ|ŌŨRšęÚšB\ö3f(Ŗ5×+?ʐŠ7Pí( ÖDün(õō—U›xm0=ŧ_˙¨#ĢũĨ´˛NÖdĘ0*ÚļøˇdCž3Ë҈Nøšįž{ÎëđŅ< ]”8OEČŅúõH׎ĩSü÷"évæh‰LA˜M됲´ŗlĀ0#ø^Įƒ`Ę‚Œ…ˆh ęJíŪ”ąEōņ-5äIéŗ#nÅcTwoÛ"ŅĮžâņÆCLã÷Wįlv•}ŪÕK¸nË 6OovĮ[ļá„ÍāŪëĀīEšīÃ`Āŧ]æÆÔ›ņÎ%) F}öæŦC- é7HĸQ LO´1ÄÁˇZčVÅPíĒ8ģ¨G+V@Ōj‚S æ–%åĘ[”*ĸŋ&.SšŊ~ĸ°€F0ă2ņ<ˆÃPļĖL|sEŸÎ”Ę'īWjļOۛų`‡už3~Ø Š °"S¨¨r­Z•OÛ&xxԚ_,eĀiĒAā$ŧœEɸøØœūč3ŠXmŲ5H^Ž}Ņ ė’…ęOɑ]ģ‹ i9ļŌˇÖ°å^Ē^~LĘ˙^¨mõū|đÁĒęoĒmõË Ĩüŕ°[Y‡C ĘdbÎ;rųƧäšíĢŧÆzL,ûAžŠ˛o‰olâ;`j*ĻdԞDdÕiĶĻŠsŋ,{Ÿ=ķm mË)ɠƒ‰ĸĉŦ-ëāÜ)„142BÖ¯FŒUuë)!d(ŗ ?ü5!îĐ •Ũ‰Ā°˛>úč#[6´„e>Éâŋ‘ÖP€4‡kņ1[ĻĒ0¨s=Īŋ3Ŗ .į"õÁúĀl)#Üy?Ž%<>ĄjĒ`Ė×S-ōöÎM>ÆQÂb[ÖĖAAÖŋÁ@ôDÕ$é?”ƒ" ZÜđ!C%‰ĩĩK˙TE}D•`ŪcĪČT9 Ļ/l[ļĪ_ˆ˛u„´H´ ĒŒö6SmV†4§¯”č!GJs&`¯ŨÜaË:Ž6éɀÁ§”ēČČø!â¯H-ũ´ŦŦ•R x5ŒĢühY’ŒéXnÜë–7_/ę‰Rƒ>PņV:P0ëlX Ä%žžYŽØĄ-'éķ^]H¯Ē›ųšōžū”j™HĄ_|ņ…ôéĶG­7l.—â;˙Æbx°L*)í žJž€$ĩĄvĢĐÁ3ģđWųšl™ gĄ‹ĢĢŗT;û% Y¨˜(iÔS°ÆØ™ūQPFŽ;î8™={64Ŗã­&{‹ô}Rx`ˇČ2ūĪĘ Ģŧŧ6ŅÍJ{RvDØk  Õ¯i•pCb; 8{Œ„€p–kĖÛ&•ŋƒ÷ 3‹ ášg>đĀ lvAWj‚¨ŋ\Ņ–e–#_ŽŽ°6˜ÁE+ã¯÷”-›ŠōĨlę3!'H=8ŗŠr÷6h]`ĖFÄGK>L=ņ_íËĐÍD$RŗGąz fb†˜<úū—Á°š`_ =åé4ņˆÖLÄB°×mz^^ČũD2ÆČ00 ØSúŪ&ˇuûŸREĖĮT,˙Å[Ŋŧqm›rÍũ™×5ɋ ˙H„kŠG°Ã#ȰhTÍj$ß™™ŲČg܌ŧŅ „Ql”5K×K‘IÂĸgN3&7ækE'ŒgĻ'Ύ D§ đųŽB0™šQ^­č×,ãŽ^7æ jų‹Ŧ.]r˙ŸPG* ę(¯„ī‡Ī˙öĖ)˛ qvzbÆŪŊ í[2_–ĀĀĖŠCúöÁ,×­Õü„XEŗk­ÁôãԖpԚ*ŽoSŊ5i6ˇ) ”C†ÕœÖ¯Û¤B´ī„敆mŲúĶHh\‚¸ˆüJu‚IÆ5Ž! üö H´r;CO|Gí@üq=Nb}Zú­A¤sųšUJôŒ‹3‹Ņˆšō¤T~8]ĒžAėP9K9Ņ2H —nŌ̝Dī{ a3sŧ~.[ęS-äGxPL?eĐ\ą¸L+’V[I‰ē‡ÁƒøäT‰Iëoé†6,nŊ?û HH_ ŸĖ\6 ›vų™>×ŲFĶŗxk˜VķŸķ<D˙Ûƒ&…iŒĖüĢöƒ15#N§aģQ:drą&á8 Ō´/Ą/N’7כ—.”B|Gô„ę‰Á”¯Ŋöš7|Š@Eˇũ&õk‹-FvÚ¨ö`ötų¸kū¨Ŋ˜•ų<åååōōË/Ëȑ#姟~2īnķ:Ŗâ5 }}'_~Đ4ŧäā'JÚž5_%Ũ3ųžī]~•ēˇe“ūˆuė é*ÆcwEĶ:“ķLßßT%˛kŦÆá–F^ĻžKûe0ŦųŪ ×7$Rđ8rĮŽ0šęnŽîΨ85ÂXW‡Ø} ŠļŋM7•ˇAŋËD"%UËc3íĨ´ ŧwH-vĀ~5ŋ͑TĢT ž%鉩wđá–ķsÛ;ž“kP肅|ŲÜ´ƒ9xyOW ĀÅ4e3J‰ęKŠ%•yÜ_ˆ\1#´gkjˇ[W1õ6äJ#&=ĨĨé1°Ę`522 Ö ÜLO,ŦÚ­'&gĩá YĨč‰{Å­Ģ1 O›æŪ§dĨ˜Õ ¤,Ųxi`Ÿ’÷ĨĖČ˙A;|ūRM$Lŗ×Ŗš“WÃDj;ØįJ€+æ~ķe@ļ,~^œ6oʆ7Đ3!yĮ3¯īžá LąĶSÔđã<š: mĢZ—A_¸Oãl§kÜn Ü{¸ŖÅ Ésí˜ķ>¨&‰†JüAÚđŸRgBSģč.ŧ?~uœM™yx(sÔų š×Öda†´˜Đŧ}ĶĮē…ôøčŠš’›ŨĻÜ=}?З¤ ^ €ŠI<å,[æI›Ö…U.{ģ¸+C-+L¸=-q˜â­ĩRđÜŠ}û%úFY}=H ë-ŌŽ†ĐĀKŗ+:AØbåÔŽ> ߇ ÉË[,ûĄ _xü^˜ŒĮ°ƒ›ožYîēë.ÕķKũĨŌ¨õųĐEĪ ėMDÆģīž[Ū}÷Ũvš,>­Ä™žÃ­ŸžoIyŌīį2•:]6((#¸(¨|.fĒč4–°n=P0ųt…šÁ€Ķ†M|Üá1ũLĐ8›é.ĻŨDÎ,pĮt]ßŪÅ2ĢUŦß(‘ąĄBĩP1,ėeD{ԑG{ÛqĄaĶ:äÂÃ`'-PęÚSĸ8Äx ˜ĢęøĒ[Hĩp_„lŠVn@*QBœ¤:Éü­ĀÛ-ąwM””1ס"čŽŖm„(ĻÁoĨÚËX-æ †_3Áŧ$…īŊ.UO? Ž:Ėžĸ÷͡÷:Ū[}–Q%ā)ŧeéYt" ŌÂ&ôŠā“­đVŅũ¤ŖĐÎ]Ĩ™ÆZNraô¸ãWiÕFë'Į$ĻŨ(B,gÍÃ˙HĶÜ| ‹‰Đzđūrâ 3†°A„ Đ&vũõ×ËgœĄōųč}¤jŧ+éŽ;î@–‚ŅĻ֖ķzčĄrÚi§YepuövĮčwŪ^UeJķ*Æņ~!}…–Ûá‚+Ť•0˜p:Ūũ˜„jāPŅk˙øą_žpķ…PZÚoŪŽ_oˇ'=Uv ÎÕwŽ-7¨˜÷Ã×ëhj˛W-dŌq ĒȄë!Ðč54| ZGøe RŒ TČ%K9T ĒNďĶj!ĸ“tęĖi;TÆMEŽŊY:Ũ9ÁR–‡e"Wî"]ņVį+n=‰§xkg%iŲŠļlY2į)“¸ Zˇ%ąõlm_âslĘÉ´t@•„ÔČĸšxO&†EûŅJۚ)ēß`#Æ;búS5lVå˙anŽėU!ĩ…Å~ilû†gdú{3d^ũ*ÉĢ/ņÆVņ@ĒæĖ[ŧvĶs@8eäŋ=z´ŧúęĢōÕW_О_gžyĻw6Mģ=Ŗ´Ŗ=ųä“ĸĨ%™Ûų[/**Ú ôĨõü㴟yŠfĸiL‹ãĶLdVD´Ĩ3Ĩ 0™UˁLˇ#Ãęôú'’úōL‰<™+L€Į{k*/•šß~6ŸÂģŽžķđĖŦų\Ū˜įtË;Ŋ˜””äFPée ,c°6/Wz]p…ÄtˆQi!ÄĘb#ÜŽ‘‡ Wj`Ä>HüX 8DÎ:1,n'ĮŽũųkĂ´ÎÆ%2*aˆôB‚ĒS„9įčD„ ĐÃŖŸM›P†+y…ŒõQĖ’"-ƒĮ`Grŧ?O‹9Ž}–ØūƒĨ~؟ RĨĻīJ˙RÅXë€\,ˆĶŧ>_ÄûĸúB†Å´ĨU,Íëa?pÃ}Ā Iíęč:器Ė*ËĨ|ÖëŌH´‰JLL”‡~Ō5Jˇ­+•ęÁ˜, +LŪ õS¨@ÜGK(™V‹ \…/ŧûĒüŽÖŊ÷7%%E12,bI}úé§ōÕ?säģĘŋåãĸųōiņÔ9,Vw‰âÁôŽ+^“ííÃ׌ÁōÎ;ī Ļ0I5Ķ@,éå3œQʆFĪķ/ŊôR¨U âT¨Õ܇~ŠqJŖ1~gˆĪAß[$ŽŖ¸ÁûJ§QĮ‚šx6rîæË3ŸĮŒĖĘÛŊ× ŒŗÅöHįH Ė=Õß|ėmf^”Χššš˜ˇë×9*ۍ`§ø ž™-vÛģā÷"É)I žáIŽLĄŽ¨Ų×ņ@jtąąWč;䀚Ŗ'Æ9)ŧwÅhô{Z—ɤúGuˇ¨Uîú:Éģ˙F)€`9 ]ęfN•åJ(ˆ’Vøö‚$åA|MĘSđ öJŗMä†{˛^—‡˛ßVˊ‰ŋūˆ÷@÷û¤^WĘ@ø2ČŅLÕPÉ īŊ^š3F—˙Š‘ÃsPņr4t홨=gNĮÆm„—POŒ-bĨ!‘›Mé)|˛D÷\gb …`ž§'Į{iøę)z×žĀžČŦė=d$vÄ`Đ×ķž–ŗŌĮˏČ {Ŋa1Õ)†hD/ŌÕ´Uī/™‹°öíÛ×ģ ´QŌ5j”a{ +ÄÂĘÉÉ ´šm;ä{+éTm†ķĢeÆäkdKÆÆ,T÷Ęĸüũŧ÷bˆöŒįq"p;`ŋFˆĢÕÖJųO_K!ō rīŧJ Ž-ÕOŨ/Í AĘ‚ Õ@uîÛîœęžúí#I€^Ž;Đh{ĶÚŋ–÷•\ŋųy%5æAlRՁnîzŽ*tau]oMūCˇJãŸy˜n.¯ĖĒĪ…ŠŏŪ)EÜ(åoŊˆČeHPĻ>ŲÖŊyŊ!}x °äF•3QÅã{ËŠË7ėĸ$ŌĨ‡’ŽČŦš`˙(šō4ž!{Y”P&ĐFä–^;â7%‹iîmCø=ũøãúUĩĖ4ÚĪ´ˆzs|JZm!džO=õT[5Ŗgēڎēü<•WHfURRŽP0(°TĨud÷ c;!Đë íD0'­žā‚ æ:í×ļˇ+Ãb§›g`f„ŌjĨüys¤:'_ĸbÂQæ)AŠAÖVlXzāá‘ė™ĩĩ#hb:Œoŧw7đŪ‡ 9ŲˇäAĻXģ#WŠøBōÆß"Eˇ@œ˙ōqA"HA˜]ÔŗŽM‘ŽŖNą=ôĢâ…ˆŽž¤ŧdxĒAĩōˤŖŠiŸČ.–~=Å[”ēw§Āã é_c0„ö´o tĸ ĸ|Íܐ}?_*fždØéõYßc=‚ÍÄčvEā3 @5cGQbdJŗô¤́ūĒž7‚Š‹&?,”†÷QBÔĢc4iØ%3@• ÚĒÉ×nŸûŪ{īŊ–˛ˆ`jφō2Š­ŦEČ’ÖU0¨xss>Öé5Ŧūõ{ X§v™8ĸÛßAü™ß—äŦÂų°ŗk!eÍŗkÁÚi… æâC¨ÉmįŪ7ÁŦbŽa9ÍˆÉ ŠĮp†7ʎ”ÉÁ}RĮÃívÛncO5™%˙…GĨäŽ+$dÅâŠ%ˆĮct#\žÆ=!)įÁĩԟérūúGdYåFe̞ļ°n!ĶĸįDæ"š‰q3…ͧH52įŗm‘tĖíėÖCWSĪWÃļîœ,Ë!ŒgkĘ´ĒX™#5•Õ)ws‰rĶŽĢ†jŊž"û Ré.dC” ÖĒž¤HŋÛīrL/āõ÷€ÄysžßC- úôécH™ÉĪĪØ[,íhįĸ*님‚ĶVb`) ;Cš NßGUîvYĩd…ä!ƒ´ ¨īL[#ĸVŌAͯßi[-ŋx^yúŪąė°Ų`7nlšž 7Ú†5Ã醷~‚˜)qņą0ÂÆzmYŸÁĶ’oŅĮœlQūæT,•˛cb%˛w< @;ęĸČ#ŊÆĘ˜N'ĢP"“φ'…^… ˇ㎓ēˇ^ĐFX(ƒ`TwaŽ”¨ëî‘ÔÛDšQ¤vIŪ_ĸ=ЃøÂ4MœĻ1-˛+$­ņ ˇø›O¤ü‘ÛÅÅA „HĮAxīžpŠåŊ>.„ą"æÔŗŒņrø8YP¤ÆŌĀčjMÂō0ÜŠd3ŊĘLŽ´,˛20+Úå%Îčƒî~PFũžLFũąBŽ˙s…ôŋņ…2hævŨēu3 b2;¯ígvFx}ŒÔ¯Ŋüũ÷ß}ŒūæVšŠĄ´Dę r*IÜŧŗMë|ĢDĘ.ĐN¨:l{ŗSīá”NZļˇ;Ãbŋ˜e~…îk;R-,]šEKĢ͐ˇö‚ŸËÆnĒ…û‘¨ž} msëŠdåŠu’ōî ™@„´ŠĒ0s&O‹—É@üü~ß§ä—ũž•šû=å'åuD•_Ũų4m}ŠÚ‰ˆS^0sš.„ įn‚ZvÖE’úĐŗÁfēËä*DÅOGÔ5ÕC_ĄÚĄ,ŪJč›ũīRŒXÛŽ˙-˙ë7)fDHALDåė§ūČtÁp,ėæ–t˙Spˆ< ņWÜ"ņWŨ&)ZŒ:r¤Į]̓ZfŌĒ TÁ1žvbĻĶŊOr,:k_]“ŠÚh˙ŅvŨo T>?A*–ũĨmčˇëŠgʏOD ÃDHÂŅH™ę+=÷Ŧ6íŊ6K[ôŽBdFL´v"2ŗ¯ŋūÚiw@Û×ŦYŖ¸­Õ% ãɰ,Ãz¨nÛ}õjl[^õŸKŲkĪø …tÕ‰ĪYü2vkĢ™˜šŋŠ—–ĪˆUģ#Y(ķ­WU@íX11Čk ×Bpd3 å~‡ ?|ņ-°“Â$áŽ!š [#+MDĻÅŌMTGˆæ7÷Č7üžė}ü¸īĶōÉ  X‰j)6*č¯Ј `3Ô  ą×éAt5JRŸxMĸML——Ękģ+kĒ<ŊmļēFsâļévÔ*=ˆˆ˙–gz_+㑇hwLõæR0îZD~ß%Ĩ€ŽF=Ĩģ njōō^´^ŲؑbĪžXâ.ģ^â.šV"Ŗz ?p?LÎ:u+—Jé{S‘Ņ`´CQdå’}҉?—UkT“€å_÷ķ7RúŨįęØ@˙ŖÛį˛ĢUä6Ž ˙pŠŅ“Ūc.”žz ÎāįiÍiāī:ø-3:LŎ|đAUēËn_ Û PļĢŦ ļĀ6Ŋ•†Åž´wŪÆ~õ‡Q:'xŸ?´Q(Täw0!ÜyÍ5ר4]'Áŋ9ŨÁN‹xÍ0ĸŊíäaČųđ=)OĪ”ˆh—˛eͰĀŦ\ČW+yđŠĩąĨĀã .-Ĩ›ÂûA5t[–ví4^3Š*&Ŗá™ŋGģP$RxXŲfjŋÛåķÁ"ÆËŪÃW“%ŸW6T–U-īšámÆāWÅ[7e&^ÖmŒįuŲ…0˜a‚5͐nVüKi7ĢJs›úĸŠ|ur™ÚRpÃ’ēEw^)ŏ“šų?)Μ÷†Ly;ÁdÉhŊĖ ’YíĸųRxįRxëĨRųí§æSÉāŨĻ0°LÅČTˇÃā^€=šQ¤ jŦķ›Ķˇn]f<ێŸŋd/Ė&Ą¨1’ÁÔãNj= Ā% `ZúúfŠžp ōøãĢ’\™™™ĘîDXæöČ d •VĩÚpQŽP•5'ŠxhĀÍC 0ãŌbWx@'#Đ÷cįÅų:É.aXÕ¯?ø°mÅËMč6Ô.ū̓Yäp/TĢˊah7Ē€ē.ܯ‡ é8M@LĐöą-cæė€ õũØ-G$&I$lcNę]öŦˇĨąŽĘx4æI6BB™Ÿ•””ZĶĶfįm34 %3pžšÍî^ˇ3úģâ$j8.¸].§1a‘xZd‘i!ø÷tTšžŠe[Ūdģ1ÎiƒÜŠ`ËZk×ļž¸Pļ~üž qč ã{llŒ_ĻEq#ôâŌī­v FĶ{rėąĮĒĶUÎÜ ĨO@Ā3Íčv×Č6J-ŒÆ>i4|Ha°›+›÷Ŗ¸ëÅë ščAsˆžÅ[¯žÍÖ5NO§.]°‘Ē$áĄéAdåé@)ōĄžûpœ˜ĄŅîG;íŨģwWĢîLäĢŨBģÛĒęĀ=€1đZųŪ'đü­’ūZ+#]$O:M;Ĩ÷ˇą˛ŌÖ@ģV "ī;ôíīmČBFF†ĄCĩč}ÎŨ°ÂāT§ĀÔ@N˙×_V'†+1Ń •ēŨv,ÖMHy~†ÂäW ~tIëL÷i™Ļ›6)KœąĨ #ßņ`';IY›^{AĒ2ķ$<Ę%ŊzÛx.ô§‚ˆI ö˜)?ŠÛZĀ^ũ !ž´BƒŊv}_;ąŦBĸēČũī”n‡ĐĨ°âé¯Ŋpm¯1ģŠâ­]#xJÂQtÁL„`šÅ[ß)øIIZz“ā}ģ˙ŨrI承.Ŋëü:b‡!īxõ!QųcŽDF›]âŊOHęĢŗ%éÆqfaFXY™qI$eŋ29?x하2MwĮsbGī‹ĮJoęÚM"’’%éČ#dØėΤ×ŗ‹ÛwÔÄÃņĨč ëĄ1aŌaā`ũVŋË(žbhÃoZcƆģa…ž×@Ŋ–æËĄíkŲ2ĢĨ&<­Û&Zķ Lë´}˛8rü5wJę”÷áš˙/&- 2EI a#á>-ë.•°x øFđ-†+jYЁ dũäĮ•-+11/!ÉŅČØšĒߖÚŧ톮čŊyũõ×åC<ŌCpēYēIÅ`éđ íäJ-l[„Ēyh æTšzD WŧķĒBhĶiđ˛ŊėuštzüU‰ęf ¯ ĒwgæĢō$j!ŌhHņVJ2ÄLŸŒkž§û`֏†cģ ‘íąũ!ĖMB+ĻĮ72ÜÕ<˙Ā%vŋ™7hę q*:AģÜvüJ1ŊĶŧ@ āwPų#䐗§ÉūŸ‘”Ŗ“>—_+>ũ@˙Ŧ÷Čķ0 2~Ÿ–čûOL›”9aXīP°›viFÔÍķl‡ééé˛}ģq ąˆũ—žƒ='ÕL2ްîŊ$qÜ㒠Ė8j"ŧ‡`Z/šđģœaAü,‡qō]§›Ü2ķ-)ũg …HĪ^˜5QuƒÖ@4d˛¤ÜF;vŦ*Ņ­ĩ¯˜šŪÔžkoLãdDË_Ûå íÔŪߊß‘F‚đé5ôv€åCBoí€" fĸgķ™íĒĐÎ@â4C9ø7 ëų´lÕĘ Āæßuĩ4 ƒPĮ5HЍ[ą˛¸ÃķäģrŖÅZH´&"ū‰E'Ü[*01û Á=ŗ.W9L‡:ŽŽxŋäĪ]„ ­M¨æ *iēû.9úģ_åĐ)S% RˆÚŪÚŦu —ĶŖWëzK;vė@!cŽ#˖í :īŧķÚ|Z؇PÎ ĒšŽ8!E ÚĪãÖmoīEšhۊBpĘäˇ$ōā#|žšĄƒ˙ũ÷ĢõŒ_~O;.#wéUčŨ9v]6ÂÅē~ō$5;GĸJn÷n], ‹āzĩBsŖ<žhdg_ÜŲ•Rõé&[TI­M{ūRŌēĩëš20ē§ĄÛ&ŧ˜ę/gCōąŸá }­ĐƒØ­§t„1aÄ(ۖ3 æČÕ“UĨ3šĒŨbŊ\–z’L0NÕN4ˇ#¨aūS¨äībÄk1l¸ûN ‡!đZ™Su¨2i–VtŒ…¨/Ûwķõč×kļåČâ1(Ēēzjâéö€ ûYp ;2+¡Ŗ}0DUŠ/z"lŒĻōęˇīĘe–JcŊÁļKŲ›‰ˆĄ=z㙑ķīzĸ´å‚?ņž'% 6I'ĸā‚į~œhģ ē< 5>ėdËÚūå'R0¨Ÿø;wI‘„„øVÕō{ĸ§ĢæZĶnšå!ĶRûíVM% cØIFáôMÛķÄ"Ž7u9Ë´p)đ˛I.x4ĨÝ!0ƒ&T‰?Y’ĪžĐF‘ų(ĖA\Wƒ‚•†Qė|bJˆ'$*ŗ>(ƒM —GąbŗņZŠđáT˙™=,$¯f¨Á یƒ™qK*ĨĢĻJ„-˜^ åh&ŦK4%,3ĐČĀgâņRHä_ŽekNđį5ãX1‰ų˜cŽ öōwĒ=ü|…SøęœRĸųØ>âĀCÅĶ\žod÷cųBS;I"˛(\¨ĒãDáč‰üäs´ũģ…aņd7Ūxã,<čÅډõŋ44¯~đNi@ejWXˆôčŲĩ510ĘMëQ*,×hô¤á‘ę F5ŋl•š99°šņkīÄ/Ŋp§' ƒ”e´55 CãĒĨ°ņ@×ŨY‚ˇĨ#-ú&x¯ŊÃփČōéį#ėanŲr[UĪîĢu@lЎNđëA í¤Ø–¨vÕ CB1Ęûے!näĸé)--͛,ŦėWĻĸžļÍĒfŖū8ũ2KžÅôę­ßä]._ŗR2ߜf”˛ŧ{ tųWZZ$‚Ͳvõ&ŠvHŽ7i\Ŗtb6Wž˜^ģŨAƒ–ŗo+ũöRŗŠ‹-‡GĄNBĀÕĪ-Gˇ}‡™5w‘Aë3tČį@×k&L˜€ēŨ$aņD8a=*D?ęũ^˛t1T甔•ĐAētIURS-V˙c1čę•LKCÅ{ë=ËģųĒXCcåė$ŧtÕũTĶļ6¯*"B .ŧZRīy@ˆžt}yõÅ2Ä÷hJdR;Ãēž=—Û9ŧŖLī\”zŧyˇwŨU1^Zæ ē`hwål÷'īJŲ]WJÁ„Û,´ˇ1¨ēhīZĨäØHŊœĪãeGŠĮŒ’‘s~“–Ŧ’ÃۘĪžĩ3&ĘhuˇëÁŗU]]Ŋ*žžvŠJÔĩ˛đ|°´dÉ p'NBÎÜ~ûíÁvÕĻö=ö˜` ĩéXôÉ'ŸXŽu%ĨJøāĄJģ-ûŨ@SņĘÔž`¨š•1§  Fû‰‰}AĘ2uęԁ\nˇņÄÎüđnž‡”õŗSģÍS_ŠôÍ*š[÷.Ņvœē5Ë-‡čąŠ2Q_p})l,ģWēŌ.ŠŠ0Įw<ÔRŖN.H¨"둴8zv†0Û4!5&ôäŗĨĶ#öÅ[ĢĖBŸĪn˙Hå âAäõCėš>7ČHéąŖšŒuR„:ˆî¯fK؃7ˎ.”¯WđĪôŒš‰öEpãũØŠé eqR3ÅĻõ“#g}.ÉCD´zœôs‘úÚ;ø.Œƒp3¨Píd*ävVĨVŒ ÕĢi’PLŋtjKDh°K\Æd,Çļģ Ú3j^˙Íu0•–™"QĐ%”Īôøļ(ץ{Ų+OHų´gUUG¯˛]˙pŅžsâ™v{Õ6z !eŠ4AŽį°Ũ\ĒF)īA†6\FV"ˆØúg`€ĮUED„IīžŊ‡R(ĩL.ÖcP4ČnŽ_V ÍHũđ5Ķęo÷EzéŊÜÛĸWģ-[ʁCÕDûĪē•HŨ)VFCIP^DÚ¸œFœÃ•r°…@ŒO}æM‰ĨwĮDĖ‹||ë,šg °Õqmoõxīīqą<ŨįZÛâ­Ė›ÜŊŠEķĨÁĪ€×\ūEĀĀĘŗā%S:-m4:R¸Ŋ퉧ÂËī1ĸcĄ”Ŗôŧā21zJ<ä‰@8Œ“é…ęĢR׀ŠiŌž:Ī›?ÛB~øĄå0ڔŪ@*á‡wQí|ôŅGwĒëyķæŲ†3D >ä÷§]åĢáM.ēīzŠ„´]ņū›Rx÷•RŸžŌc>Đúųe‘ŠčQ§zWÚÂcxŪ'#Rv/!äˆĩ9āŗ?˜![?üļ(TØé„ØŦæ:Š/+5\$™•>ʸn5ôō6>tCĮm\áĀ#âÃāhŖĢŧ †ëR$įÁã–ķ%H0>OV=ûÔŊ´qŲ" 8ž 6*W‹X­P`bô´4ĄŒRІ´Ŋōwō„ņŲĸQ:ą;€÷A”ˆĢ:ũGŪėw—¤„9CíŽ×ļrĘ)Ū 1( ß ā>; Kkoū%6øŦ 8Ęāû’!/ŋ)=ÎģXúßpģüŌ4ô †oŖŨņæįՐŲfɖ “G[ˆA—YYY–CéũčŖZÍ–mÛ@t‡gŸE^įN|ßL š8qĸå\‰Éˆŋ4 LöÁŸae…”ŋ3īĒUl‚ĐQ6å)úG × īd(jO†ŗØˆAĘ‚qŸ¸Û¯uã^ÂŦd ÜÁžfˆĻ+īŊU*ÕJ‡WdĩUcÉ(ī ÄĮڈä핂ãđŧünĻzŗOŒ‘aé"y}1ŧ+—I)P €^š÷Õ %ĄđƋ¤b­Rj3î…ŋŠk;œP…]ˇ•]€Š>T‡Ėķ fĻÆ¸Ž\­¤3Îŗ0@ŲuoŨŒx§Ā —YĸNNM4^ĨõčīÃn™i"Æ “n¸AŪ~ûmĄBЃTûw>„ŊēA[—. ĒĨķí_.%K!‘bŌōŪ3™XßĢŽDjÎLā[='ҐhÆāĨã:ųSQQ™ädooũNŧ°€wáÎŪŦßđ2 ]°Ä—ąøĶÃ?üpģŨAmcáŽYŗfŲ2š :Bc2ŧūųĮrXÔČŠDņßÔĒQ/ Ņä ãI(aU}õ!„Ī7`9ąÍJkQ‡eŗĮŗ 9™Š………0í~Zž|šnī\DĻ˜gž78wUæ&é}ᅒ÷͡˛cžŅėuÖYgyņĩ ĐĮŧÁæōú fqķ9wvƒ/öƒx’‚čŦ ž7fûēėLІ=¨ šÕ?~!ĩșlDšŽF"R ŧ ŗ›,‹F)ŒUt•Ôß )#bØą ĻV‹ OĒBzĘCi̟K—)å>H+ĸ ëØĻgdĒB0]U—)[k[Btr€Ū{īŊōôĶOË=÷ÜŖŠtˆŗĒUÃē){EAŊX^3 ¨ā,¸¯O‹čz?Ũ%Ô{œ{(ܧîvČ ´?nį$NՄ°–F8_˜lœ››ŌT9jŲ;ąĪ UŸÍ˛…~Ö7Ŗģ?$ŋĀ]Ķķ /ēč"ÛhsĒ…ÜĮÄhV¸Á Ķęw™­×^{­*'vÔQÎØoG- XŊ‡*ĨN†Ė¤ãÍ€aĨͰhˇj@€q铨@Pģkqc?Õ͐Ã%B8ãŨU˙ôĨ]w܆W˛h0,žažy晞đÂu3UĸÜyLīÁR7lÁ‚_ ģYŲwčĐĄj[Sq-Š"l[Į T5ôÖ>+?îž"ƒÎ:\ō#ĀZYÕf†y4Ēn늅‡´ ‘ķDo$ šÅCÖ­’ü\UüU Xõ›Áď8RGwŠ…´‰Å+uD[ĢÄô:Õ¯ņîÜh“Ø+ŖīšB ē7HújO;=cp Č¤I“”ęgƊbzÃLJ'.&<ėmaømˆŅúŦ#š°|ĩ*BĄoRŊ%Ņėą¨•7Bđé÷iËŧw–OgLUvö6ÉŖÚ‘W€Ē/@čĀN[f…Ņ ûUÅoĒ_­/ķo8ęIĻN~[yĪę ē뉡ücíA;b˜™ÍØącåÄOTy”B™NCÆŦ8ĩĖ*ŌD˙¤Ŋīä“O–̝žZžxâ šä’KT GģžƒŲØa!—9Ĩˆ}t8įb‰=ũ`8Æī$ūéšgĨx'íxBēPw!ō`H›y 1û@¯Ãb4ŧ7ë7ë´g×rmC\I2 .ūĀ… ˛;E_â!‡Kîˇ_vŋđ Ēĸ.7Ö¯)’‚ą`hœ…÷čŨāĀ4ģO;QBŽL‚¸­_ŋîô ˛xņbUjœåÆÍé† r…6 AwČŅ ëŨO"đ›ãĸJ„]SdŨi€†õ윭$-ŋúĸæˇ$?p¸D_Ø_Ąhš—IæŪB ,=ŸS)…5ĀģĒ’úå¨ŦƒÂšUÁ¨ü eßk~ŲJ”Ũšh‘üX’ōÂģž€Í–B§ôÉ6ŒŊcõ¤â‰wâƒ×‰ž6„÷ßGR_zßĨdßm)l4˜v^>s;‚Ôúââ‚xĢ.Įōõ;ÕVxéFf%aRíÂ\)ēar-ėgqģßUۘ3sFIšä‘ūĖį!´-ąˆø1A-VˌL€jÖÔ3÷i^į –y(fōZ›p­ũ՝O—‡{^&apÉr QŊ´“„hŠØ?I:Ŋu< ЭΏŠĸAj~Ø"ÕßnQ!%ŧwÍVÂwá‡Qi×Áß(0­ģŗĻÃū{ũfĩœ|äp9ú{LH4hžųfÅŦŠŠJÁ<[¯Ëԁim‚UŸĪ’Ō3íi]M˙4`QÎPĄTˆĖJ8i31pūüų •ÂŧoOŽ3^ŒŒÔ÷ĒŖ$#Š>bÄ^™¤p×˰VQ*¸}Ŧ#S1ô&” §Sũž‹ 1YŠaån5tŖ­€ą7ī1•P썆YđvĀĖ=BÛæīrÆž¤ã&¸ÍĢŋÎÚŖę vŊđîeŌ\Ŧøn ŋÃhޏœMŠĐÎÃ|0ŠūW•6ã?^Ĩ˛PŊķVL„ŌL[¨ /Ūí§¨č2”š'žÖņ‡IÂĄŨ3RęEŨ4ÆëoDXBũú¤RĀ~)˛ö/ĀüLX,UŸlæ *jaxŠé餝@ޟ|舸ÁōSéU>^ sŖ{¤Iʰ!Ps={hŗ*/¯RFu‹P°y™÷ī*™•šX†Ö4æÔŗ%ūâk[ÕH á÷EØ $3ĩfę—Ū7Ļēđ[$éž&Jo¯ŊöšR-9 Z.ņîG%jč1­÷giä°Ác(TåÖ_(ąļdäáÃũĢ…x7”ŒkPcĀéŨ`\đ îyBTt¨K?a€ äjŽ;î8ųõWĖē ÆüjŲ1ú§=nt×_7Ĩ W|„„vĒÕĸŖ„@ą>qÖ%FBbÃõÍ-Ë´9Pu\ģv­Ŧ[ˇNyxX´9U°ĪS`,šá¨#†É?|-)1QuŖ”ŋ´ŌcĖ7õC¸ž¤M1õIâŗ“ČL‡ŧJæü˛r1ŠÉsLOŅ@V9į0Žxá#{FFļ˛Wð(-yԙ;ôŨ{—ÃėŖTAe(ÖŠJĒĸ5` oŖ$ ī- ’%[Ÿ>}ĖģvÛ:Zéá7cKę ×ß#ūwYđˊl Ģ ŒžuƒĄ˜ãN•ć&ąÃ¤Ķ›;Á9”„uũųŌ/šQ…Ūã/ ĒR=’˜˙B4ëĨĐī=.&ģ+nŲFĐÆíURˇyn´ė-Ä­ŠIdH´ŗ`°3ü]!õ˙bļß&Õ߀‰á¯ö—!R*cĘhčVĮF"Á¸EĨ¤$Á<˛´´4áŒNÆuųå—+ÆÍ” zîø<¨šyą2 UË@ˆ¸ôR‘X(ĸFsĮgŲÂTé7Ø6 \%0âįÅŋI‘ųtTą!]šģ÷“܈x)+.QęƒnˇßE&n×/˙9§¯[ ĀąČeK‚¯¤…Yi=ƒ‰…§ PqquKūĐôĮĮ? ˇIDATļzŠŽ}ūųį˛`Á5Ųđ=ZĖÛI ”ĒhN°ƒŒņv…ī0ūō›%ūŌë<ĖƟņÛ{ gRiÅŦixnSM{[e(NĖÉgÁĻé‡ÕĀķØĪwÕī;27ØZ_Á´ž÷âz^@îĶ AåëĒ8 9ŖŅîCŠ>ž‡Tŧģ^1MJņuüŽØĮ˜"O âāyczŒÁFĖ“r°S:Ņŋ<4bhFÜ˙uËā‰ÂŊ)‰+*eįhJ9 EĨ„ziV)é'P˙øņ’(qŅKšiĶ&‡K#?UK–›b>œž(Í1LD#„¤ J;vg)e1Ėa|KåV”6#Ķ͆ĮN/Ŋ'.ŸÕŠlú6ž–+Õ`02q"ĒÆž~žg˜%+Sgtˇ+•ĒļZĘgLą0>6g ˙8Š0ū‰K{ÛˇŽĀb,¸â‹B;u4ņíôZ%YĩYU!w´įļR#bĮÆ~ÛNÄb"MĖppˆē§} ßęŽ]8_:]šīíŧÛĸí "B6w&€ í:€Zņ¸…Rũ]6f@?œÜšË6īaŊĀbwšŦ­Ūĸ˜ƒ3YYZĢÚLæ€01Aį—f¸ļ% ˙ˆ…Fį‚ߕˇtØÅšĪ‹/ž(ņ#Ņ&”ų/XĀāŪwE”O"ܰųŲTģ]]K2rđz!@sr ‡ÄöWé-i( F& )Hc”ŋ(Q0€3p&†îÁŧTq220hWĮH0­X ķRŒ Ė,ŦWqĨD;ĢvęJ[˙#ZkŅ­ŋĄ tyĀĮ´ŨžKœø,O];NĘĢ ģP…:uĘ {’ŖúĻ?‚ŒŗwŅŊ×)Tũ.Ē))Īž-‘œm‡ĪL°ÅhYŋąŠØŲv¤?köîŨ[:č õGž9ąœtéAfx Í”iã¤ÂÉ…á Œ§ Äá9äHÅĐUunÚĢ4 蝓ĖĒĘ% BvĨ!J°Šaõčãķ}ņœD|āD`G0‰”ã9íˇ‡ļŨeØoƒĘ‚ ą{PŽûI-ČÎŽ%_>“Q™A*{~…TLOWž,ģöíŊ Ÿ-NŖœšnŧ,7ŠÍįęŒ¯đ$0ąžÂ$iūō¯°¨B;@2ƒŅQ11ë[TJsļëā[ĩŊŌ–Ŋ*%<’áT#5iŦ¤1¨™f/e͏9xvËÅMĮEÄŪa$ž×”ŧĪåĄė–ÛŽ=ũ<éxį˙ž'ə›ŠN%OŪo釆vÆ$ĩePŗ3F“­œũ–b^mŦa?Tˆ#iô^et×_(uV”īēëîģīŽ‡ĄxŦ~Ÿ~ųÕW_UeĀ˙ķŸ˙(IŋoW.3ĨdTAåĩ•QœU—§ūˆËNbŲ.Ē”€¯ÕPFŪŨũ=P1ŌíbĖd`Ėõķ÷m>PĒ-ˆ›ŨfęôRÖū&ÅÃč đ!ÛîEDE9HŽwv;_ŽŪ4ŲpeŦ6Mu,ūĒÛ YšB´–Ta˜/*†™Yą ëãEŧŽ-öíęR‹7„÷Šīe”¯BÚ‹ō MÛ{%åÕ:\xĨ É ´ŋ3÷‚Ąš_ž“’ÉųŦæíëė WĮ$QT}0QĻKÕŖĘ”ŗâ9!aũž`EJđuA{bJEÃ};FF„ËÚ3äHš†™‚7–īr•6–|4ŊáehˇŋĒöŨĘbô õHā1i˛oLoÅĐē@B‹†¤G•’ƒ\IcddŠĩī5ėîŪ4•û´ôûQzŗáôaŨzHęk¨˛ÆK‘ÚHTËQt×ΚÚER_žéņ\ĩÁÆc<“i Ō–JaœtíīŋJÍš(q–;WŅN¯ylÆ5E"zäÉĒLīĶ)YØteöĢœØ¨6ƒ÷‹îĶŗ;A4¤ŋ¤‰/âaOs JģÅ€ŲŽų ؆`ŋr#…ībéí]ĶĒÍÅrŒ“āᚯVW‡&21:ouž]ö í!5P%vQM+r—É•ĪČŠ5ģę4õ‡â|Ũ#RtÚ }!•õĒ–Ũ R&ĀfÆ"¯ĸ”qŸ, ÛF č\{ĒÕfæŪ•eâ@ˆq‚eĀĒ]0‹’ Āā5Sâ}O( ‹‘ÕģŒ`čį@¤c€ vĖÁcjOũú5 ‹‹*UGJEž(pĪ›ĄzIBõĖ…äīĐŽ= Ŋ R˜įaŨ{Ãë‡-ņ ´™xB|H.~ī×ˆķU"Xŗlę3mļåų:ŠŽ}šŗW—á x×üŠŊķč+ĮŽ{Ø´iĶū ‹Lkđ…ž€'%ŲéõÄāŌī6w€*ÎI#v{ĪäßŅ#hŽÆnĪķėL_dRT ŲLã>%ą}ŒŲ5" %É<ĄėŸ2YPĄ;sam<–*r.ŠkŒZs‡BNÕw=ō$Izø9ÃėM&Ņpž"€#6BĘ1Ũũ 7Ūģsv+s§ūÖq! †­ÁЉQâPĖ žA*§gXQ æŒņ ĄhüR*ŽĒ¸3LJģVšqee¯MVÉŨÚæöü%Ķíôęl õŌĀûĢ…ŨŒ^\'‚õ)xOīå~…ÊųŪąŽŨߑ8| jĢ}ˆ˜ĸxzVĖÉÁ9õrņ†IōP.M=QŲzę ”mģ„ōīû’ŋäŪ-oČVœgo%ŪīļúBõ7ŋ|…ēLz2+Ē“,GÆhrJbiXī‘Ü/ä dˇ7“ßŲįD†šu8%<Á°ę–ū Īæ6Ĩ&ŅĶGfEģc‡ė˜UäaGIüÕˇ;ÛŊvöbŽ§d‹T-.ĨxöBb 3ĐŪĻ‘ÆŒZ~wJŨĶú4ũR‚Ŗ4S {UíŸķM{Ûo5æ„Ķ‘?Ûß"΀{¯™÷ƒa“~ę`RĐža¸é_ðxąˆŪū9‡cD÷!V!)ÂËeŌ+ĢU2U~Âæ]ŨG+Ŗ5 Ķ ČÁĘ[´U…ƒQmŦŨ&/å~&ūnjÜŧ–1ÖĢ67ūũ]šÎ{éņ0j§!ȕԨđ¸ũäÜ䑊1īmL‹Œ]3c„ęW.•˜SúûŽ{k–”L' ›7˜›JTĢÄ;&@ršÆJF{šLŒiˇ\íU”hūøņcĪYĐ'Úķ\qņąĢNũ*ī*IkĀŗī@˛ÍîÔŠĶr-OŌg`ĻC{t30ĩ×!  ˇędæ×1_Ë.n%LæãĸyǞp§ˆDU’=*Ë^) î‚ŋ$ūr+÷E Ü#­áû‘ôę,ya ÷AĒZŒĖ@ĻĄÃhxpö;°,]$î]ci öe€T´›ūĢkn %’ŽŠŅ?ÚĨë'$ĸ<ģéüž†īĻ×õ6ėXæ RĖ@RÎäĩđĘTÎĢŨ…0ˆÄ‡ŸWļ 6ų×īū;¨Ō[B+?|[JŸŸØæ"Ū-3ĸ= ąFÎödÚ+üZˇ|ącˇ¨ đŒík t˛¨ļiī˙…û4\÷&DÃ?Ļ&ĒŌLėÜĒŧĒsÆô“áņûÉQ$XŽ‹vžHHPÜG[c~ĘŨÕ*Āá ķʖËŌĘ ÂA,…C îxĪcĒ"‰Š=Q*$<ĀÆĐĀØĩ…𲠨õGiĀŊ-ۂė9ÛŖ}Øšæí÷ŧĒ´ˇ˜įŠÎî€dxÜęÛĨÄmŅaB2UŊĒĪߡ÷Â!Š–P1§žĨāKÚã9ũÛú  ČÂL%Ē_ĩl—_>ßIĘ ī(•×)öJ!ŋBē"dOǤÛÆv3ÆųņШæjû˙U*ĄvҌĮŊ‚ŧŦâĸĸĸ)ˆ†O`¤0ÕCģČx2$ÖņīåÜĪU)÷Nˆ Ä2\ē@¨DŠ7°žĄRî …÷ˆčé)°ĢtŗÄŗ0ę7*XX—nˆ:T†6 zŒÜ[6+÷wÃæ@đ\-n:2ŪhwRaC™‚wI„‡ą Īlo &•įח8ž—†L3ā€Ĩl.ĄRbƒ: °I‹Čt˛ yÄ|5á‡"åģ30Æė†fŧWJ¨÷ļŒĒ‰4Ĩú2 “‡hh˜ž7CŋVE€""M­ļÆĩÜ ˆ×ĘΆŊn[ŨģnÁØĪ[ÚúĀöXϝųlílĻ7ŌũSį~ÁYzŲE˙ĨHulûÅØ0ę+įâúhĤąŊĒ;JAúÂķŨ˜%אu<ˆĻ°—Q;m|åEwđ;ÃëÎ{JûļZ‘˙Ø*ž‰xûŦe×ē ’+ȝ'œĄ7ⅆĘöų(Ej_hčIō]Î-÷’áhŖkx?B˛Œ÷ĩČĨ™Yą¯ęC!›lD°â͊×Üz#Ú~'ÅŠŊJ˜sÃļN6ęöA+!,hg*ø™ŗ'ÕSđ´øļDÁŌŒË‹ÖRŌ\ÆyšŒE—SgjßÍN°ô×Dķ:Z[ €íņ› ā\WƚVqĸQäh+{ dĄ6›4ŠČ%Âɞ€“ާL ]•ß*°Žž ŸÃOa/í˜×1pL`‰ÃPŸŸšk׎Ŋm—ū–ŅqkŒ˙'°'ĪõäÛ;g8Uˇbãëϝ“€+õsķLK<~Ņø$ßT‡‘] €!PkP›såˇßÔéÖk'-vWį^`~jvÆqƒˆŗ“û垍aī"ų#)…dm ž fļŽ1!WŲüôNø˛÷|\ŋŦšŌ‚¤EÉö(¯æwŪ°Đ#y‚+ė¨Ģ2ęŦs->RĄG=˛Ûé~˜ĩÚ ~ ÚŦÛäOM|v0G4oÃU¯ģž˜Īn1ČŊŋƝáÕūr ]-ƒjŲÕîžo›4iŌŲøI=„V•×÷Ģ +$å<:í$Ā‹ŗ~ōļl0úÚ ĀôÅÔ?Ū…†ęŖ1Öüt‰;´#t6NĄLßāžJ¸ŽČûŖˆã9 íß˙ī-ī™uÕJŖîdˇRį tH*ŨŪá‚@`ՌĻRsĮMæ&"mB<čĘMč;îxd 2ž;TB€!â]kFˇØæ÷ŪvMo•ē#;ˇ›fM”/Q‘ЧĖķĪN4ˆņ1‚Æ;8ü4œ÷÷ÜÖ9Tˇ–+4įË*++īT•¸šÕ<ØžÁ¨._)¯„‘€üŽRŠ/|ˆ’é8É )e7O–4/˙02NC´Š:É'?[æZ:“‹–æ‘éŋ0gŌxƒ–ĀJ =ĸĄĮpEņƒÉÅ:ˆ´uT&äfüS y¤9$LvI¤ŧ ÎūĸĄl*Û^ šÍčī-gļ­(Ũ@BNŽô@Tļ(ŠŒßĸÁi~‡aCŊ#ä]ė CŠeG/ũ‘ËøÆ…Öfĸą-EĒn÷#k}Œ[šybƒ’ÎF(š ąa+Ģģŗ[_"n€EJŖ‰°+ŧ€ĶįÔāzëŅI@Ô˛fČˆĄ…Ĩ …ųđˆ—}ÁŒ¯>š&†&ƒ/Ē†ÖôĐŨŽbÃ=*(ĻĶûĻ^ëæežhQˆÁ…HÉiõĘ]ˇG¤īĩqÎÍëmT gEЧl+Ō´ŦÃėũĐ´ĻÖÇŦfŌ\å'”NJ¯öŽŒfSuŨR×ôæŋCj/ĮGMĶ÷°LÃC[fˆ§™YÕMIĶ×Čl],]Q7‡TĻ˙¤–œfCĔŗüšhl[Ŋ¸OČ)zV>z­$īUĒŽēŋgb üGį ŦČĢųž—ËYj8ĀÂNåÃÚ˙fáũđJ,$ 24ą>$Mœėwe™„í&qܲādļibhą€ŊĄ'@FãĄ{šēĩ?qĩ˙ÚŌŠĘâĶēmōr÷íãNˇY;…ņÄĸ(ôFŋG+ˇ¸?z°ËįāûH[Ęšõ^›…•v%?ŗŠĢ/Å [|X§õ”“æēqwn4ųh§ ÚâĶĒūõĪM“æŖ!ÕXĨõąčņp\6`ƒÂLÉC͙W,Ŗ ī¤IŠŽÚīĸļ" TtÔF,9)í"ÖÅ´E†ÖrRnøį?Ü᧡ hī#Ā"üæO$žŧcNw׈ `a<;ūšŋÉãÜ+ņ“€†‹Ę¨+ƒĢ<āå8*[˜†—â#Ōt˛UMcX ČĐ|°IT¯\îęąīt,tIwŲøsÜĩ',‚_~ŧš<ČWĒ7E, Š )îMûŒklcųĶ/#[ŸRʋ*Ø4%ę{ôĀ~÷ɒķÃԘčÂŖÎ;éūÎÛv'u8ũ)c‹ Ě “ģ€@ŋëBwiķe3QųCŅéįu”Ŧڞ—eFr†E–ɘ}U|ŖŗˆĨ\Äībŗ™j6҆Å}č—6üFnōģ:˛ë]×đ“ū„„ŒõĻ´Č7¸-k}4įĮ°ĐŽHĀ1ę)’F,ŒĻŪ11–€4%l_šņ%â†V–8!Θ9厤a“ČhÁ4ŪV5`42ŖīĀ^WŊúWŋ{g—•KéŌņ_wAø7‘„ąœe‰‘ĀK •Ĩ€īŖĻrã+{ā“ŋ“Đ#ōđ!eæ—Ŋú áCŗŠšs;ŧyS§ēJ͐?\ ΍™ß]ÂÔüxžˇ™fäˇĩĶ ˆ&›EĩąÜ,¤šâ9ŽĖ.˛?ĐĄYˆeCę+iƒąøÆfģ´SOwi§/ ­Ų\Ķ ícĻ÷m?Ŋ{­ęMŠÖ‘9l„z~Ų§šJˇēÆ­[,›vofgw,qgØqÉ%—”lذ!"É~ĖköėŲŲ;(„Æä*ä-dØr4„L*˜Ä0 åXÎ8š(VQļųŪøČf\ûĢÜÁW5Ëܐ?{–ãĄ-^UâÎ˞OŒŠ–VN§•În‚~šĸĨÆm‡ņiČŸ¨ŪęDgMI›&žVëŦū6ŧ >I= Ām$ú_Fl<û“ĻL3cģžĮÎ +g? å,uJžĶÂzĨŖĨō¤EÃF€šq–‘HB ¤a]ģ-Ėî8tūČÁVf…œ›!-Lō¸ˆĩ=Š´w›ŠũQU…yŠ7Ŋž zĶėqą|bf Á{õEŧ B Š]ܤcÛęâo*āŒíüŧ™Á‹n`N0‚_\ųãԀ•ôRnɤĐj|~ŗk<°/bÅr°q)“õTr)*%™č—Ĩî×Ã3Ļŋ “~ūc2Œx­Ā˛e~gą}Ųr?ĩqwdîcŗŠŠ´ĶY0ĩ”ļs3‚×2ׁãÅã$Ī^˙pn$1=ōæfĄ¯‹Š†Æ|k iwr÷“ŋ™™™Wcķž;ŌÅ<Š$Ąŋ_M$ЊûO˛W‰|/˜ŠÃ#õ€P@Ÿ8YŒĐ ZÛO^⚱c…fål('M‰˙5´k´†ĒļÔo#ūw"?Ãíøj^éNĀhlK~~~Ē]#CBOÊ­hčjũ VâÂO?û›Æ*{ۑ;Ú=¸Í“đ2[”:­ĸ›6@bøVũЍ&a&˛5I‹ęŗ{ÂI}¤Ũ°ĘŠæ™cŽaÉĨō­įņŋúr4đŽņ$,ŲQŌĶ)iU€Ĩ†oˇFS ļAŸä­y `Į睂e‘D–Ƙ9€‘3đš˜^Ô쨈‘€´(sĨa€Ģm¸7b„0Ŧ˛h;æ€Ĩ›â‡õŧ6ŸąÂESīOžF¤4Ki_TJN\ zˆŨTāá)ũaüĐ RÕĪ+žb)ü°6ßu×]Ĩąŧf¯5sæĖB,˙š—ÕÖûy2đڀ×:ĩĀĒlƌSz 0ņ8g° āšÁ‘Ã,O^đÚ@h`rî)SĻˉöôęš ĶÉÍÍŊ#|•÷˛B_–'O#ĩ 01× X=9gÎ9™ž2mÚ´“ąimBÛû|'ĩĐÛæÉÄk#Ŗ €;¯^´hQĘāCĒ IÛBũ›‹ ūoąm•aŧõ€Ëo¯ Œ6 ūŽVõ Žå „AĐ0øWKJJ˛°o-ŧ~x•‚ēĩRŠš÷ķdāĩaÔ¤QŅĪ7ŅßĪYŗfMŸ#kéä_TT4ąĻĻæ$ŧãOå—p‘ŗÉÉU_á=8FééÉØĀ›”÷ŗW< x”PĀ씀j'3€Oæää<\VVöz›BŌį:8`…{Ā)aÖŦYdÜÉ!=X.˙Ohii™Š !ëJcŸOĖĸrqë9”C+ÁŒaŸ_¸K{û< xčƒL”F–úU1äÛHŊΈé-øđļ‘Īáí×^{­ûŒŠŊŧ÷ ŦpĪ YŨÉc–w|”ĖŖ@t(6].”Ķ!œ„ ´"ŽÍܲų?u[&KЧ>ļyZ[8A{ûF”ÔT :úH9 $*ŅŨŦWŗ­Š˙=( ĻZúY%ũŽž°°°Ļ´´´sãŽ% YĀ2ŠFņAÛ3§ ŠĨ2äLG°Ų,SŲ7ÁgjŦŸĀ2‡}Jō ЎËä2t>/JËLļbâęÍņÉl‹ĸ&Ū!CYŧg}ØZx†î^6‡$$ T[ ļr˙š9í–ĖĪ  sŒ}ĸu=ØĀV¨Ü‰bTŠgyˆsØT 5đÛËĩʈí;——WŅ_@¤:ES†=`E#„ŽĮđM.ŧdådJ Q—ÁË]WWg/ 0 { if v, ok := a[n-1].(error); ok { err = v } } return &QueryError{ Err: err, Message: fmt.Sprintf(format, a...), } } func (err *QueryError) Error() string { if err == nil { return "" } str := fmt.Sprintf("graphql: %s", err.Message) for _, loc := range err.Locations { str += fmt.Sprintf(" (line %d, column %d)", loc.Line, loc.Column) } return str } func (err *QueryError) Unwrap() error { if err == nil { return nil } return err.Err } var _ error = &QueryError{} graphql-go-1.6.0/errors/errors_test.go000066400000000000000000000020331475633407000200040ustar00rootroot00000000000000package errors import ( "io" "testing" ) // Is is simplified facsimile of the go 1.13 errors.Is to ensure QueryError is compatible func Is(err, target error) bool { for err != nil { if target == err { return true } switch e := err.(type) { case interface{ Unwrap() error }: err = e.Unwrap() default: break } } return false } func TestErrorf(t *testing.T) { cause := io.EOF t.Run("wrap error", func(t *testing.T) { err := Errorf("boom: %v", cause) if !Is(err, cause) { t.Fatalf("expected errors.Is to return true") } }) t.Run("handles nil", func(t *testing.T) { var err *QueryError if Is(err, cause) { t.Fatalf("expected errors.Is to return false") } }) t.Run("handle no arguments", func(t *testing.T) { err := Errorf("boom") if Is(err, cause) { t.Fatalf("expected errors.Is to return false") } }) t.Run("handle non-error argument arguments", func(t *testing.T) { err := Errorf("boom: %v", "shaka") if Is(err, cause) { t.Fatalf("expected errors.Is to return false") } }) } graphql-go-1.6.0/errors/panic_handler.go000066400000000000000000000010651475633407000202240ustar00rootroot00000000000000package errors import ( "context" ) // PanicHandler is the interface used to create custom panic errors that occur during query execution. type PanicHandler interface { MakePanicError(ctx context.Context, value interface{}) *QueryError } // DefaultPanicHandler is the default [PanicHandler]. type DefaultPanicHandler struct{} // MakePanicError creates a new QueryError from a panic that occurred during execution. func (h *DefaultPanicHandler) MakePanicError(ctx context.Context, value interface{}) *QueryError { return Errorf("panic occurred: %v", value) } graphql-go-1.6.0/errors/panic_handler_test.go000066400000000000000000000011461475633407000212630ustar00rootroot00000000000000package errors import ( "context" "testing" ) func TestDefaultPanicHandler(t *testing.T) { handler := &DefaultPanicHandler{} qErr := handler.MakePanicError(context.Background(), "foo") if qErr == nil { t.Fatal("Panic error must not be nil") } const ( expectedMessage = "panic occurred: foo" expectedError = "graphql: " + expectedMessage ) if qErr.Error() != expectedError { t.Errorf("Unexpected panic error message: %q != %q", qErr.Error(), expectedError) } if qErr.Message != expectedMessage { t.Errorf("Unexpected panic QueryError.Message: %q != %q", qErr.Message, expectedMessage) } } graphql-go-1.6.0/example/000077500000000000000000000000001475633407000152235ustar00rootroot00000000000000graphql-go-1.6.0/example/caching/000077500000000000000000000000001475633407000166175ustar00rootroot00000000000000graphql-go-1.6.0/example/caching/cache/000077500000000000000000000000001475633407000176625ustar00rootroot00000000000000graphql-go-1.6.0/example/caching/cache/hint.go000066400000000000000000000036451475633407000211630ustar00rootroot00000000000000// Package cache implements caching of GraphQL requests by allowing resolvers to provide hints about their cacheability, // which can be used by the transport handlers (e.g. HTTP) to provide caching indicators in the response. package cache import ( "context" "fmt" "time" ) type ctxKey string const ( hintsKey ctxKey = "hints" ) type scope int // Cache control scopes. const ( ScopePublic scope = iota ScopePrivate ) const ( hintsBuffer = 20 ) // Hint defines a hint as to how long something should be cached for. type Hint struct { MaxAge *time.Duration Scope scope } // String resolves the HTTP Cache-Control value of the Hint. func (h Hint) String() string { var s string switch h.Scope { case ScopePublic: s = "public" case ScopePrivate: s = "private" } return fmt.Sprintf("%s, max-age=%d", s, int(h.MaxAge.Seconds())) } // TTL defines the cache duration. func TTL(d time.Duration) *time.Duration { return &d } // AddHint applies a caching hint to the request context. func AddHint(ctx context.Context, hint Hint) { c := hints(ctx) if c == nil { return } c <- hint } // Hintable extends the context with the ability to add cache hints. func Hintable(ctx context.Context) (hintCtx context.Context, hint <-chan Hint, done func()) { hints := make(chan Hint, hintsBuffer) h := make(chan Hint) go func() { h <- resolve(hints) }() done = func() { close(hints) } return context.WithValue(ctx, hintsKey, hints), h, done } func hints(ctx context.Context) chan Hint { h, ok := ctx.Value(hintsKey).(chan Hint) if !ok { return nil } return h } func resolve(hints <-chan Hint) Hint { var minAge *time.Duration s := ScopePublic for h := range hints { if h.Scope == ScopePrivate { s = h.Scope } if h.MaxAge != nil && (minAge == nil || *h.MaxAge < *minAge) { minAge = h.MaxAge } } if minAge == nil { var noCache time.Duration minAge = &noCache } return Hint{MaxAge: minAge, Scope: s} } graphql-go-1.6.0/example/caching/caching.go000066400000000000000000000014271475633407000205460ustar00rootroot00000000000000package caching import ( "context" "time" "github.com/graph-gophers/graphql-go/example/caching/cache" ) const Schema = ` schema { query: Query } type Query { hello(name: String!): String! me: UserProfile! } type UserProfile { name: String! } ` type Resolver struct{} func (r Resolver) Hello(ctx context.Context, args struct{ Name string }) string { cache.AddHint(ctx, cache.Hint{MaxAge: cache.TTL(1 * time.Hour), Scope: cache.ScopePublic}) return "Hello " + args.Name + "!" } func (r Resolver) Me(ctx context.Context) *UserProfile { cache.AddHint(ctx, cache.Hint{MaxAge: cache.TTL(1 * time.Minute), Scope: cache.ScopePrivate}) return &UserProfile{name: "World"} } type UserProfile struct { name string } func (p *UserProfile) Name() string { return p.name } graphql-go-1.6.0/example/caching/server/000077500000000000000000000000001475633407000201255ustar00rootroot00000000000000graphql-go-1.6.0/example/caching/server/server.go000066400000000000000000000077531475633407000217760ustar00rootroot00000000000000package main import ( "encoding/json" "fmt" "log" "net/http" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/caching" "github.com/graph-gophers/graphql-go/example/caching/cache" ) var schema *graphql.Schema func init() { schema = graphql.MustParseSchema(caching.Schema, &caching.Resolver{}) } func main() { http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write(page) })) http.Handle("/query", &Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":8080", nil)) } type Handler struct { Schema *graphql.Schema } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { p, ok := h.parseRequest(w, r) if !ok { return } var response *graphql.Response var hint *cache.Hint if cacheable(r) { ctx, hints, done := cache.Hintable(r.Context()) response = h.Schema.Exec(ctx, p.Query, p.OperationName, p.Variables) done() v := <-hints hint = &v } else { response = h.Schema.Exec(r.Context(), p.Query, p.OperationName, p.Variables) } responseJSON, err := json.Marshal(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if hint != nil { w.Header().Set("Cache-Control", hint.String()) } w.Header().Set("Content-Type", "application/json") w.Write(responseJSON) } func (h *Handler) parseRequest(w http.ResponseWriter, r *http.Request) (params, bool) { var p params switch r.Method { case http.MethodGet: q := r.URL.Query() if p.Query = q.Get("query"); p.Query == "" { http.Error(w, "A non-empty 'query' parameter is required", http.StatusBadRequest) return params{}, false } p.OperationName = q.Get("operationName") if vars := q.Get("variables"); vars != "" { if err := json.Unmarshal([]byte(vars), &p.Variables); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return params{}, false } } return p, true case http.MethodPost: if err := json.NewDecoder(r.Body).Decode(&p); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return params{}, false } return p, true default: http.Error(w, fmt.Sprintf("unsupported HTTP method: %s", r.Method), http.StatusMethodNotAllowed) return params{}, false } } func cacheable(r *http.Request) bool { return r.Method == http.MethodGet } type params struct { Query string `json:"query"` OperationName string `json:"operationName"` Variables map[string]interface{} `json:"variables"` } var page = []byte(`
Loading...
`) graphql-go-1.6.0/example/enum/000077500000000000000000000000001475633407000161675ustar00rootroot00000000000000graphql-go-1.6.0/example/enum/index.html000066400000000000000000000023021475633407000201610ustar00rootroot00000000000000 GraphiQL
Loading...
graphql-go-1.6.0/example/enum/main.go000066400000000000000000000022411475633407000174410ustar00rootroot00000000000000// Package main demonstrates a simple web app that uses type-safe enums in a GraphQL resolver. package main import ( "context" _ "embed" "log" "net/http" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/relay" ) //go:embed index.html var page []byte //go:embed schema.graphql var sdl string type resolver struct { state State } func (r *resolver) Query() *queryResolver { return &queryResolver{state: &r.state} } func (r *resolver) Mutation() *mutationResolver { return &mutationResolver{state: &r.state} } type queryResolver struct { state *State } func (q *queryResolver) State(ctx context.Context) State { return *q.state } type mutationResolver struct { state *State } func (m *mutationResolver) State(ctx context.Context, args *struct{ State State }) State { *m.state = args.State return *m.state } func main() { opts := []graphql.SchemaOpt{graphql.UseStringDescriptions()} schema := graphql.MustParseSchema(sdl, &resolver{}, opts...) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write(page) }) http.Handle("/query", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":8080", nil)) } graphql-go-1.6.0/example/enum/schema.graphql000066400000000000000000000010241475633407000210040ustar00rootroot00000000000000type Query { """ Returns the current state. """ state: State! } type Mutation { """ Changes the state and returns the new state. """ state(state: State!): State! } """ State holds the possible task states. """ enum State { "Backlog indicates that a task is in the backlog and needs to be triaged." BACKLOG "Todo means that a task is ready to be worked on." TODO "INPROG means that a task is currently in progress." INPROG "Done means that a task is finished." DONE }graphql-go-1.6.0/example/enum/state.go000066400000000000000000000025551475633407000176450ustar00rootroot00000000000000package main import ( "fmt" ) // State represents a type-safe enum in Go which corresponds to the State enum in the GraphQL schema. type State int const ( Backlog State = iota // default value TODO InProg Done ) // the items in this array must have the exact same order as the corresponding constants above var states = [...]string{"BACKLOG", "TODO", "INPROG", "DONE"} func (s State) String() string { return states[s] } func (s *State) Deserialize(str string) { var found bool for i, st := range states { if st == str { found = true (*s) = State(i) } } if !found { panic("invalid value for enum State: " + str) } } // ImplementsGraphQLType instructs the GraphQL server that the State enum can be represented by the State type in Golang. // If this method is missing we would get a runtime error that the State type can't be assigned a string. However, since // this type implements the State enum, the server will try to call its [State.UnmarshalGraphQL] method to set a value. func (State) ImplementsGraphQLType(name string) bool { return name == "State" } // UnmarshalGraphQL tries to unmarshal a type from a given GraphQL value. func (s *State) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: s.Deserialize(input) default: err = fmt.Errorf("wrong type for State: %T", input) } return err } graphql-go-1.6.0/example/federation/000077500000000000000000000000001475633407000173435ustar00rootroot00000000000000graphql-go-1.6.0/example/federation/compatibility/000077500000000000000000000000001475633407000222145ustar00rootroot00000000000000graphql-go-1.6.0/example/federation/compatibility/README.md000066400000000000000000000104631475633407000234770ustar00rootroot00000000000000# Apollo Federation Subgraph Compatibility ## Overview This application was created to demonstrate that the library is fully compatible with [the Apollo Federation Subgraph spec](https://www.apollographql.com/docs/federation/subgraph-spec/). ## Compatibility Results
Federation 1 SupportFederation 2 Support
_serviceđŸŸĸ
@key (single)đŸŸĸ
@key (multi)đŸŸĸ
@key (composite)đŸŸĸ
repeatable @keyđŸŸĸ
@requiresđŸŸĸ
@providesđŸŸĸ
federated tracing🔲
@linkđŸŸĸ
@shareableđŸŸĸ
@tagđŸŸĸ
@overrideđŸŸĸ
@inaccessibleđŸŸĸ
@composeDirectiveđŸŸĸ
@interfaceObjectđŸŸĸ
>*This app intentionally does not demonstrate the use of Apollo Tracing since this is not part of the GraphQL spec. However, you can implement it yourself. ## Test it yourself The application also has the graphiql interface available at `/graphiql` and you can play with the server. Particularly interesting queries are those using the `_entities` resolver and providing different key representations of type `_Any`. Below is a sample query you can play with. In order to run it: 1. Run `go run .` 2. Navigate to http://localhost:4001/graphiql 3. Copy the query below into the GraphiQL UI: ```graphql query ($representations: [_Any!]!) { _entities(representations: $representations) { __typename ...on DeprecatedProduct { sku package reason } ...on Product { id sku createdBy { email name } } ...on ProductResearch { study { caseNumber description } } ...on User { email name } } } ``` 4. Paste this into the variables section: ```json { "representations": [ { "__typename": "DeprecatedProduct", "sku": "apollo-federation-v1", "package": "@apollo/federation-v1" }, { "__typename": "ProductResearch", "study": { "caseNumber": "1234" } }, { "__typename": "User", "email": "support@apollographql.com" }, { "__typename": "Product", "id": "apollo-federation" }, { "__typename": "Product", "sku": "federation", "package": "@apollo/federation" }, { "__typename": "Product", "sku": "studio", "variation": { "id": "platform" } } ] } ``` 5. After executing the query you should see the following result: ```josn { "data": { "_entities": [ { "__typename": "DeprecatedProduct", "package": "@apollo/federation-v1", "reason": "Migrate to Federation V2" }, { "__typename": "ProductResearch", "study": { "caseNumber": "1234", "description": "Federation Study" } }, { "__typename": "User", "email": "support@apollographql.com", "name": "Jane Smith" }, { "__typename": "Product", "id": "apollo-federation", "sku": "federation" }, { "__typename": "Product", "id": "apollo-federation", "sku": "federation" }, { "__typename": "Product", "id": "apollo-studio", "sku": "studio" } ] } } ``` 6. In case you want to run the compatiblity tests yourself: ``` npx @apollo/federation-subgraph-compatibility@1.2.1 pm2 --endpoint http://localhost:4001 ``` graphql-go-1.6.0/example/federation/compatibility/any.go000066400000000000000000000050661475633407000233410ustar00rootroot00000000000000package main import ( "encoding/json" "fmt" "github.com/graph-gophers/graphql-go" ) type key interface { isKey() } type ProdKey struct { ID *graphql.ID `json:"id"` SKU *string `json:"sku"` Package *string `json:"package"` Variation *struct { ID graphql.ID `json:"id"` } `json:"variation"` } type DepProdKey struct { SKU string `json:"sku"` Package string `json:"package"` } type ProdResKey struct { Study struct { ID graphql.ID `json:"caseNumber"` } `json:"study"` } type UserKey struct { Email graphql.ID `json:"email"` } type InvKey struct { ID graphql.ID `json:"id"` } func (ProdKey) isKey() {} func (DepProdKey) isKey() {} func (ProdResKey) isKey() {} func (UserKey) isKey() {} func (InvKey) isKey() {} type Any struct { TypeName string `json:"__typename"` Key key } func (a *Any) UnmarshalJSON(d []byte) error { var temp struct { T string `json:"__typename"` } err := json.Unmarshal(d, &temp) if err != nil { return fmt.Errorf("failed to unmarshal typename: %w", err) } a.TypeName = temp.T switch a.TypeName { case "DeprecatedProduct": var p DepProdKey err := json.Unmarshal(d, &p) if err != nil { return fmt.Errorf("failed to unmarshal deprecated product key: %w", err) } (*a).Key = p case "Inventory": var i InvKey err := json.Unmarshal(d, &i) if err != nil { return fmt.Errorf("failed to unmarshal inventory key: %w", err) } (*a).Key = i case "Product": var p ProdKey err := json.Unmarshal(d, &p) if err != nil { return fmt.Errorf("failed to unmarshal product key: %w", err) } (*a).Key = p case "ProductResearch": var r ProdResKey err := json.Unmarshal(d, &r) if err != nil { return fmt.Errorf("failed to unmarshal product research key: %w", err) } (*a).Key = r case "User": var u UserKey err := json.Unmarshal(d, &u) if err != nil { return fmt.Errorf("failed to unmarshal product key: %w", err) } (*a).Key = u default: return fmt.Errorf("invalid typename %q", a.TypeName) } return nil } func (Any) ImplementsGraphQLType(name string) bool { return name == "_Any" } func (a *Any) UnmarshalGraphQL(input interface{}) error { var data []byte switch val := input.(type) { case string: // json wrapped in quotes v := fmt.Sprint(val) // turns all the `\"` into `"` data = []byte(v) case map[string]interface{}: var err error data, err = json.Marshal(val) if err != nil { return fmt.Errorf("failed to marshal to json: %w", err) } default: return fmt.Errorf("invalid input type %T", input) } return json.Unmarshal(data, a) } graphql-go-1.6.0/example/federation/compatibility/index.html000066400000000000000000000022751475633407000242170ustar00rootroot00000000000000 GraphiQL
Loading...
graphql-go-1.6.0/example/federation/compatibility/schema.graphql000066400000000000000000000065331475633407000250430ustar00rootroot00000000000000type Product @custom @key(fields: "id") @key(fields: "sku package") @key(fields: "sku variation { id }") { id: ID! sku: String package: String variation: ProductVariation dimensions: ProductDimension createdBy: User @provides(fields: "totalProductsCreated") notes: String @tag(name: "internal") research: [ProductResearch!]! } type DeprecatedProduct @key(fields: "sku package") { sku: String! package: String! reason: String createdBy: User } type ProductVariation { id: ID! } type ProductResearch @key(fields: "study { caseNumber }") { study: CaseStudy! outcome: String } type CaseStudy { caseNumber: ID! description: String } type ProductDimension @shareable { size: String weight: Float unit: String @inaccessible } type Query { product(id: ID!): Product deprecatedProduct(sku: String!, package: String!): DeprecatedProduct @deprecated(reason: "Use product query instead") } type User @extends @key(fields: "email") { averageProductsCreatedPerYear: Int @requires(fields: "totalProductsCreated yearsOfEmployment") email: ID! @external name: String @override(from: "users") totalProductsCreated: Int @external yearsOfEmployment: Int! @external } type Inventory @interfaceObject @key(fields: "id") { id: ID! deprecatedProducts: [DeprecatedProduct!]! } # # Apollo Federation schema # scalar FieldSet scalar link__Import scalar _Any union _Entity = Product | DeprecatedProduct | ProductResearch | User | Inventory enum link__Purpose { """ `SECURITY` features provide metadata necessary to securely resolve fields. """ SECURITY """ `EXECUTIO`N features provide metadata necessary for operation execution. """ EXECUTION } type _Service { sdl: String! } extend type Query { _service: _Service! _entities(representations: [_Any!]!): [_Entity]! } directive @composeDirective(name: String!) repeatable on SCHEMA directive @external on FIELD_DEFINITION | OBJECT directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @interfaceObject on OBJECT directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @link(url: String!, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA directive @override(from: String!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable repeatable on OBJECT | FIELD_DEFINITION directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION # This definition is required only for libraries that don't support # GraphQL's built-in `extend` keyword directive @extends on OBJECT | INTERFACE directive @custom on OBJECT extend schema @link( url: "https://specs.apollo.dev/federation/v2.3", import: [ "@composeDirective", "@extends", "@external", "@inaccessible", "@interfaceObject", "@key", "@override", "@provides", "@requires", "@shareable", "@tag", "FieldSet" ] ) @link(url: "https://myspecs.dev/myCustomDirective/v1.0", import: ["@custom"]) @composeDirective(name: "@custom") graphql-go-1.6.0/example/federation/compatibility/server.go000066400000000000000000000171241475633407000240560ustar00rootroot00000000000000package main import ( _ "embed" "fmt" "log" "net/http" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/relay" ) //go:embed index.html var page []byte //go:embed schema.graphql var sdl string type entitiesFunc func(args struct{ Representations []Any }) ([]*Entity, error) type resolver struct { depProducts map[string]DeprecatedProduct products map[graphql.ID]Product Entities entitiesFunc `graphql:"_entities"` Service func() Service `graphql:"_service"` } func (r *resolver) Product(args struct{ ID graphql.ID }) (*Product, error) { p, ok := r.products[args.ID] if !ok { return nil, fmt.Errorf("product not found") } return &p, nil } type Product struct { ID graphql.ID SKU *string Package *string Variation *ProductVariation Dimensions *ProductDimension CreatedBy *User Notes *string Research []ProductResearch } type ProductVariation struct { ID graphql.ID } type ProductDimension struct { Size *string Weight *float64 Unit *string } type ProductResearch struct { Study CaseStudy Outcome *string } type CaseStudy struct { CaseNumber graphql.ID Description *string } type DeprecatedProductArgs struct { SKU string Package string } func (r *resolver) DeprecatedProduct(args *DeprecatedProductArgs) (*DeprecatedProduct, error) { if args == nil { return nil, fmt.Errorf("args required") } key := args.SKU + "-" + args.Package p, ok := r.depProducts[key] if !ok { return nil, fmt.Errorf("product not found") } return &p, nil } type DeprecatedProduct struct { SKU string Package string Reason *string CreatedBy *User } type User struct { AverageProductsCreatedPerYear *int32 Email graphql.ID Name *string TotalProductsCreated *int32 YearsOfEmployment int32 } type Inventory struct { ID graphql.ID DeprecatedProducts []DeprecatedProduct } func entities(depProds map[string]DeprecatedProduct, invs map[graphql.ID]Inventory, products map[graphql.ID]Product, researches map[graphql.ID]ProductResearch, users map[graphql.ID]User) entitiesFunc { pkgKey := func(sku, pkg string) string { return sku + "-" + pkg } productsByPkg := map[string]graphql.ID{} for _, p := range products { if p.SKU != nil && p.Package != nil { productsByPkg[pkgKey(*p.SKU, *p.Package)] = p.ID } } variationKey := func(sku string, variationID graphql.ID) string { return sku + "-" + string(variationID) } productsByVariation := map[string]graphql.ID{} for _, p := range products { if p.SKU != nil && p.Variation != nil { productsByVariation[variationKey(*p.SKU, p.Variation.ID)] = p.ID } } return func(args struct{ Representations []Any }) ([]*Entity, error) { var res []*Entity for _, rep := range args.Representations { switch rep.TypeName { case "DeprecatedProduct": var prod *DeprecatedProduct key := rep.Key.(DepProdKey) p, ok := depProds[pkgKey(key.SKU, key.Package)] if ok { prod = &p } res = append(res, &Entity{entity: prod}) case "Inventory": var inv *Inventory key := rep.Key.(InvKey) i, ok := invs[key.ID] if ok { inv = &i } res = append(res, &Entity{entity: inv}) case "Product": var prod *Product key := rep.Key.(ProdKey) if key.ID != nil { p, ok := products[*key.ID] if ok { prod = &p } } else if key.SKU != nil { // next two checks require SKU if key.Package != nil { id, ok := productsByPkg[pkgKey(*key.SKU, *key.Package)] if ok { p := products[id] prod = &p } } else if key.Variation != nil { id, ok := productsByVariation[variationKey(*key.SKU, key.Variation.ID)] if ok { p := products[id] prod = &p } } } res = append(res, &Entity{entity: prod}) case "ProductResearch": var pr *ProductResearch key := rep.Key.(ProdResKey) r, ok := researches[key.Study.ID] if ok { pr = &r } res = append(res, &Entity{entity: pr}) case "User": var usr *User key := rep.Key.(UserKey) u, ok := users[key.Email] if ok { usr = &u } res = append(res, &Entity{entity: usr}) default: return nil, fmt.Errorf("unexpected representation type %q", rep.TypeName) } } return res, nil } } type Entity struct { entity interface{} } func (e *Entity) ToProduct() (*Product, bool) { p, ok := e.entity.(*Product) return p, ok } func (e *Entity) ToDeprecatedProduct() (*DeprecatedProduct, bool) { p, ok := e.entity.(*DeprecatedProduct) return p, ok } func (e *Entity) ToProductResearch() (*ProductResearch, bool) { p, ok := e.entity.(*ProductResearch) return p, ok } func (e *Entity) ToUser() (*User, bool) { u, ok := e.entity.(*User) return u, ok } func (e *Entity) ToInventory() (*Inventory, bool) { i, ok := e.entity.(*Inventory) return i, ok } func service(s string) func() Service { return func() Service { return Service{SDL: s} } } type Service struct { SDL string } func populateResolver(sdl string) *resolver { defaultUser := &User{ Email: graphql.ID("support@apollographql.com"), Name: strptr("Jane Smith"), TotalProductsCreated: intptr(1337), AverageProductsCreatedPerYear: intptr(134), YearsOfEmployment: 10, } users := map[graphql.ID]User{ defaultUser.Email: *defaultUser, } prodResearch1 := ProductResearch{ Study: CaseStudy{ CaseNumber: "1234", Description: strptr("Federation Study"), }, } prodResearch2 := ProductResearch{ Study: CaseStudy{ CaseNumber: "1235", Description: strptr("Studio Study"), }, } researches := map[graphql.ID]ProductResearch{ prodResearch1.Study.CaseNumber: prodResearch1, prodResearch2.Study.CaseNumber: prodResearch2, } dim := ProductDimension{ Size: strptr("small"), Weight: floatptr(1), Unit: strptr("kg"), } prod1 := Product{ ID: "apollo-federation", SKU: strptr("federation"), Dimensions: &dim, CreatedBy: defaultUser, Package: strptr("@apollo/federation"), Variation: &ProductVariation{ID: "OSS"}, Research: []ProductResearch{prodResearch1}, } prod2 := Product{ ID: "apollo-studio", SKU: strptr("studio"), Dimensions: &dim, CreatedBy: defaultUser, Variation: &ProductVariation{ID: "platform"}, Research: []ProductResearch{prodResearch2}, } products := map[graphql.ID]Product{ prod1.ID: prod1, prod2.ID: prod2, } depProduct1 := DeprecatedProduct{ SKU: "apollo-federation-v1", Package: "@apollo/federation-v1", Reason: strptr("Migrate to Federation V2"), CreatedBy: defaultUser, } depProducts := map[string]DeprecatedProduct{ depProduct1.SKU + "-" + depProduct1.Package: depProduct1, } inv := Inventory{ ID: graphql.ID("apollo-oss"), DeprecatedProducts: []DeprecatedProduct{depProduct1}, } invs := map[graphql.ID]Inventory{ inv.ID: inv, } return &resolver{ depProducts: depProducts, products: products, Entities: entities(depProducts, invs, products, researches, users), Service: service(sdl), } } func intptr(i int32) *int32 { return &i } func strptr(s string) *string { return &s } func floatptr(f float64) *float64 { return &f } func main() { r := populateResolver(sdl) opts := []graphql.SchemaOpt{graphql.UseStringDescriptions(), graphql.UseFieldResolvers()} schema := graphql.MustParseSchema(sdl, r, opts...) http.HandleFunc("/graphiql", func(w http.ResponseWriter, r *http.Request) { w.Write(page) }) http.Handle("/", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":4001", nil)) } graphql-go-1.6.0/example/federation/integration/000077500000000000000000000000001475633407000216665ustar00rootroot00000000000000graphql-go-1.6.0/example/federation/integration/README.md000066400000000000000000000011111475633407000231370ustar00rootroot00000000000000# Apollo Federation A basic example of integration with apollo federation as subgraph. Tested with Go v1.18, Node.js v16.14.2 and yarn 1.22.18. To run this server `go run ./example/apollo-federation/subgraph-one/server.go` `go run ./example/apollo-federation/subgraph-two/server.go` `cd example/apollo-federation/gateway` `yarn start` and go to localhost:4000 to interact Execute the query: ``` query { hello hi } ``` and you should see a result similar to this: ```json { "data": { "hello": "Hello from subgraph one!", "hi": "Hi from subgraph two!" } } ``` graphql-go-1.6.0/example/federation/integration/gateway/000077500000000000000000000000001475633407000233275ustar00rootroot00000000000000graphql-go-1.6.0/example/federation/integration/gateway/.gitignore000066400000000000000000000000311475633407000253110ustar00rootroot00000000000000/node_modules /yarn.lock graphql-go-1.6.0/example/federation/integration/gateway/index.js000066400000000000000000000012501475633407000247720ustar00rootroot00000000000000import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; import { ApolloGateway, IntrospectAndCompose } from '@apollo/gateway'; const gateway = new ApolloGateway({ supergraphSdl: new IntrospectAndCompose({ subgraphs: [ { name: 'one', url: 'http://localhost:4001/query' }, { name: 'two', url: 'http://localhost:4002/query' }, ], }), }); const server = new ApolloServer({ gateway, plugins: [ ApolloServerPluginSubscription(), ], }); (async () => { const { url } = await startStandaloneServer(server); console.log(`Server ready at ${url}`); })(); graphql-go-1.6.0/example/federation/integration/gateway/package-lock.json000066400000000000000000003632031475633407000265520ustar00rootroot00000000000000{ "name": "apollo-federation-gateway", "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "apollo-federation-gateway", "version": "1.0.1", "dependencies": { "@apollo/gateway": "^2.10.0", "@apollo/server": "^4.11.3", "graphql": "^16.10.0" } }, "node_modules/@apollo/cache-control-types": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@apollo/cache-control-types/-/cache-control-types-1.0.3.tgz", "integrity": "sha512-F17/vCp7QVwom9eG7ToauIKdAxpSoadsJnqIfyryLFSkLSOEqu+eC5Z3N8OXcUVStuOMcNHlyraRsA6rRICu4g==", "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/gateway": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@apollo/gateway/-/gateway-2.10.0.tgz", "integrity": "sha512-XJdyCpUr1G309j8tkHkkaG/KhHDD44/NbxwEF0d8CBMp+DHyYl8fgojUogSmiVJ/Ggdg9/68U4RznojbvCqBCw==", "dependencies": { "@apollo/composition": "2.10.0", "@apollo/federation-internals": "2.10.0", "@apollo/query-planner": "2.10.0", "@apollo/server-gateway-interface": "^1.1.0", "@apollo/usage-reporting-protobuf": "^4.1.0", "@apollo/utils.createhash": "^2.0.0", "@apollo/utils.fetcher": "^2.0.0", "@apollo/utils.isnodelike": "^2.0.0", "@apollo/utils.keyvaluecache": "^2.1.0", "@apollo/utils.logger": "^2.0.0", "@josephg/resolvable": "^1.0.1", "@opentelemetry/api": "^1.0.1", "@types/node-fetch": "^2.6.2", "async-retry": "^1.3.3", "loglevel": "^1.6.1", "make-fetch-happen": "^11.0.0", "node-abort-controller": "^3.0.1", "node-fetch": "^2.6.7" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/gateway/node_modules/@apollo/composition": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@apollo/composition/-/composition-2.10.0.tgz", "integrity": "sha512-+EkUY97+DMrBnMvbaPGwYwVmOzNgvAmbvRJSMGGC95ISnev7Cd5sVVVP6HKKann3hd8Yq6y83Enzp1xew5HI4w==", "dependencies": { "@apollo/federation-internals": "2.10.0", "@apollo/query-graphs": "2.10.0" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/gateway/node_modules/@apollo/composition/node_modules/@apollo/query-graphs": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.10.0.tgz", "integrity": "sha512-d9PgQ7sjd98lK2NvKKYp1iOZP24OrvbtRw5GNuW7nlQPYD4D8DCofEBGtDXs7RIcWaMp5OrU4U2dQcCzPi2MDg==", "dependencies": { "@apollo/federation-internals": "2.10.0", "deep-equal": "^2.0.5", "ts-graphviz": "^1.5.4", "uuid": "^9.0.0" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/gateway/node_modules/@apollo/federation-internals": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@apollo/federation-internals/-/federation-internals-2.10.0.tgz", "integrity": "sha512-IneWuwDc9ozKLcEsKw7YEkhzo7VAa54RjKwAVEqPFHPVqUjIYUKX99gSWAp9ZQeI8DGrz2a8uKEDlKGOHh3UaA==", "dependencies": { "@types/uuid": "^9.0.0", "chalk": "^4.1.0", "js-levenshtein": "^1.1.6", "uuid": "^9.0.0" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/gateway/node_modules/@apollo/query-planner": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@apollo/query-planner/-/query-planner-2.10.0.tgz", "integrity": "sha512-vt+UaHbH1Jyc1Xi04XXnDrQFBwL/T/7cJC/13zuAZUnge5oN6udfc3wa/MeSxri8a1pV+c8us3dwlnAAsp7vvw==", "dependencies": { "@apollo/federation-internals": "2.10.0", "@apollo/query-graphs": "2.10.0", "@apollo/utils.keyvaluecache": "^2.1.0", "chalk": "^4.1.0", "deep-equal": "^2.0.5", "pretty-format": "^29.0.0" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/gateway/node_modules/@apollo/query-planner/node_modules/@apollo/query-graphs": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.10.0.tgz", "integrity": "sha512-d9PgQ7sjd98lK2NvKKYp1iOZP24OrvbtRw5GNuW7nlQPYD4D8DCofEBGtDXs7RIcWaMp5OrU4U2dQcCzPi2MDg==", "dependencies": { "@apollo/federation-internals": "2.10.0", "deep-equal": "^2.0.5", "ts-graphviz": "^1.5.4", "uuid": "^9.0.0" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/protobufjs": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.7.tgz", "integrity": "sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/long": "^4.0.0", "long": "^4.0.0" }, "bin": { "apollo-pbjs": "bin/pbjs", "apollo-pbts": "bin/pbts" } }, "node_modules/@apollo/server": { "version": "4.11.3", "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.11.3.tgz", "integrity": "sha512-mW8idE2q0/BN14mimfJU5DAnoPHZRrAWgwsVLBEdACds+mxapIYxIbI6AH4AsOpxfrpvHts3PCYDbopy1XPW1g==", "dependencies": { "@apollo/cache-control-types": "^1.0.3", "@apollo/server-gateway-interface": "^1.1.1", "@apollo/usage-reporting-protobuf": "^4.1.1", "@apollo/utils.createhash": "^2.0.2", "@apollo/utils.fetcher": "^2.0.0", "@apollo/utils.isnodelike": "^2.0.0", "@apollo/utils.keyvaluecache": "^2.1.0", "@apollo/utils.logger": "^2.0.0", "@apollo/utils.usagereporting": "^2.1.0", "@apollo/utils.withrequired": "^2.0.0", "@graphql-tools/schema": "^9.0.0", "@types/express": "^4.17.13", "@types/express-serve-static-core": "^4.17.30", "@types/node-fetch": "^2.6.1", "async-retry": "^1.2.1", "cors": "^2.8.5", "express": "^4.21.1", "loglevel": "^1.6.8", "lru-cache": "^7.10.1", "negotiator": "^0.6.3", "node-abort-controller": "^3.1.1", "node-fetch": "^2.6.7", "uuid": "^9.0.0", "whatwg-mimetype": "^3.0.0" }, "engines": { "node": ">=14.16.0" }, "peerDependencies": { "graphql": "^16.6.0" } }, "node_modules/@apollo/server-gateway-interface": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-1.1.1.tgz", "integrity": "sha512-pGwCl/po6+rxRmDMFgozKQo2pbsSwE91TpsDBAOgf74CRDPXHHtM88wbwjab0wMMZh95QfR45GGyDIdhY24bkQ==", "dependencies": { "@apollo/usage-reporting-protobuf": "^4.1.1", "@apollo/utils.fetcher": "^2.0.0", "@apollo/utils.keyvaluecache": "^2.1.0", "@apollo/utils.logger": "^2.0.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/usage-reporting-protobuf": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz", "integrity": "sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==", "dependencies": { "@apollo/protobufjs": "1.2.7" } }, "node_modules/@apollo/utils.createhash": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-2.0.2.tgz", "integrity": "sha512-UkS3xqnVFLZ3JFpEmU/2cM2iKJotQXMoSTgxXsfQgXLC5gR1WaepoXagmYnPSA7Q/2cmnyTYK5OgAgoC4RULPg==", "dependencies": { "@apollo/utils.isnodelike": "^2.0.1", "sha.js": "^2.4.11" }, "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.dropunuseddefinitions": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-2.0.1.tgz", "integrity": "sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==", "engines": { "node": ">=14" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.fetcher": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-2.0.1.tgz", "integrity": "sha512-jvvon885hEyWXd4H6zpWeN3tl88QcWnHp5gWF5OPF34uhvoR+DFqcNxs9vrRaBBSY3qda3Qe0bdud7tz2zGx1A==", "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.isnodelike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-2.0.1.tgz", "integrity": "sha512-w41XyepR+jBEuVpoRM715N2ZD0xMD413UiJx8w5xnAZD2ZkSJnMJBoIzauK83kJpSgNuR6ywbV29jG9NmxjK0Q==", "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.keyvaluecache": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-2.1.1.tgz", "integrity": "sha512-qVo5PvUUMD8oB9oYvq4ViCjYAMWnZ5zZwEjNF37L2m1u528x5mueMlU+Cr1UinupCgdB78g+egA1G98rbJ03Vw==", "dependencies": { "@apollo/utils.logger": "^2.0.1", "lru-cache": "^7.14.1" }, "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.logger": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-2.0.1.tgz", "integrity": "sha512-YuplwLHaHf1oviidB7MxnCXAdHp3IqYV8n0momZ3JfLniae92eYqMIx+j5qJFX6WKJPs6q7bczmV4lXIsTu5Pg==", "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.printwithreducedwhitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz", "integrity": "sha512-9M4LUXV/fQBh8vZWlLvb/HyyhjJ77/I5ZKu+NBWV/BmYGyRmoEP9EVAy7LCVoY3t8BDcyCAGfxJaLFCSuQkPUg==", "engines": { "node": ">=14" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.removealiases": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-2.0.1.tgz", "integrity": "sha512-0joRc2HBO4u594Op1nev+mUF6yRnxoUH64xw8x3bX7n8QBDYdeYgY4tF0vJReTy+zdn2xv6fMsquATSgC722FA==", "engines": { "node": ">=14" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.sortast": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-2.0.1.tgz", "integrity": "sha512-eciIavsWpJ09za1pn37wpsCGrQNXUhM0TktnZmHwO+Zy9O4fu/WdB4+5BvVhFiZYOXvfjzJUcc+hsIV8RUOtMw==", "dependencies": { "lodash.sortby": "^4.7.0" }, "engines": { "node": ">=14" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.stripsensitiveliterals": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-2.0.1.tgz", "integrity": "sha512-QJs7HtzXS/JIPMKWimFnUMK7VjkGQTzqD9bKD1h3iuPAqLsxd0mUNVbkYOPTsDhUKgcvUOfOqOJWYohAKMvcSA==", "engines": { "node": ">=14" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.usagereporting": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-2.1.0.tgz", "integrity": "sha512-LPSlBrn+S17oBy5eWkrRSGb98sWmnEzo3DPTZgp8IQc8sJe0prDgDuppGq4NeQlpoqEHz0hQeYHAOA0Z3aQsxQ==", "dependencies": { "@apollo/usage-reporting-protobuf": "^4.1.0", "@apollo/utils.dropunuseddefinitions": "^2.0.1", "@apollo/utils.printwithreducedwhitespace": "^2.0.1", "@apollo/utils.removealiases": "2.0.1", "@apollo/utils.sortast": "^2.0.1", "@apollo/utils.stripsensitiveliterals": "^2.0.1" }, "engines": { "node": ">=14" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.withrequired": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-2.0.1.tgz", "integrity": "sha512-YBDiuAX9i1lLc6GeTy1m7DGLFn/gMnvXqlalOIMjM7DeOgIacEjjfwPqb0M1CQ2v11HhR15d1NmxJoRCfrNqcA==", "engines": { "node": ">=14" } }, "node_modules/@graphql-tools/merge": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz", "integrity": "sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==", "dependencies": { "@graphql-tools/utils": "^9.2.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/schema": { "version": "9.0.19", "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz", "integrity": "sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==", "dependencies": { "@graphql-tools/merge": "^8.4.1", "@graphql-tools/utils": "^9.2.1", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/utils": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { "node": ">=12" } }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dependencies": { "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@josephg/resolvable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==" }, "node_modules/@npmcli/fs": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", "dependencies": { "semver": "^7.3.5" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@opentelemetry/api": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.0.tgz", "integrity": "sha512-IgMK9i3sFGNUqPMbjABm0G26g0QCKCUBfglhQ7rQq6WcxbKfEHRcmwsoER4hZcuYqJgkYn2OeuoJIv7Jsftp7g==", "engines": { "node": ">=8.0.0" } }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "optional": true, "engines": { "node": ">=14" } }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "engines": { "node": ">= 10" } }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { "version": "4.19.6", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/node": { "version": "18.14.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.4.tgz", "integrity": "sha512-VhCw7I7qO2X49+jaKcAUwi3rR+hbxT5VcYF493+Z5kMLI0DL568b7JI4IDJaxWFH0D/xwmGJNoXisyX+w7GH/g==" }, "node_modules/@types/node-fetch": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "node_modules/@types/serve-static": { "version": "1.15.7", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "*" } }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" } }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dependencies": { "debug": "4" }, "engines": { "node": ">= 6.0.0" } }, "node_modules/agent-base/node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/agent-base/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/agentkeepalive": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", "dependencies": { "humanize-ms": "^1.2.1" }, "engines": { "node": ">= 8.0.0" } }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dependencies": { "retry": "0.13.1" } }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dependencies": { "possible-typed-array-names": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } }, "node_modules/cacache": { "version": "17.1.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz", "integrity": "sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==", "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^7.7.1", "minipass": "^7.0.3", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", "ssri": "^10.0.0", "tar": "^6.1.11", "unique-filename": "^3.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/cacache/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/call-bound": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "engines": { "node": ">=10" } }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "engines": { "node": ">=6" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { "delayed-stream": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dependencies": { "object-assign": "^4", "vary": "^1" }, "engines": { "node": ">= 0.10" } }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.5", "es-get-iterator": "^1.1.3", "get-intrinsic": "^1.2.2", "is-arguments": "^1.1.1", "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "isarray": "^2.0.5", "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { "node": ">=0.4.0" } }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { "node": ">= 0.8" } }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "optional": true, "dependencies": { "iconv-lite": "^0.6.2" } }, "node_modules/encoding/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "engines": { "node": ">= 0.4" } }, "node_modules/es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", "has-symbols": "^1.0.3", "is-arguments": "^1.1.1", "is-map": "^2.0.2", "is-set": "^2.0.2", "is-string": "^1.0.7", "isarray": "^2.0.5", "stop-iteration-iterator": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { "es-errors": "^1.3.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-set-tostringtag": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.10.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dependencies": { "is-callable": "^1.1.3" } }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/form-data": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", "dependencies": { "minipass": "^7.0.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/fs-minipass/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-intrinsic": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graphql": { "version": "16.10.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { "node": ">=8" } }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-tostringtag": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" }, "engines": { "node": ">= 0.8" } }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" }, "engines": { "node": ">= 6" } }, "node_modules/http-proxy-agent/node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/http-proxy-agent/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dependencies": { "agent-base": "6", "debug": "4" }, "engines": { "node": ">= 6" } }, "node_modules/https-proxy-agent/node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/https-proxy-agent/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dependencies": { "ms": "^2.0.0" } }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" } }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "engines": { "node": ">=8" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" }, "engines": { "node": ">= 12" } }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { "node": ">= 0.10" } }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dependencies": { "has-bigints": "^1.0.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { "node": ">=8" } }, "node_modules/is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==" }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dependencies": { "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-symbol": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dependencies": { "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/isaacs" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", "engines": { "node": ">=0.10.0" } }, "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, "node_modules/loglevel": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", "engines": { "node": ">= 0.6.0" }, "funding": { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/loglevel" } }, "node_modules/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "engines": { "node": ">=12" } }, "node_modules/make-fetch-happen": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^17.0.0", "http-cache-semantics": "^4.1.1", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", "lru-cache": "^7.7.1", "minipass": "^5.0.0", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", "socks-proxy-agent": "^7.0.0", "ssri": "^10.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "engines": { "node": ">= 0.4" } }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { "node": ">= 0.6" } }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "bin": { "mime": "cli.js" }, "engines": { "node": ">=4" } }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "engines": { "node": ">=8" } }, "node_modules/minipass-collect": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/minipass-collect/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-fetch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "node_modules/minipass-fetch/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-flush": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-pipeline/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-sized/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "bin": { "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" } }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" }, "node_modules/node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "peerDependenciesMeta": { "encoding": { "optional": true } } }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-is": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, "engines": { "node": ">= 0.8" } }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dependencies": { "aggregate-error": "^3.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { "node": ">= 0.8" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "engines": { "node": ">=8" } }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "engines": { "node": ">= 0.4" } }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" }, "engines": { "node": ">=10" } }, "node_modules/promise-retry/node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "engines": { "node": ">= 4" } }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" } }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "engines": { "node": ">= 4" } }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/send/node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/set-function-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" }, "bin": { "sha.js": "bin.js" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "engines": { "node": ">=8" } }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-list": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-weakmap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks": { "version": "2.8.4", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", "socks": "^2.6.2" }, "engines": { "node": ">= 10" } }, "node_modules/socks-proxy-agent/node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/socks-proxy-agent/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "node_modules/ssri": { "version": "10.0.6", "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", "dependencies": { "minipass": "^7.0.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/ssri/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { "node": ">= 0.8" } }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/string-width-cjs/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { "ansi-regex": "^6.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/tar/node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/ts-graphviz": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/ts-graphviz/-/ts-graphviz-1.8.2.tgz", "integrity": "sha512-5YhbFoHmjxa7pgQLkB07MtGnGJ/yhvjmc9uhsnDBEICME6gkPf83SBwLDQqGDoCa3XzUMWLk1AU2Wn1u1naDtA==", "engines": { "node": ">=14.16" }, "funding": { "type": "github", "url": "https://github.com/sponsors/ts-graphviz" } }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" }, "engines": { "node": ">= 0.6" } }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", "dependencies": { "unique-slug": "^4.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/unique-slug": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "engines": { "node": ">= 0.8" } }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "engines": { "node": ">= 0.4.0" } }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/value-or-promise": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", "engines": { "node": ">=12" } }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "engines": { "node": ">= 0.8" } }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", "engines": { "node": ">=12" } }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", "is-number-object": "^1.0.4", "is-string": "^1.0.5", "is-symbol": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.3", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } graphql-go-1.6.0/example/federation/integration/gateway/package.json000066400000000000000000000004701475633407000256160ustar00rootroot00000000000000{ "name": "apollo-federation-gateway", "version": "1.1.0", "description": "Graphql Federation", "main": "index.js", "type": "module", "scripts": { "start": "node index.js" }, "dependencies": { "@apollo/gateway": "^2.10.0", "@apollo/server": "^4.11.3", "graphql": "^16.10.0" } } graphql-go-1.6.0/example/federation/integration/subgraph-one/000077500000000000000000000000001475633407000242605ustar00rootroot00000000000000graphql-go-1.6.0/example/federation/integration/subgraph-one/server.go000066400000000000000000000014411475633407000261150ustar00rootroot00000000000000package main import ( "log" "net/http" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/relay" ) var sdl = ` type Query { hello: String! _service: Service! } type Service { sdl: String! } ` type resolver struct { Service func() Service `graphql:"_service"` } func (r *resolver) Hello() string { return "Hello from subgraph one!" } func service(s string) func() Service { return func() Service { return Service{SDL: s} } } type Service struct { SDL string } func main() { opts := []graphql.SchemaOpt{graphql.UseFieldResolvers(), graphql.MaxParallelism(20)} schema := graphql.MustParseSchema(sdl, &resolver{Service: service(sdl)}, opts...) http.Handle("/query", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":4001", nil)) } graphql-go-1.6.0/example/federation/integration/subgraph-two/000077500000000000000000000000001475633407000243105ustar00rootroot00000000000000graphql-go-1.6.0/example/federation/integration/subgraph-two/server.go000066400000000000000000000014301475633407000261430ustar00rootroot00000000000000package main import ( "log" "net/http" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/relay" ) var sdl = ` type Query { hi: String! _service: Service! } type Service { sdl: String! } ` type resolver struct { Service func() Service `graphql:"_service"` } func (r *resolver) Hi() string { return "Hi from subgraph two!" } func service(s string) func() Service { return func() Service { return Service{SDL: s} } } type Service struct { SDL string } func main() { opts := []graphql.SchemaOpt{graphql.UseFieldResolvers(), graphql.MaxParallelism(20)} schema := graphql.MustParseSchema(sdl, &resolver{Service: service(sdl)}, opts...) http.Handle("/query", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":4002", nil)) } graphql-go-1.6.0/example/social/000077500000000000000000000000001475633407000164755ustar00rootroot00000000000000graphql-go-1.6.0/example/social/README.md000066400000000000000000000003111475633407000177470ustar00rootroot00000000000000# Social App A simple example of how to use struct fields as resolvers instead of methods. To run this server `go run ./example/social/server/server.go` and go to http://localhost:8080 to interact graphql-go-1.6.0/example/social/introspect.json000066400000000000000000001224561475633407000215740ustar00rootroot00000000000000{ "__schema": { "directives": [ { "args": [ { "defaultValue": "\"No longer supported\"", "description": "Explains why this element was deprecated, usually also including a suggestion\nfor how to access supported similar data. Formatted in\n[Markdown](https://daringfireball.net/projects/markdown/).", "name": "reason", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "description": "Marks an element of a GraphQL schema as no longer supported.", "locations": [ "FIELD_DEFINITION", "ENUM_VALUE", "ARGUMENT_DEFINITION" ], "name": "deprecated" }, { "args": [ { "defaultValue": null, "description": "Included when true.", "name": "if", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } } ], "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", "locations": [ "FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT" ], "name": "include" }, { "args": [ { "defaultValue": null, "description": "Skipped when true.", "name": "if", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } } ], "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", "locations": [ "FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT" ], "name": "skip" }, { "args": [ { "defaultValue": null, "description": "The URL should point to a human-readable specification of the data format, serialization, and coercion rules.", "name": "url", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } ], "description": "Provides a scalar specification URL for specifying the behavior of custom scalar types.", "locations": [ "SCALAR" ], "name": "specifiedBy" } ], "mutationType": null, "queryType": { "name": "Query" }, "subscriptionType": null, "types": [ { "description": null, "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "role", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "Role", "ofType": null } } } ], "inputFields": null, "interfaces": null, "kind": "INTERFACE", "name": "Admin", "possibleTypes": [ { "kind": "OBJECT", "name": "User", "ofType": null } ] }, { "description": "The `Boolean` scalar type represents `true` or `false`.", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "Boolean", "possibleTypes": null }, { "description": "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "Float", "possibleTypes": null }, { "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "ID", "possibleTypes": null }, { "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "Int", "possibleTypes": null }, { "description": null, "enumValues": null, "fields": null, "inputFields": [ { "defaultValue": null, "description": null, "name": "first", "type": { "kind": "SCALAR", "name": "Int", "ofType": null } }, { "defaultValue": null, "description": null, "name": "last", "type": { "kind": "SCALAR", "name": "Int", "ofType": null } } ], "interfaces": null, "kind": "INPUT_OBJECT", "name": "Pagination", "possibleTypes": null }, { "description": null, "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } ], "inputFields": null, "interfaces": null, "kind": "INTERFACE", "name": "Person", "possibleTypes": [ { "kind": "OBJECT", "name": "User", "ofType": null } ] }, { "description": null, "enumValues": null, "fields": [ { "args": [ { "defaultValue": null, "description": null, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } }, { "defaultValue": "ADMIN", "description": null, "name": "role", "type": { "kind": "ENUM", "name": "Role", "ofType": null } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "admin", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "INTERFACE", "name": "Admin", "ofType": null } } }, { "args": [ { "defaultValue": null, "description": null, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "user", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "User", "ofType": null } } }, { "args": [ { "defaultValue": null, "description": null, "name": "text", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "search", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "UNION", "name": "SearchResult", "ofType": null } } } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "Query", "possibleTypes": null }, { "description": null, "enumValues": [ { "deprecationReason": null, "description": null, "isDeprecated": false, "name": "ADMIN" }, { "deprecationReason": null, "description": null, "isDeprecated": false, "name": "USER" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", "name": "Role", "possibleTypes": null }, { "description": null, "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "UNION", "name": "SearchResult", "possibleTypes": [ { "kind": "OBJECT", "name": "User", "ofType": null } ] }, { "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "String", "possibleTypes": null }, { "description": null, "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "Time", "possibleTypes": null }, { "description": null, "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "email", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "role", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "Role", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "phone", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "address", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } }, { "args": [ { "defaultValue": null, "description": null, "name": "page", "type": { "kind": "INPUT_OBJECT", "name": "Pagination", "ofType": null } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "friends", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "OBJECT", "name": "User", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "createdAt", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Time", "ofType": null } } } ], "inputFields": null, "interfaces": [ { "kind": "INTERFACE", "name": "Admin", "ofType": null }, { "kind": "INTERFACE", "name": "Person", "ofType": null } ], "kind": "OBJECT", "name": "User", "possibleTypes": null }, { "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior\nin ways field arguments will not suffice, such as conditionally including or\nskipping a field. Directives provide this by describing additional information\nto the executor.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "locations", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "__DirectiveLocation", "ofType": null } } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "args", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } } } } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__Directive", "possibleTypes": null }, { "description": "A Directive can be adjacent to many parts of the GraphQL language, a\n__DirectiveLocation describes one such possible adjacencies.", "enumValues": [ { "deprecationReason": null, "description": "Location adjacent to a query operation.", "isDeprecated": false, "name": "QUERY" }, { "deprecationReason": null, "description": "Location adjacent to a mutation operation.", "isDeprecated": false, "name": "MUTATION" }, { "deprecationReason": null, "description": "Location adjacent to a subscription operation.", "isDeprecated": false, "name": "SUBSCRIPTION" }, { "deprecationReason": null, "description": "Location adjacent to a field.", "isDeprecated": false, "name": "FIELD" }, { "deprecationReason": null, "description": "Location adjacent to a fragment definition.", "isDeprecated": false, "name": "FRAGMENT_DEFINITION" }, { "deprecationReason": null, "description": "Location adjacent to a fragment spread.", "isDeprecated": false, "name": "FRAGMENT_SPREAD" }, { "deprecationReason": null, "description": "Location adjacent to an inline fragment.", "isDeprecated": false, "name": "INLINE_FRAGMENT" }, { "deprecationReason": null, "description": "Location adjacent to a schema definition.", "isDeprecated": false, "name": "SCHEMA" }, { "deprecationReason": null, "description": "Location adjacent to a scalar definition.", "isDeprecated": false, "name": "SCALAR" }, { "deprecationReason": null, "description": "Location adjacent to an object type definition.", "isDeprecated": false, "name": "OBJECT" }, { "deprecationReason": null, "description": "Location adjacent to a field definition.", "isDeprecated": false, "name": "FIELD_DEFINITION" }, { "deprecationReason": null, "description": "Location adjacent to an argument definition.", "isDeprecated": false, "name": "ARGUMENT_DEFINITION" }, { "deprecationReason": null, "description": "Location adjacent to an interface definition.", "isDeprecated": false, "name": "INTERFACE" }, { "deprecationReason": null, "description": "Location adjacent to a union definition.", "isDeprecated": false, "name": "UNION" }, { "deprecationReason": null, "description": "Location adjacent to an enum definition.", "isDeprecated": false, "name": "ENUM" }, { "deprecationReason": null, "description": "Location adjacent to an enum value definition.", "isDeprecated": false, "name": "ENUM_VALUE" }, { "deprecationReason": null, "description": "Location adjacent to an input object type definition.", "isDeprecated": false, "name": "INPUT_OBJECT" }, { "deprecationReason": null, "description": "Location adjacent to an input object field definition.", "isDeprecated": false, "name": "INPUT_FIELD_DEFINITION" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", "name": "__DirectiveLocation", "possibleTypes": null }, { "description": "One possible value for a given Enum. Enum values are unique values, not a\nplaceholder for a string or numeric value. However an Enum value is returned in\na JSON response as a string.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "isDeprecated", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "deprecationReason", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__EnumValue", "possibleTypes": null }, { "description": "Object and Interface types are described by a list of Fields, each of which has\na name, potentially a list of arguments, and a return type.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "args", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "type", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "isDeprecated", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "deprecationReason", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__Field", "possibleTypes": null }, { "description": "Arguments provided to Fields or Directives and the input fields of an\nInputObject are represented as Input Values which describe their type and\noptionally a default value.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "type", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "A GraphQL-formatted string representing the default value for this input value.", "isDeprecated": false, "name": "defaultValue", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "isDeprecated", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "deprecationReason", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__InputValue", "possibleTypes": null }, { "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all\navailable types and directives on the server, as well as the entry points for\nquery, mutation, and subscription operations.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "A list of all types supported by this server.", "isDeprecated": false, "name": "types", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } } } }, { "args": [], "deprecationReason": null, "description": "The type that query operations will be rooted at.", "isDeprecated": false, "name": "queryType", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "If this server supports mutation, the type that mutation operations will be rooted at.", "isDeprecated": false, "name": "mutationType", "type": { "kind": "OBJECT", "name": "__Type", "ofType": null } }, { "args": [], "deprecationReason": null, "description": "If this server support subscription, the type that subscription operations will be rooted at.", "isDeprecated": false, "name": "subscriptionType", "type": { "kind": "OBJECT", "name": "__Type", "ofType": null } }, { "args": [], "deprecationReason": null, "description": "A list of all directives supported by this server.", "isDeprecated": false, "name": "directives", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Directive", "ofType": null } } } } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__Schema", "possibleTypes": null }, { "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of\ntypes in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that\ntype. Scalar types provide no information beyond a name and description, while\nEnum types provide their values. Object and Interface types provide the fields\nthey describe. Abstract types, Union and Interface, provide the Object types\npossible at runtime. List and NonNull types compose other types.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "kind", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "__TypeKind", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [ { "defaultValue": "false", "description": null, "name": "includeDeprecated", "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "fields", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Field", "ofType": null } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "interfaces", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "possibleTypes", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } } }, { "args": [ { "defaultValue": "false", "description": null, "name": "includeDeprecated", "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "enumValues", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__EnumValue", "ofType": null } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "inputFields", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "ofType", "type": { "kind": "OBJECT", "name": "__Type", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "specifiedByURL", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__Type", "possibleTypes": null }, { "description": "An enum describing what kind of type a given `__Type` is.", "enumValues": [ { "deprecationReason": null, "description": "Indicates this type is a scalar.", "isDeprecated": false, "name": "SCALAR" }, { "deprecationReason": null, "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", "isDeprecated": false, "name": "OBJECT" }, { "deprecationReason": null, "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", "isDeprecated": false, "name": "INTERFACE" }, { "deprecationReason": null, "description": "Indicates this type is a union. `possibleTypes` is a valid field.", "isDeprecated": false, "name": "UNION" }, { "deprecationReason": null, "description": "Indicates this type is an enum. `enumValues` is a valid field.", "isDeprecated": false, "name": "ENUM" }, { "deprecationReason": null, "description": "Indicates this type is an input object. `inputFields` is a valid field.", "isDeprecated": false, "name": "INPUT_OBJECT" }, { "deprecationReason": null, "description": "Indicates this type is a list. `ofType` is a valid field.", "isDeprecated": false, "name": "LIST" }, { "deprecationReason": null, "description": "Indicates this type is a non-null. `ofType` is a valid field.", "isDeprecated": false, "name": "NON_NULL" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", "name": "__TypeKind", "possibleTypes": null } ] } } graphql-go-1.6.0/example/social/server/000077500000000000000000000000001475633407000200035ustar00rootroot00000000000000graphql-go-1.6.0/example/social/server/server.go000066400000000000000000000034261475633407000216450ustar00rootroot00000000000000package main import ( "log" "net/http" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/social" "github.com/graph-gophers/graphql-go/relay" ) func main() { opts := []graphql.SchemaOpt{graphql.UseFieldResolvers(), graphql.MaxParallelism(20)} schema := graphql.MustParseSchema(social.Schema, &social.Resolver{}, opts...) http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write(page) })) http.Handle("/query", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":8080", nil)) } var page = []byte(` GraphiQL
Loading...
`) graphql-go-1.6.0/example/social/social.go000066400000000000000000000104231475633407000202760ustar00rootroot00000000000000package social import ( "context" "errors" "fmt" "strings" "time" "github.com/graph-gophers/graphql-go" ) const Schema = ` schema { query: Query } type Query { admin(id: ID!, role: Role = ADMIN): Admin! user(id: ID!): User! search(text: String!): [SearchResult]! } interface Admin { id: ID! name: String! role: Role! } interface Person { name: String! } scalar Time type User implements Admin & Person { id: ID! name: String! email: String! role: Role! phone: String! address: [String!] friends(page: Pagination): [User] createdAt: Time! } input Pagination { first: Int last: Int } enum Role { ADMIN USER } union SearchResult = User ` type page struct { First *float64 Last *float64 } type admin interface { ID() graphql.ID Name() string Role() string } type adminResolver struct { admin } func (r *adminResolver) ToUser() (*user, bool) { n, ok := r.admin.(user) return &n, ok } type searchResult struct { result interface{} } func (r *searchResult) ToUser() (*user, bool) { res, ok := r.result.(*user) return res, ok } type contact struct { Email string Phone string } type user struct { IDField string NameField string RoleField string Address *[]string FriendsField *[]*user CreatedAt graphql.Time contact } func (u user) ID() graphql.ID { return graphql.ID(u.IDField) } func (u user) Name() string { return u.NameField } func (u user) Role() string { return u.RoleField } func (u user) Friends(args struct{ Page *page }) (*[]*user, error) { var from int numFriends := len(*u.FriendsField) to := numFriends if args.Page != nil { if args.Page.First != nil { from = int(*args.Page.First) if from > numFriends { return nil, errors.New("not enough users") } } if args.Page.Last != nil { to = int(*args.Page.Last) if to == 0 || to > numFriends { to = numFriends } } } friends := (*u.FriendsField)[from:to] return &friends, nil } var users = []*user{ { IDField: "0x01", NameField: "Albus Dumbledore", RoleField: "ADMIN", Address: &[]string{"Office @ Hogwarts", "where Horcruxes are"}, CreatedAt: graphql.Time{Time: time.Now()}, contact: contact{ Email: "Albus@hogwarts.com", Phone: "000-000-0000", }, }, { IDField: "0x02", NameField: "Harry Potter", RoleField: "USER", Address: &[]string{"123 dorm room @ Hogwarts", "456 random place"}, CreatedAt: graphql.Time{Time: time.Now()}, contact: contact{ Email: "harry@hogwarts.com", Phone: "000-000-0001", }, }, { IDField: "0x03", NameField: "Hermione Granger", RoleField: "USER", Address: &[]string{"233 dorm room @ Hogwarts", "786 @ random place"}, CreatedAt: graphql.Time{Time: time.Now()}, contact: contact{ Email: "hermione@hogwarts.com", Phone: "000-000-0011", }, }, { IDField: "0x04", NameField: "Ronald Weasley", RoleField: "USER", Address: &[]string{"411 dorm room @ Hogwarts", "981 @ random place"}, CreatedAt: graphql.Time{Time: time.Now()}, contact: contact{ Email: "ronald@hogwarts.com", Phone: "000-000-0111", }, }, } var usersMap = make(map[string]*user) func init() { users[0].FriendsField = &[]*user{users[1]} users[1].FriendsField = &[]*user{users[0], users[2], users[3]} users[2].FriendsField = &[]*user{users[1], users[3]} users[3].FriendsField = &[]*user{users[1], users[2]} for _, usr := range users { usersMap[usr.IDField] = usr } } type Resolver struct{} func (r *Resolver) Admin(ctx context.Context, args struct { ID string Role string }) (*adminResolver, error) { if usr, ok := usersMap[args.ID]; ok { if usr.RoleField == args.Role { return &adminResolver{*usr}, nil } } err := fmt.Errorf("user with id=%s and role=%s does not exist", args.ID, args.Role) return nil, err } func (r *Resolver) User(ctx context.Context, args struct{ Id string }) (user, error) { if usr, ok := usersMap[args.Id]; ok { return *usr, nil } err := fmt.Errorf("user with id=%s does not exist", args.Id) return user{}, err } func (r *Resolver) Search(ctx context.Context, args struct{ Text string }) ([]*searchResult, error) { var result []*searchResult for _, usr := range users { if strings.Contains(usr.NameField, args.Text) { result = append(result, &searchResult{usr}) } } return result, nil } graphql-go-1.6.0/example/starwars/000077500000000000000000000000001475633407000170715ustar00rootroot00000000000000graphql-go-1.6.0/example/starwars/introspect.json000066400000000000000000001676411475633407000221750ustar00rootroot00000000000000{ "__schema": { "directives": [ { "args": [ { "defaultValue": "\"No longer supported\"", "description": "Explains why this element was deprecated, usually also including a suggestion\nfor how to access supported similar data. Formatted in\n[Markdown](https://daringfireball.net/projects/markdown/).", "name": "reason", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "description": "Marks an element of a GraphQL schema as no longer supported.", "locations": [ "FIELD_DEFINITION", "ENUM_VALUE", "ARGUMENT_DEFINITION" ], "name": "deprecated" }, { "args": [ { "defaultValue": null, "description": "Included when true.", "name": "if", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } } ], "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", "locations": [ "FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT" ], "name": "include" }, { "args": [ { "defaultValue": null, "description": "Skipped when true.", "name": "if", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } } ], "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", "locations": [ "FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT" ], "name": "skip" }, { "args": [ { "defaultValue": null, "description": "The URL should point to a human-readable specification of the data format, serialization, and coercion rules.", "name": "url", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } ], "description": "Provides a scalar specification URL for specifying the behavior of custom scalar types.", "locations": [ "SCALAR" ], "name": "specifiedBy" } ], "mutationType": { "name": "Mutation" }, "queryType": { "name": "Query" }, "subscriptionType": null, "types": [ { "description": "The `Boolean` scalar type represents `true` or `false`.", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "Boolean", "possibleTypes": null }, { "description": "A character from the Star Wars universe", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "The ID of the character", "isDeprecated": false, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "The name of the character", "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "The friends of the character, or an empty list if they have none", "isDeprecated": false, "name": "friends", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "INTERFACE", "name": "Character", "ofType": null } } }, { "args": [ { "defaultValue": null, "description": null, "name": "first", "type": { "kind": "SCALAR", "name": "Int", "ofType": null } }, { "defaultValue": null, "description": null, "name": "after", "type": { "kind": "SCALAR", "name": "ID", "ofType": null } } ], "deprecationReason": null, "description": "The friends of the character exposed as a connection with edges", "isDeprecated": false, "name": "friendsConnection", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "FriendsConnection", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "The movies this character appears in", "isDeprecated": false, "name": "appearsIn", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "Episode", "ofType": null } } } } } ], "inputFields": null, "interfaces": null, "kind": "INTERFACE", "name": "Character", "possibleTypes": [ { "kind": "OBJECT", "name": "Human", "ofType": null }, { "kind": "OBJECT", "name": "Droid", "ofType": null } ] }, { "description": "An autonomous mechanical character in the Star Wars universe", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "The ID of the droid", "isDeprecated": false, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "What others call this droid", "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "This droid's friends, or an empty list if they have none", "isDeprecated": false, "name": "friends", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "INTERFACE", "name": "Character", "ofType": null } } }, { "args": [ { "defaultValue": null, "description": null, "name": "first", "type": { "kind": "SCALAR", "name": "Int", "ofType": null } }, { "defaultValue": null, "description": null, "name": "after", "type": { "kind": "SCALAR", "name": "ID", "ofType": null } } ], "deprecationReason": null, "description": "The friends of the droid exposed as a connection with edges", "isDeprecated": false, "name": "friendsConnection", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "FriendsConnection", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "The movies this droid appears in", "isDeprecated": false, "name": "appearsIn", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "Episode", "ofType": null } } } } }, { "args": [], "deprecationReason": null, "description": "This droid's primary function", "isDeprecated": false, "name": "primaryFunction", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [ { "kind": "INTERFACE", "name": "Character", "ofType": null } ], "kind": "OBJECT", "name": "Droid", "possibleTypes": null }, { "description": "The episodes in the Star Wars trilogy", "enumValues": [ { "deprecationReason": null, "description": "Star Wars Episode IV: A New Hope, released in 1977.", "isDeprecated": false, "name": "NEWHOPE" }, { "deprecationReason": null, "description": "Star Wars Episode V: The Empire Strikes Back, released in 1980.", "isDeprecated": false, "name": "EMPIRE" }, { "deprecationReason": null, "description": "Star Wars Episode VI: Return of the Jedi, released in 1983.", "isDeprecated": false, "name": "JEDI" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", "name": "Episode", "possibleTypes": null }, { "description": "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "Float", "possibleTypes": null }, { "description": "A connection object for a character's friends", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "The total number of friends", "isDeprecated": false, "name": "totalCount", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Int", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "The edges for each of the character's friends.", "isDeprecated": false, "name": "edges", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "OBJECT", "name": "FriendsEdge", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "A list of the friends, as a convenience when edges are not needed.", "isDeprecated": false, "name": "friends", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "INTERFACE", "name": "Character", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "Information for paginating this connection", "isDeprecated": false, "name": "pageInfo", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "PageInfo", "ofType": null } } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "FriendsConnection", "possibleTypes": null }, { "description": "An edge object for a character's friends", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "A cursor used for pagination", "isDeprecated": false, "name": "cursor", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "The character represented by this friendship edge", "isDeprecated": false, "name": "node", "type": { "kind": "INTERFACE", "name": "Character", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "FriendsEdge", "possibleTypes": null }, { "description": "A humanoid creature from the Star Wars universe", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "The ID of the human", "isDeprecated": false, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "What this human calls themselves", "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [ { "defaultValue": "METER", "description": null, "name": "unit", "type": { "kind": "ENUM", "name": "LengthUnit", "ofType": null } } ], "deprecationReason": null, "description": "Height in the preferred unit, default is meters", "isDeprecated": false, "name": "height", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "Mass in kilograms, or null if unknown", "isDeprecated": false, "name": "mass", "type": { "kind": "SCALAR", "name": "Float", "ofType": null } }, { "args": [], "deprecationReason": null, "description": "This human's friends, or an empty list if they have none", "isDeprecated": false, "name": "friends", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "INTERFACE", "name": "Character", "ofType": null } } }, { "args": [ { "defaultValue": null, "description": null, "name": "first", "type": { "kind": "SCALAR", "name": "Int", "ofType": null } }, { "defaultValue": null, "description": null, "name": "after", "type": { "kind": "SCALAR", "name": "ID", "ofType": null } } ], "deprecationReason": null, "description": "The friends of the human exposed as a connection with edges", "isDeprecated": false, "name": "friendsConnection", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "FriendsConnection", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "The movies this human appears in", "isDeprecated": false, "name": "appearsIn", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "Episode", "ofType": null } } } } }, { "args": [], "deprecationReason": null, "description": "A list of starships this person has piloted, or an empty list if none", "isDeprecated": false, "name": "starships", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "OBJECT", "name": "Starship", "ofType": null } } } ], "inputFields": null, "interfaces": [ { "kind": "INTERFACE", "name": "Character", "ofType": null } ], "kind": "OBJECT", "name": "Human", "possibleTypes": null }, { "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "ID", "possibleTypes": null }, { "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "Int", "possibleTypes": null }, { "description": "Units of height", "enumValues": [ { "deprecationReason": null, "description": "The standard unit around the world", "isDeprecated": false, "name": "METER" }, { "deprecationReason": null, "description": "Primarily used in the United States", "isDeprecated": false, "name": "FOOT" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", "name": "LengthUnit", "possibleTypes": null }, { "description": "The mutation type, represents all updates we can make to our data", "enumValues": null, "fields": [ { "args": [ { "defaultValue": null, "description": null, "name": "episode", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "Episode", "ofType": null } } }, { "defaultValue": null, "description": null, "name": "review", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "INPUT_OBJECT", "name": "ReviewInput", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "createReview", "type": { "kind": "OBJECT", "name": "Review", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "Mutation", "possibleTypes": null }, { "description": "Information for paginating this connection", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "startCursor", "type": { "kind": "SCALAR", "name": "ID", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "endCursor", "type": { "kind": "SCALAR", "name": "ID", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "hasNextPage", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "PageInfo", "possibleTypes": null }, { "description": "The query type, represents all of the entry points into our object graph", "enumValues": null, "fields": [ { "args": [ { "defaultValue": "NEWHOPE", "description": null, "name": "episode", "type": { "kind": "ENUM", "name": "Episode", "ofType": null } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "hero", "type": { "kind": "INTERFACE", "name": "Character", "ofType": null } }, { "args": [ { "defaultValue": null, "description": null, "name": "episode", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "Episode", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "reviews", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "OBJECT", "name": "Review", "ofType": null } } } }, { "args": [ { "defaultValue": null, "description": null, "name": "text", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "search", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "UNION", "name": "SearchResult", "ofType": null } } } }, { "args": [ { "defaultValue": null, "description": null, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "character", "type": { "kind": "INTERFACE", "name": "Character", "ofType": null } }, { "args": [ { "defaultValue": null, "description": null, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "droid", "type": { "kind": "OBJECT", "name": "Droid", "ofType": null } }, { "args": [ { "defaultValue": null, "description": null, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "human", "type": { "kind": "OBJECT", "name": "Human", "ofType": null } }, { "args": [ { "defaultValue": null, "description": null, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "starship", "type": { "kind": "OBJECT", "name": "Starship", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "Query", "possibleTypes": null }, { "description": "Represents a review for a movie", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "The number of stars this review gave, 1-5", "isDeprecated": false, "name": "stars", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Int", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "Comment about the movie", "isDeprecated": false, "name": "commentary", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "Review", "possibleTypes": null }, { "description": "The input object sent when someone is creating a new review", "enumValues": null, "fields": null, "inputFields": [ { "defaultValue": null, "description": "0-5 stars", "name": "stars", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Int", "ofType": null } } }, { "defaultValue": null, "description": "Comment about the movie, optional", "name": "commentary", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "interfaces": null, "kind": "INPUT_OBJECT", "name": "ReviewInput", "possibleTypes": null }, { "description": null, "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "UNION", "name": "SearchResult", "possibleTypes": [ { "kind": "OBJECT", "name": "Human", "ofType": null }, { "kind": "OBJECT", "name": "Droid", "ofType": null }, { "kind": "OBJECT", "name": "Starship", "ofType": null } ] }, { "description": null, "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "The ID of the starship", "isDeprecated": false, "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "The name of the starship", "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [ { "defaultValue": "METER", "description": null, "name": "unit", "type": { "kind": "ENUM", "name": "LengthUnit", "ofType": null } } ], "deprecationReason": null, "description": "Length of the starship, along the longest axis", "isDeprecated": false, "name": "length", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "Starship", "possibleTypes": null }, { "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, "kind": "SCALAR", "name": "String", "possibleTypes": null }, { "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior\nin ways field arguments will not suffice, such as conditionally including or\nskipping a field. Directives provide this by describing additional information\nto the executor.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "locations", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "__DirectiveLocation", "ofType": null } } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "args", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } } } } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__Directive", "possibleTypes": null }, { "description": "A Directive can be adjacent to many parts of the GraphQL language, a\n__DirectiveLocation describes one such possible adjacencies.", "enumValues": [ { "deprecationReason": null, "description": "Location adjacent to a query operation.", "isDeprecated": false, "name": "QUERY" }, { "deprecationReason": null, "description": "Location adjacent to a mutation operation.", "isDeprecated": false, "name": "MUTATION" }, { "deprecationReason": null, "description": "Location adjacent to a subscription operation.", "isDeprecated": false, "name": "SUBSCRIPTION" }, { "deprecationReason": null, "description": "Location adjacent to a field.", "isDeprecated": false, "name": "FIELD" }, { "deprecationReason": null, "description": "Location adjacent to a fragment definition.", "isDeprecated": false, "name": "FRAGMENT_DEFINITION" }, { "deprecationReason": null, "description": "Location adjacent to a fragment spread.", "isDeprecated": false, "name": "FRAGMENT_SPREAD" }, { "deprecationReason": null, "description": "Location adjacent to an inline fragment.", "isDeprecated": false, "name": "INLINE_FRAGMENT" }, { "deprecationReason": null, "description": "Location adjacent to a schema definition.", "isDeprecated": false, "name": "SCHEMA" }, { "deprecationReason": null, "description": "Location adjacent to a scalar definition.", "isDeprecated": false, "name": "SCALAR" }, { "deprecationReason": null, "description": "Location adjacent to an object type definition.", "isDeprecated": false, "name": "OBJECT" }, { "deprecationReason": null, "description": "Location adjacent to a field definition.", "isDeprecated": false, "name": "FIELD_DEFINITION" }, { "deprecationReason": null, "description": "Location adjacent to an argument definition.", "isDeprecated": false, "name": "ARGUMENT_DEFINITION" }, { "deprecationReason": null, "description": "Location adjacent to an interface definition.", "isDeprecated": false, "name": "INTERFACE" }, { "deprecationReason": null, "description": "Location adjacent to a union definition.", "isDeprecated": false, "name": "UNION" }, { "deprecationReason": null, "description": "Location adjacent to an enum definition.", "isDeprecated": false, "name": "ENUM" }, { "deprecationReason": null, "description": "Location adjacent to an enum value definition.", "isDeprecated": false, "name": "ENUM_VALUE" }, { "deprecationReason": null, "description": "Location adjacent to an input object type definition.", "isDeprecated": false, "name": "INPUT_OBJECT" }, { "deprecationReason": null, "description": "Location adjacent to an input object field definition.", "isDeprecated": false, "name": "INPUT_FIELD_DEFINITION" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", "name": "__DirectiveLocation", "possibleTypes": null }, { "description": "One possible value for a given Enum. Enum values are unique values, not a\nplaceholder for a string or numeric value. However an Enum value is returned in\na JSON response as a string.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "isDeprecated", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "deprecationReason", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__EnumValue", "possibleTypes": null }, { "description": "Object and Interface types are described by a list of Fields, each of which has\na name, potentially a list of arguments, and a return type.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "args", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "type", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "isDeprecated", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "deprecationReason", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__Field", "possibleTypes": null }, { "description": "Arguments provided to Fields or Directives and the input fields of an\nInputObject are represented as Input Values which describe their type and\noptionally a default value.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "type", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "A GraphQL-formatted string representing the default value for this input value.", "isDeprecated": false, "name": "defaultValue", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "isDeprecated", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "deprecationReason", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__InputValue", "possibleTypes": null }, { "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all\navailable types and directives on the server, as well as the entry points for\nquery, mutation, and subscription operations.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": "A list of all types supported by this server.", "isDeprecated": false, "name": "types", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } } } }, { "args": [], "deprecationReason": null, "description": "The type that query operations will be rooted at.", "isDeprecated": false, "name": "queryType", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": "If this server supports mutation, the type that mutation operations will be rooted at.", "isDeprecated": false, "name": "mutationType", "type": { "kind": "OBJECT", "name": "__Type", "ofType": null } }, { "args": [], "deprecationReason": null, "description": "If this server support subscription, the type that subscription operations will be rooted at.", "isDeprecated": false, "name": "subscriptionType", "type": { "kind": "OBJECT", "name": "__Type", "ofType": null } }, { "args": [], "deprecationReason": null, "description": "A list of all directives supported by this server.", "isDeprecated": false, "name": "directives", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Directive", "ofType": null } } } } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__Schema", "possibleTypes": null }, { "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of\ntypes in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that\ntype. Scalar types provide no information beyond a name and description, while\nEnum types provide their values. Object and Interface types provide the fields\nthey describe. Abstract types, Union and Interface, provide the Object types\npossible at runtime. List and NonNull types compose other types.", "enumValues": null, "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "kind", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", "name": "__TypeKind", "ofType": null } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "name", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "description", "type": { "kind": "SCALAR", "name": "String", "ofType": null } }, { "args": [ { "defaultValue": "false", "description": null, "name": "includeDeprecated", "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "fields", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Field", "ofType": null } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "interfaces", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "possibleTypes", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } } } }, { "args": [ { "defaultValue": "false", "description": null, "name": "includeDeprecated", "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null } } ], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "enumValues", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__EnumValue", "ofType": null } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "inputFields", "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } } } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "ofType", "type": { "kind": "OBJECT", "name": "__Type", "ofType": null } }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, "name": "specifiedByURL", "type": { "kind": "SCALAR", "name": "String", "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", "name": "__Type", "possibleTypes": null }, { "description": "An enum describing what kind of type a given `__Type` is.", "enumValues": [ { "deprecationReason": null, "description": "Indicates this type is a scalar.", "isDeprecated": false, "name": "SCALAR" }, { "deprecationReason": null, "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", "isDeprecated": false, "name": "OBJECT" }, { "deprecationReason": null, "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", "isDeprecated": false, "name": "INTERFACE" }, { "deprecationReason": null, "description": "Indicates this type is a union. `possibleTypes` is a valid field.", "isDeprecated": false, "name": "UNION" }, { "deprecationReason": null, "description": "Indicates this type is an enum. `enumValues` is a valid field.", "isDeprecated": false, "name": "ENUM" }, { "deprecationReason": null, "description": "Indicates this type is an input object. `inputFields` is a valid field.", "isDeprecated": false, "name": "INPUT_OBJECT" }, { "deprecationReason": null, "description": "Indicates this type is a list. `ofType` is a valid field.", "isDeprecated": false, "name": "LIST" }, { "deprecationReason": null, "description": "Indicates this type is a non-null. `ofType` is a valid field.", "isDeprecated": false, "name": "NON_NULL" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", "name": "__TypeKind", "possibleTypes": null } ] } } graphql-go-1.6.0/example/starwars/server/000077500000000000000000000000001475633407000203775ustar00rootroot00000000000000graphql-go-1.6.0/example/starwars/server/server.go000066400000000000000000000033501475633407000222350ustar00rootroot00000000000000package main import ( "log" "net/http" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" "github.com/graph-gophers/graphql-go/relay" ) var schema *graphql.Schema func init() { schema = graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}) } func main() { http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write(page) })) http.Handle("/query", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":8080", nil)) } var page = []byte(` GraphiQL
Loading...
`) graphql-go-1.6.0/example/starwars/starwars.go000066400000000000000000000352331475633407000212740ustar00rootroot00000000000000// Package starwars provides a example schema and resolver based on Star Wars characters. // // Source: https://github.com/graphql/graphql.github.io/blob/source/site/_core/swapiSchema.js package starwars import ( "encoding/base64" "fmt" "strconv" "strings" graphql "github.com/graph-gophers/graphql-go" ) var Schema = ` schema { query: Query mutation: Mutation } # The query type, represents all of the entry points into our object graph type Query { hero(episode: Episode = NEWHOPE): Character reviews(episode: Episode!): [Review]! search(text: String!): [SearchResult]! character(id: ID!): Character droid(id: ID!): Droid human(id: ID!): Human starship(id: ID!): Starship } # The mutation type, represents all updates we can make to our data type Mutation { createReview(episode: Episode!, review: ReviewInput!): Review } # The episodes in the Star Wars trilogy enum Episode { # Star Wars Episode IV: A New Hope, released in 1977. NEWHOPE # Star Wars Episode V: The Empire Strikes Back, released in 1980. EMPIRE # Star Wars Episode VI: Return of the Jedi, released in 1983. JEDI } # A character from the Star Wars universe interface Character { # The ID of the character id: ID! # The name of the character name: String! # The friends of the character, or an empty list if they have none friends: [Character] # The friends of the character exposed as a connection with edges friendsConnection(first: Int, after: ID): FriendsConnection! # The movies this character appears in appearsIn: [Episode!]! } # Units of height enum LengthUnit { # The standard unit around the world METER # Primarily used in the United States FOOT } # A humanoid creature from the Star Wars universe type Human implements Character { # The ID of the human id: ID! # What this human calls themselves name: String! # Height in the preferred unit, default is meters height(unit: LengthUnit = METER): Float! # Mass in kilograms, or null if unknown mass: Float # This human's friends, or an empty list if they have none friends: [Character] # The friends of the human exposed as a connection with edges friendsConnection(first: Int, after: ID): FriendsConnection! # The movies this human appears in appearsIn: [Episode!]! # A list of starships this person has piloted, or an empty list if none starships: [Starship] } # An autonomous mechanical character in the Star Wars universe type Droid implements Character { # The ID of the droid id: ID! # What others call this droid name: String! # This droid's friends, or an empty list if they have none friends: [Character] # The friends of the droid exposed as a connection with edges friendsConnection(first: Int, after: ID): FriendsConnection! # The movies this droid appears in appearsIn: [Episode!]! # This droid's primary function primaryFunction: String } # A connection object for a character's friends type FriendsConnection { # The total number of friends totalCount: Int! # The edges for each of the character's friends. edges: [FriendsEdge] # A list of the friends, as a convenience when edges are not needed. friends: [Character] # Information for paginating this connection pageInfo: PageInfo! } # An edge object for a character's friends type FriendsEdge { # A cursor used for pagination cursor: ID! # The character represented by this friendship edge node: Character } # Information for paginating this connection type PageInfo { startCursor: ID endCursor: ID hasNextPage: Boolean! } # Represents a review for a movie type Review { # The number of stars this review gave, 1-5 stars: Int! # Comment about the movie commentary: String } # The input object sent when someone is creating a new review input ReviewInput { # 0-5 stars stars: Int! # Comment about the movie, optional commentary: String } type Starship { # The ID of the starship id: ID! # The name of the starship name: String! # Length of the starship, along the longest axis length(unit: LengthUnit = METER): Float! } union SearchResult = Human | Droid | Starship ` type human struct { ID graphql.ID Name string Friends []graphql.ID AppearsIn []string Height float64 Mass int Starships []graphql.ID } var humans = []*human{ { ID: "1000", Name: "Luke Skywalker", Friends: []graphql.ID{"1002", "1003", "2000", "2001"}, AppearsIn: []string{"NEWHOPE", "EMPIRE", "JEDI"}, Height: 1.72, Mass: 77, Starships: []graphql.ID{"3001", "3003"}, }, { ID: "1001", Name: "Darth Vader", Friends: []graphql.ID{"1004"}, AppearsIn: []string{"NEWHOPE", "EMPIRE", "JEDI"}, Height: 2.02, Mass: 136, Starships: []graphql.ID{"3002"}, }, { ID: "1002", Name: "Han Solo", Friends: []graphql.ID{"1000", "1003", "2001"}, AppearsIn: []string{"NEWHOPE", "EMPIRE", "JEDI"}, Height: 1.8, Mass: 80, Starships: []graphql.ID{"3000", "3003"}, }, { ID: "1003", Name: "Leia Organa", Friends: []graphql.ID{"1000", "1002", "2000", "2001"}, AppearsIn: []string{"NEWHOPE", "EMPIRE", "JEDI"}, Height: 1.5, Mass: 49, }, { ID: "1004", Name: "Wilhuff Tarkin", Friends: []graphql.ID{"1001"}, AppearsIn: []string{"NEWHOPE"}, Height: 1.8, Mass: 0, }, } var humanData = make(map[graphql.ID]*human) func init() { for _, h := range humans { humanData[h.ID] = h } } type droid struct { ID graphql.ID Name string Friends []graphql.ID AppearsIn []string PrimaryFunction string } var droids = []*droid{ { ID: "2000", Name: "C-3PO", Friends: []graphql.ID{"1000", "1002", "1003", "2001"}, AppearsIn: []string{"NEWHOPE", "EMPIRE", "JEDI"}, PrimaryFunction: "Protocol", }, { ID: "2001", Name: "R2-D2", Friends: []graphql.ID{"1000", "1002", "1003"}, AppearsIn: []string{"NEWHOPE", "EMPIRE", "JEDI"}, PrimaryFunction: "Astromech", }, } var droidData = make(map[graphql.ID]*droid) func init() { for _, d := range droids { droidData[d.ID] = d } } type starship struct { ID graphql.ID Name string Length float64 } var starships = []*starship{ { ID: "3000", Name: "Millennium Falcon", Length: 34.37, }, { ID: "3001", Name: "X-Wing", Length: 12.5, }, { ID: "3002", Name: "TIE Advanced x1", Length: 9.2, }, { ID: "3003", Name: "Imperial shuttle", Length: 20, }, } var starshipData = make(map[graphql.ID]*starship) func init() { for _, s := range starships { starshipData[s.ID] = s } } type review struct { stars int32 commentary *string } var reviews = make(map[string][]*review) type Resolver struct{} func (*Resolver) Query() *QueryResolver { return &QueryResolver{} } type QueryResolver struct{} func (r *QueryResolver) Hero(args struct{ Episode string }) *characterResolver { if args.Episode == "EMPIRE" { return &characterResolver{&humanResolver{humanData["1000"]}} } return &characterResolver{&droidResolver{droidData["2001"]}} } func (r *QueryResolver) Reviews(args struct{ Episode string }) []*reviewResolver { var l []*reviewResolver for _, review := range reviews[args.Episode] { l = append(l, &reviewResolver{review}) } return l } func (r *QueryResolver) Search(args struct{ Text string }) []*searchResultResolver { var l []*searchResultResolver for _, h := range humans { if strings.Contains(h.Name, args.Text) { l = append(l, &searchResultResolver{&humanResolver{h}}) } } for _, d := range droids { if strings.Contains(d.Name, args.Text) { l = append(l, &searchResultResolver{&droidResolver{d}}) } } for _, s := range starships { if strings.Contains(s.Name, args.Text) { l = append(l, &searchResultResolver{&starshipResolver{s}}) } } return l } func (r *QueryResolver) Character(args struct{ ID graphql.ID }) *characterResolver { if h := humanData[args.ID]; h != nil { return &characterResolver{&humanResolver{h}} } if d := droidData[args.ID]; d != nil { return &characterResolver{&droidResolver{d}} } return nil } func (r *QueryResolver) Human(args struct{ ID graphql.ID }) *humanResolver { if h := humanData[args.ID]; h != nil { return &humanResolver{h} } return nil } func (r *QueryResolver) Droid(args struct{ ID graphql.ID }) *droidResolver { if d := droidData[args.ID]; d != nil { return &droidResolver{d} } return nil } func (r *QueryResolver) Starship(args struct{ ID graphql.ID }) *starshipResolver { if s := starshipData[args.ID]; s != nil { return &starshipResolver{s} } return nil } func (*Resolver) Mutation() *MutationResolver { return &MutationResolver{} } type MutationResolver struct{} func (r *MutationResolver) CreateReview(args *struct { Episode string Review reviewInput }) *reviewResolver { review := &review{ stars: args.Review.Stars, commentary: args.Review.Commentary, } reviews[args.Episode] = append(reviews[args.Episode], review) return &reviewResolver{review} } type friendsConnectionArgs struct { First *int32 After *graphql.ID } type character interface { ID() graphql.ID Name() string Friends() *[]*characterResolver FriendsConnection(friendsConnectionArgs) (*friendsConnectionResolver, error) AppearsIn() []string } type characterResolver struct { character } func (r *characterResolver) ToHuman() (*humanResolver, bool) { c, ok := r.character.(*humanResolver) return c, ok } func (r *characterResolver) ToDroid() (*droidResolver, bool) { c, ok := r.character.(*droidResolver) return c, ok } type humanResolver struct { h *human } func (r *humanResolver) ID() graphql.ID { return r.h.ID } func (r *humanResolver) Name() string { return r.h.Name } func (r *humanResolver) Height(args struct{ Unit string }) float64 { return convertLength(r.h.Height, args.Unit) } func (r *humanResolver) Mass() *float64 { if r.h.Mass == 0 { return nil } f := float64(r.h.Mass) return &f } func (r *humanResolver) Friends() *[]*characterResolver { return resolveCharacters(r.h.Friends) } func (r *humanResolver) FriendsConnection(args friendsConnectionArgs) (*friendsConnectionResolver, error) { return newFriendsConnectionResolver(r.h.Friends, args) } func (r *humanResolver) AppearsIn() []string { return r.h.AppearsIn } func (r *humanResolver) Starships() *[]*starshipResolver { l := make([]*starshipResolver, len(r.h.Starships)) for i, id := range r.h.Starships { l[i] = &starshipResolver{starshipData[id]} } return &l } type droidResolver struct { d *droid } func (r *droidResolver) ID() graphql.ID { return r.d.ID } func (r *droidResolver) Name() string { return r.d.Name } func (r *droidResolver) Friends() *[]*characterResolver { return resolveCharacters(r.d.Friends) } func (r *droidResolver) FriendsConnection(args friendsConnectionArgs) (*friendsConnectionResolver, error) { return newFriendsConnectionResolver(r.d.Friends, args) } func (r *droidResolver) AppearsIn() []string { return r.d.AppearsIn } func (r *droidResolver) PrimaryFunction() *string { if r.d.PrimaryFunction == "" { return nil } return &r.d.PrimaryFunction } type starshipResolver struct { s *starship } func (r *starshipResolver) ID() graphql.ID { return r.s.ID } func (r *starshipResolver) Name() string { return r.s.Name } func (r *starshipResolver) Length(args struct{ Unit string }) float64 { return convertLength(r.s.Length, args.Unit) } type searchResultResolver struct { result interface{} } func (r *searchResultResolver) ToHuman() (*humanResolver, bool) { res, ok := r.result.(*humanResolver) return res, ok } func (r *searchResultResolver) ToDroid() (*droidResolver, bool) { res, ok := r.result.(*droidResolver) return res, ok } func (r *searchResultResolver) ToStarship() (*starshipResolver, bool) { res, ok := r.result.(*starshipResolver) return res, ok } func convertLength(meters float64, unit string) float64 { switch unit { case "METER": return meters case "FOOT": return meters * 3.28084 default: panic("invalid unit") } } func resolveCharacters(ids []graphql.ID) *[]*characterResolver { var characters []*characterResolver for _, id := range ids { if c := resolveCharacter(id); c != nil { characters = append(characters, c) } } return &characters } func resolveCharacter(id graphql.ID) *characterResolver { if h, ok := humanData[id]; ok { return &characterResolver{&humanResolver{h}} } if d, ok := droidData[id]; ok { return &characterResolver{&droidResolver{d}} } return nil } type reviewResolver struct { r *review } func (r *reviewResolver) Stars() int32 { return r.r.stars } func (r *reviewResolver) Commentary() *string { return r.r.commentary } type friendsConnectionResolver struct { ids []graphql.ID from int to int } func newFriendsConnectionResolver(ids []graphql.ID, args friendsConnectionArgs) (*friendsConnectionResolver, error) { from := 0 if args.After != nil { b, err := base64.StdEncoding.DecodeString(string(*args.After)) if err != nil { return nil, err } i, err := strconv.Atoi(strings.TrimPrefix(string(b), "cursor")) if err != nil { return nil, err } from = i } to := len(ids) if args.First != nil { to = from + int(*args.First) if to > len(ids) { to = len(ids) } } return &friendsConnectionResolver{ ids: ids, from: from, to: to, }, nil } func (r *friendsConnectionResolver) TotalCount() int32 { return int32(len(r.ids)) } func (r *friendsConnectionResolver) Edges() *[]*friendsEdgeResolver { l := make([]*friendsEdgeResolver, r.to-r.from) for i := range l { l[i] = &friendsEdgeResolver{ cursor: encodeCursor(r.from + i), id: r.ids[r.from+i], } } return &l } func (r *friendsConnectionResolver) Friends() *[]*characterResolver { return resolveCharacters(r.ids[r.from:r.to]) } func (r *friendsConnectionResolver) PageInfo() *pageInfoResolver { return &pageInfoResolver{ startCursor: encodeCursor(r.from), endCursor: encodeCursor(r.to - 1), hasNextPage: r.to < len(r.ids), } } func encodeCursor(i int) graphql.ID { return graphql.ID(base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("cursor%d", i+1)))) } type friendsEdgeResolver struct { cursor graphql.ID id graphql.ID } func (r *friendsEdgeResolver) Cursor() graphql.ID { return r.cursor } func (r *friendsEdgeResolver) Node() *characterResolver { return resolveCharacter(r.id) } type pageInfoResolver struct { startCursor graphql.ID endCursor graphql.ID hasNextPage bool } func (r *pageInfoResolver) StartCursor() *graphql.ID { return &r.startCursor } func (r *pageInfoResolver) EndCursor() *graphql.ID { return &r.endCursor } func (r *pageInfoResolver) HasNextPage() bool { return r.hasNextPage } type reviewInput struct { Stars int32 Commentary *string } graphql-go-1.6.0/example_custom_err_test.go000066400000000000000000000046271475633407000210640ustar00rootroot00000000000000package graphql_test import ( "context" "encoding/json" "fmt" "os" "github.com/graph-gophers/graphql-go" ) type product struct { ID graphql.ID Name string } type custErrResolver struct { products map[graphql.ID]*product } func (r *custErrResolver) Product(ctx context.Context, args struct{ ID graphql.ID }) (*productResolver, error) { if p := r.products[args.ID]; p != nil { return &productResolver{p: p}, nil } traceID := "your-trace-id-here" // get trace ID from ctx return nil, &productNotFoundError{Code: "NotFound", Message: "Product not found", TraceID: traceID} } type productResolver struct { p *product } func (r *productResolver) ID() graphql.ID { return r.p.ID } func (r *productResolver) Name() string { return r.p.Name } type productNotFoundError struct { Code string `json:"code"` Message string `json:"message"` TraceID string `json:"traceId"` } func (e productNotFoundError) Error() string { return fmt.Sprintf("error [%s]: %s.", e.Code, e.Message) } // Extensions provides additional error context according to the spec https://spec.graphql.org/October2021/#sel-GAPHRPZCAACCBx6b. func (e productNotFoundError) Extensions() map[string]interface{} { return map[string]interface{}{ "code": e.Code, "message": e.Message, "traceId": e.TraceID, } } // Example_customErrors demonstrates the use of custom errors and error extensions. func Example_customErrors() { var products = []*product{ {ID: "1000", Name: "Product1"}, {ID: "1001", Name: "Product2"}, } resolver := &custErrResolver{ products: map[graphql.ID]*product{}, } for _, p := range products { resolver.products[p.ID] = p } s := ` schema { query: Query } type Query { product(id: ID!): Product! } type Product { id: ID! name: String! } ` schema := graphql.MustParseSchema(s, resolver) query := ` query { product(id: "1007") { id name } } ` res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // { // "errors": [ // { // "message": "error [NotFound]: Product not found.", // "path": [ // "product" // ], // "extensions": { // "code": "NotFound", // "message": "Product not found", // "traceId": "your-trace-id-here" // } // } // ], // "data": null // } } graphql-go-1.6.0/example_input_array_test.go000066400000000000000000000026751475633407000212400ustar00rootroot00000000000000package graphql_test import ( "context" "encoding/json" "os" "github.com/graph-gophers/graphql-go" ) type query struct{} type IntTuple struct { A int32 B int32 } func (*query) Reversed(args struct{ Values []string }) []string { result := make([]string, len(args.Values)) for i, value := range args.Values { for _, v := range value { result[i] = string(v) + result[i] } } return result } func (*query) Sums(args struct{ Values []IntTuple }) []int32 { result := make([]int32, len(args.Values)) for i, value := range args.Values { result[i] = value.A + value.B } return result } // Example_inputArray shows a simple GraphQL schema which defines a custom input type. // Then it executes a query against it passing array arguments. func Example_inputArray() { s := ` input IntTuple { a: Int! b: Int! } type Query { reversed(values: [String!]!): [String!]! sums(values: [IntTuple!]!): [Int!]! } ` schema := graphql.MustParseSchema(s, &query{}) query := ` query{ reversed(values:["hello", "hi"]) sums(values:[{a:2,b:3},{a:-10,b:-1}]) } ` res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // { // "data": { // "reversed": [ // "olleh", // "ih" // ], // "sums": [ // 5, // -11 // ] // } // } } graphql-go-1.6.0/example_nullbool_test.go000066400000000000000000000022751475633407000205250ustar00rootroot00000000000000package graphql_test import ( "context" "encoding/json" "fmt" "os" "github.com/graph-gophers/graphql-go" ) type mutnb struct{} func (*mutnb) Toggle(args struct{ Enabled graphql.NullBool }) string { if !args.Enabled.Set { return "input value was not provided" } else if args.Enabled.Value == nil { return "enabled is 'null'" } return fmt.Sprintf("enabled '%v'", *args.Enabled.Value) } // ExampleNullBool demonstrates how to use nullable Bool type when it is necessary to differentiate between nil and not set. func ExampleNullBool() { const s = ` schema { query: Query mutation: Mutation } type Query{} type Mutation{ toggle(enabled: Boolean): String! } ` schema := graphql.MustParseSchema(s, &mutnb{}) const query = `mutation{ toggle1: toggle() toggle2: toggle(enabled: null) toggle3: toggle(enabled: true) }` res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // { // "data": { // "toggle1": "input value was not provided", // "toggle2": "enabled is 'null'", // "toggle3": "enabled 'true'" // } // } } graphql-go-1.6.0/example_scalar_map_test.go000066400000000000000000000022211475633407000207700ustar00rootroot00000000000000package graphql_test import ( "context" "encoding/json" "fmt" "os" "github.com/graph-gophers/graphql-go" ) type Map map[string]interface{} func (Map) ImplementsGraphQLType(name string) bool { return name == "Map" } func (m *Map) UnmarshalGraphQL(input interface{}) error { val, ok := input.(map[string]interface{}) if !ok { return fmt.Errorf("wrong type") } *m = val return nil } type Args struct { Name string Data Map } type mutation struct{} func (*mutation) Hello(args Args) string { fmt.Println(args) return "Args accepted!" } func Example_customScalarMap() { s := ` scalar Map type Query {} type Mutation { hello( name: String! data: Map! ): String! } ` schema := graphql.MustParseSchema(s, &mutation{}) query := ` mutation { hello(name: "GraphQL", data: { num: 5, code: "example" }) } ` res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // {GraphQL map[code:example num:5]} // { // "data": { // "hello": "Args accepted!" // } // } } graphql-go-1.6.0/example_test.go000066400000000000000000000016511475633407000166140ustar00rootroot00000000000000package graphql_test import ( "context" "encoding/json" "fmt" "os" "github.com/graph-gophers/graphql-go" ) type exampleResolver struct{} func (*exampleResolver) Greet(ctx context.Context, args struct{ Name string }) string { return fmt.Sprintf("Hello, %s!", args.Name) } // Example demonstrates how to parse a GraphQL schema and execute a query against it. func Example() { s := ` schema { query: Query } type Query { greet(name: String!): String! } ` opts := []graphql.SchemaOpt{ // schema options go here } schema := graphql.MustParseSchema(s, &exampleResolver{}, opts...) query := ` query { greet(name: "GraphQL") } ` res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // { // "data": { // "greet": "Hello, GraphQL!" // } // } } graphql-go-1.6.0/example_time_test.go000066400000000000000000000012011475633407000176210ustar00rootroot00000000000000package graphql_test import ( "context" "encoding/json" "os" "time" "github.com/graph-gophers/graphql-go" ) type tquery struct{} func (*tquery) CurrentTime() graphql.Time { return graphql.Time{Time: time.Date(2023, 2, 6, 12, 3, 22, 0, time.UTC)} } func ExampleTime() { const s = ` scalar Time type Query { currentTime: Time! } ` schema := graphql.MustParseSchema(s, &tquery{}) const query = "{ currentTime }" res := schema.Exec(context.Background(), query, "", nil) err := json.NewEncoder(os.Stdout).Encode(res) if err != nil { panic(err) } // output: // {"data":{"currentTime":"2023-02-06T12:03:22Z"}} } graphql-go-1.6.0/examples_test.go000066400000000000000000000224301475633407000167750ustar00rootroot00000000000000package graphql_test import ( "context" "encoding/json" "fmt" "os" "strings" "text/template" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" ) type Resolver struct { post *Post } func (r *Resolver) Post() *Post { return r.post } type Post struct { id graphql.ID title string } func (p *Post) ID() graphql.ID { return p.id } func (p *Post) Title() string { return p.title } func ExampleID() { schemaString := ` schema { query: Query } type Query { post: Post! } type Post { id: ID! title: String! } ` resolver := &Resolver{ post: &Post{ id: graphql.ID("5"), title: "title", }, } schema := graphql.MustParseSchema(schemaString, resolver) query := ` query { post { id title } } ` res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // { // "data": { // "post": { // "id": "5", // "title": "title" // } // } // } } func ExampleMaxDepth() { schema := graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.MaxDepth(3)) // this query has a depth of 4 query := ` query { hero(episode:EMPIRE) { # level 1 name # level 2 friends { name # level 3 friends { id # level 4 - this would exceed the max depth } } } }` res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // { // "errors": [ // { // "message": "Field \"id\" has depth 4 that exceeds max depth 3", // "locations": [ // { // "line": 8, // "column": 12 // } // ] // } // ] // } } func ExampleMaxQueryLength() { schema := graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.MaxQueryLength(50)) // this query has a length of 53 query := `{ hero(episode:EMPIRE) { id name } }` res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // { // "errors": [ // { // "message": "query length 53 exceeds the maximum allowed query length of 50 bytes" // } // ] // } } func ExampleRestrictIntrospection() { allowKey := struct{}{} // only allow introspection if the function below returns true filter := func(ctx context.Context) bool { allow, found := ctx.Value(allowKey).(bool) return found && allow } schema := graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.RestrictIntrospection(filter)) query := `{ __type(name: "Episode") { enumValues { name } } }` cases := []struct { name string ctx context.Context }{ { name: "Empty context", ctx: context.Background(), }, { name: "Introspection forbidden", ctx: context.WithValue(context.Background(), allowKey, false), }, { name: "Introspection allowed", ctx: context.WithValue(context.Background(), allowKey, true), }, } for _, c := range cases { fmt.Println(c.name, "result:") res := schema.Exec(c.ctx, query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } } // output: // Empty context result: // { // "data": {} // } // Introspection forbidden result: // { // "data": {} // } // Introspection allowed result: // { // "data": { // "__type": { // "enumValues": [ // { // "name": "NEWHOPE" // }, // { // "name": "EMPIRE" // }, // { // "name": "JEDI" // } // ] // } // } // } } func ExampleSchema_AST() { schema := graphql.MustParseSchema(starwars.Schema, nil) ast := schema.AST() for _, e := range ast.Enums { fmt.Printf("Enum %q has the following options:\n", e.Name) for _, o := range e.EnumValuesDefinition { fmt.Printf(" - %s\n", o.EnumValue) } } // output: // Enum "Episode" has the following options: // - NEWHOPE // - EMPIRE // - JEDI // Enum "LengthUnit" has the following options: // - METER // - FOOT } func ExampleSchema_AST_generateEnum() { s := ` type Query { currentSeason: Season! } """ Season represents a season of the year. """ enum Season { SPRING SUMMER AUTUMN WINTER } ` gocode := ` {{ $enum := . }} // {{ $enum.Desc }} type {{ $enum.Name }} int const ( {{ range $i, $e := $enum.EnumValuesDefinition }}{{ if ne $i 0 }}{{ printf "\n\t" }}{{ end }} {{- $e.EnumValue | toVar }}{{ if eq $i 0 }} {{ $enum.Name }} = iota{{ end }} {{- end }} ) var {{ $enum.Name | toLower }}Items = [...]string{ {{- range $i, $e := $enum.EnumValuesDefinition }}{{ if ne $i 0 }}{{ printf ", " }}{{ end }} {{- $e.EnumValue | quote }} {{- end -}} } func (s {{ $enum.Name }}) String() string { return {{ $enum.Name | toLower }}Items[s] } func (s *{{ $enum.Name }}) Deserialize(str string) { var found bool for i, v := range {{ $enum.Name | toLower }}Items { if v == str { found = true (*s) = {{ $enum.Name }}(i) } } if !found { panic("invalid value for enum {{ $enum.Name }}: " + str) } } func ({{ $enum.Name }}) ImplementsGraphQLType(name string) bool { return name == {{ $enum.Name | quote }} } func (s *{{ $enum.Name }}) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: s.Deserialize(input) default: err = fmt.Errorf("wrong type for {{ $enum.Name }}: %T", input) } return err } ` funcs := template.FuncMap{ "quote": func(s string) string { return `"` + s + `"` }, "toLower": strings.ToLower, "toVar": func(s string) string { if len(s) == 0 { return s } return strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) }, } tpl, err := template.New("enum").Funcs(funcs).Parse(gocode) if err != nil { panic(err) } opts := []graphql.SchemaOpt{ graphql.UseStringDescriptions(), } schema := graphql.MustParseSchema(s, nil, opts...) ast := schema.AST() seasons := ast.Enums[0] err = tpl.Execute(os.Stdout, seasons) if err != nil { panic(err) } // output: // // Season represents a season of the year. // type Season int // // const ( // Spring Season = iota // Summer // Autumn // Winter // ) // // var seasonItems = [...]string{"SPRING", "SUMMER", "AUTUMN", "WINTER"} // // func (s Season) String() string { return seasonItems[s] } // // func (s *Season) Deserialize(str string) { // var found bool // for i, v := range seasonItems { // if v == str { // found = true // (*s) = Season(i) // } // } // if !found { // panic("invalid value for enum Season: " + str) // } // } // // func (Season) ImplementsGraphQLType(name string) bool { // return name == "Season" // } // // func (s *Season) UnmarshalGraphQL(input interface{}) error { // var err error // switch input := input.(type) { // case string: // s.Deserialize(input) // default: // err = fmt.Errorf("wrong type for Season: %T", input) // } // return err // } } func ExampleUseStringDescriptions() { s := ` schema { query: Query } type Query { post(id: Int!): Post } """ Post represents a blog post. """ type Post { "Unique identifier of the post." id: ID! # The title field has no description. title: String! """ Tags of the post. """ # tags can be empty tags: [String!]! } ` opts := []graphql.SchemaOpt{ graphql.UseStringDescriptions(), } schema := graphql.MustParseSchema(s, nil, opts...) ast := schema.AST() post := ast.Objects[1] fmt.Printf("Field descriptions of the %q type:\n", post.TypeName()) for _, f := range post.Fields { fmt.Printf(" field: %q, description: %q\n", f.Name, f.Desc) } // output: // Field descriptions of the "Post" type: // field: "id", description: "Unique identifier of the post." // field: "title", description: "" // field: "tags", description: "Tags of the post." } // ExampleFieldTag demonstrates the use of the graphql field tag. func Example_resolverFieldTag() { type resolver struct { Hello string HelloUnderscore string `graphql:"_hello"` HelloLower string `graphql:"hello"` HelloTitle string `graphql:"Hello"` HelloUpper string `graphql:"HELLO"` } sdl := ` type Query { _hello: String! hello: String! Hello: String! HELLO: String! }` r := &resolver{ Hello: "This field is not used during query execution!", HelloLower: "Hello, graphql!", HelloTitle: "Hello, GraphQL!", HelloUnderscore: "Hello, _!", HelloUpper: "Hello, GRAPHQL!", } query := ` { _hello hello Hello HELLO } ` schema := graphql.MustParseSchema(sdl, r, graphql.UseFieldResolvers()) res := schema.Exec(context.Background(), query, "", nil) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") err := enc.Encode(res) if err != nil { panic(err) } // output: // { // "data": { // "_hello": "Hello, _!", // "hello": "Hello, graphql!", // "Hello": "Hello, GraphQL!", // "HELLO": "Hello, GRAPHQL!" // } // } } graphql-go-1.6.0/go.mod000066400000000000000000000002671475633407000147030ustar00rootroot00000000000000module github.com/graph-gophers/graphql-go go 1.16 require ( github.com/opentracing/opentracing-go v1.2.0 go.opentelemetry.io/otel v1.6.3 go.opentelemetry.io/otel/trace v1.6.3 ) graphql-go-1.6.0/go.sum000066400000000000000000000043061475633407000147260ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/otel v1.6.3 h1:FLOfo8f9JzFVFVyU+MSRJc2HdEAXQgm7pIv2uFKRSZE= go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= go.opentelemetry.io/otel/trace v1.6.3 h1:IqN4L+5b0mPNjdXIiZ90Ni4Bl5BRkDQywePLWemd9bc= go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= graphql-go-1.6.0/gqltesting/000077500000000000000000000000001475633407000157515ustar00rootroot00000000000000graphql-go-1.6.0/gqltesting/subscriptions.go000066400000000000000000000052161475633407000212130ustar00rootroot00000000000000package gqltesting import ( "bytes" "context" "encoding/json" "strconv" "testing" graphql "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/errors" ) // TestResponse models the expected response type TestResponse struct { Data json.RawMessage Errors []*errors.QueryError } // TestSubscription is a GraphQL test case to be used with RunSubscribe. type TestSubscription struct { Name string Schema *graphql.Schema Query string OperationName string Variables map[string]interface{} ExpectedResults []TestResponse ExpectedErr error } // RunSubscribes runs the given GraphQL subscription test cases as subtests. func RunSubscribes(t *testing.T, tests []*TestSubscription) { for i, test := range tests { if test.Name == "" { test.Name = strconv.Itoa(i + 1) } t.Run(test.Name, func(t *testing.T) { RunSubscribe(t, test) }) } } // RunSubscribe runs a single GraphQL subscription test case. func RunSubscribe(t *testing.T, test *TestSubscription) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() c, err := test.Schema.Subscribe(ctx, test.Query, test.OperationName, test.Variables) if err != nil { if err.Error() != test.ExpectedErr.Error() { t.Fatalf("unexpected error: got %+v, want %+v", err, test.ExpectedErr) } return } var results []*graphql.Response for res := range c { results = append(results, res.(*graphql.Response)) } for i, expected := range test.ExpectedResults { res := results[i] checkErrorStrings(t, expected.Errors, res.Errors) resData, err := res.Data.MarshalJSON() if err != nil { t.Fatal(err) } got, err := formatJSON(resData) if err != nil { t.Fatalf("got: invalid JSON: %s; raw: %s", err, resData) } expectedData, err := expected.Data.MarshalJSON() if err != nil { t.Fatal(err) } want, err := formatJSON(expectedData) if err != nil { t.Fatalf("got: invalid JSON: %s; raw: %s", err, expectedData) } if !bytes.Equal(got, want) { t.Logf("got: %s", got) t.Logf("want: %s", want) t.Fail() } } } func checkErrorStrings(t *testing.T, expected, actual []*errors.QueryError) { expectedCount, actualCount := len(expected), len(actual) if expectedCount != actualCount { t.Fatalf("unexpected number of errors: want %d, got %d", expectedCount, actualCount) } if expectedCount > 0 { for i, want := range expected { got := actual[i] if got.Error() != want.Error() { t.Fatalf("unexpected error: got %+v, want %+v", got, want) } } // Return because we're done checking. return } for _, err := range actual { t.Errorf("unexpected error: '%s'", err) } } graphql-go-1.6.0/gqltesting/testing.go000066400000000000000000000050631475633407000177610ustar00rootroot00000000000000package gqltesting import ( "bytes" "context" "encoding/json" "fmt" "reflect" "sort" "strconv" "testing" graphql "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/errors" ) // Test is a GraphQL test case to be used with RunTest(s). type Test struct { Context context.Context Schema *graphql.Schema Query string OperationName string Variables map[string]interface{} ExpectedResult string ExpectedErrors []*errors.QueryError RawResponse bool } // RunTests runs the given GraphQL test cases as subtests. func RunTests(t *testing.T, tests []*Test) { if len(tests) == 1 { RunTest(t, tests[0]) return } for i, test := range tests { t.Run(strconv.Itoa(i+1), func(t *testing.T) { RunTest(t, test) }) } } // RunTest runs a single GraphQL test case. func RunTest(t *testing.T, test *Test) { if test.Context == nil { test.Context = context.Background() } result := test.Schema.Exec(test.Context, test.Query, test.OperationName, test.Variables) checkErrors(t, test.ExpectedErrors, result.Errors) if test.ExpectedResult == "" { if result.Data != nil { t.Fatalf("got: %s, want: null", result.Data) } return } // Verify JSON to avoid red herring errors. var got []byte if test.RawResponse { value, err := result.Data.MarshalJSON() if err != nil { t.Fatalf("got: unable to marshal JSON response: %s", err) } got = value } else { value, err := formatJSON(result.Data) if err != nil { t.Fatalf("got: invalid JSON: %s", err) } got = value } want, err := formatJSON([]byte(test.ExpectedResult)) if err != nil { t.Fatalf("want: invalid JSON: %s", err) } if !bytes.Equal(got, want) { t.Logf("got: %s", got) t.Logf("want: %s", want) t.Fail() } } func formatJSON(data []byte) ([]byte, error) { var v interface{} if err := json.Unmarshal(data, &v); err != nil { return nil, err } formatted, err := json.Marshal(v) if err != nil { return nil, err } return formatted, nil } func checkErrors(t *testing.T, want, got []*errors.QueryError) { sortErrors(want) sortErrors(got) // Clear the underlying error before the DeepEqual check. It's too // much to ask the tester to include the raw failing error. for _, err := range got { err.Err = nil } if !reflect.DeepEqual(got, want) { t.Fatalf("unexpected error: got %+v, want %+v", got, want) } } func sortErrors(errors []*errors.QueryError) { if len(errors) <= 1 { return } sort.Slice(errors, func(i, j int) bool { return fmt.Sprintf("%s", errors[i].Path) < fmt.Sprintf("%s", errors[j].Path) }) } graphql-go-1.6.0/graphql.go000066400000000000000000000323151475633407000155610ustar00rootroot00000000000000package graphql import ( "context" "encoding/json" "fmt" "time" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/common" "github.com/graph-gophers/graphql-go/internal/exec" "github.com/graph-gophers/graphql-go/internal/exec/resolvable" "github.com/graph-gophers/graphql-go/internal/exec/selected" "github.com/graph-gophers/graphql-go/internal/query" "github.com/graph-gophers/graphql-go/internal/schema" "github.com/graph-gophers/graphql-go/internal/validation" "github.com/graph-gophers/graphql-go/introspection" "github.com/graph-gophers/graphql-go/log" "github.com/graph-gophers/graphql-go/trace/noop" "github.com/graph-gophers/graphql-go/trace/tracer" ) // ParseSchema parses a GraphQL schema and attaches the given root resolver. It returns an error if // the Go type signature of the resolvers does not match the schema. If nil is passed as the // resolver, then the schema can not be executed, but it may be inspected (e.g. with [Schema.ToJSON] or [Schema.AST]). func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (*Schema, error) { s := &Schema{ schema: schema.New(), maxParallelism: 10, tracer: noop.Tracer{}, logger: &log.DefaultLogger{}, panicHandler: &errors.DefaultPanicHandler{}, } for _, opt := range opts { opt(s) } if s.validationTracer == nil { if t, ok := s.tracer.(tracer.ValidationTracer); ok { s.validationTracer = t } else { s.validationTracer = &validationBridgingTracer{tracer: tracer.LegacyNoopValidationTracer{}} //nolint:staticcheck } } if err := schema.Parse(s.schema, schemaString, s.useStringDescriptions); err != nil { return nil, err } if err := s.validateSchema(); err != nil { return nil, err } r, err := resolvable.ApplyResolver(s.schema, resolver, s.useFieldResolvers) if err != nil { return nil, err } s.res = r return s, nil } // MustParseSchema calls ParseSchema and panics on error. func MustParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) *Schema { s, err := ParseSchema(schemaString, resolver, opts...) if err != nil { panic(err) } return s } // Schema represents a GraphQL schema with an optional resolver. type Schema struct { schema *ast.Schema res *resolvable.Schema allowIntrospection func(ctx context.Context) bool maxQueryLength int maxDepth int maxParallelism int tracer tracer.Tracer validationTracer tracer.ValidationTracer logger log.Logger panicHandler errors.PanicHandler useStringDescriptions bool subscribeResolverTimeout time.Duration useFieldResolvers bool } // AST returns the abstract syntax tree of the GraphQL schema definition. // It in turn can be used by other tools such as validators or generators. func (s *Schema) AST() *ast.Schema { return s.schema } // ASTSchema returns the abstract syntax tree of the GraphQL schema definition. // // Deprecated: use [Schema.AST] instead. func (s *Schema) ASTSchema() *ast.Schema { return s.schema } // SchemaOpt is an option to pass to [ParseSchema] or [MustParseSchema]. type SchemaOpt func(*Schema) // UseStringDescriptions enables the usage of double quoted and triple quoted // strings as descriptions as per the [June 2018 spec]. When this is not enabled, // comments are parsed as descriptions instead. // // [June 2018 spec]: https://facebook.github.io/graphql/June2018/ func UseStringDescriptions() SchemaOpt { return func(s *Schema) { s.useStringDescriptions = true } } // UseFieldResolvers specifies whether to use struct fields as resolvers. func UseFieldResolvers() SchemaOpt { return func(s *Schema) { s.useFieldResolvers = true } } // MaxDepth specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking. func MaxDepth(n int) SchemaOpt { return func(s *Schema) { s.maxDepth = n } } // MaxParallelism specifies the maximum number of resolvers per request allowed to run in parallel. The default is 10. func MaxParallelism(n int) SchemaOpt { return func(s *Schema) { s.maxParallelism = n } } // MaxQueryLength specifies the maximum allowed query length in bytes. The default is 0 which disables max length checking. func MaxQueryLength(n int) SchemaOpt { return func(s *Schema) { s.maxQueryLength = n } } // Tracer is used to trace queries and fields. It defaults to [noop.Tracer]. func Tracer(t tracer.Tracer) SchemaOpt { return func(s *Schema) { s.tracer = t } } // ValidationTracer is used to trace validation errors. It defaults to [tracer.LegacyNoopValidationTracer]. // Deprecated: context is needed to support tracing correctly. Use a tracer which implements [tracer.ValidationTracer]. func ValidationTracer(tracer tracer.LegacyValidationTracer) SchemaOpt { //nolint:staticcheck return func(s *Schema) { s.validationTracer = &validationBridgingTracer{tracer: tracer} } } // Logger is used to log panics during query execution. It defaults to [log.DefaultLogger]. func Logger(logger log.Logger) SchemaOpt { return func(s *Schema) { s.logger = logger } } // PanicHandler is used to customize the panic errors during query execution. // It defaults to [errors.DefaultPanicHandler]. func PanicHandler(panicHandler errors.PanicHandler) SchemaOpt { return func(s *Schema) { s.panicHandler = panicHandler } } // RestrictIntrospection accepts a filter func. If this function returns false the introspection is disabled, otherwise it is enabled. // If this option is not provided the introspection is enabled by default. This option is useful for allowing introspection only to admin users, for example: // // filter := func(ctx context.Context) bool { // u, ok := user.FromContext(ctx) // return ok && u.IsAdmin() // } // // Do not use it together with [DisableIntrospection], otherwise the option added last takes precedence. func RestrictIntrospection(fn func(ctx context.Context) bool) SchemaOpt { return func(s *Schema) { s.allowIntrospection = fn } } // DisableIntrospection disables introspection queries. This function is left for backwards compatibility reasons and is just a shorthand for: // // filter := func(context.Context) bool { // return false // } // graphql.RestrictIntrospection(filter) // // Deprecated: use [RestrictIntrospection] filter instead. Do not use it together with [RestrictIntrospection], otherwise the option added last takes precedence. func DisableIntrospection() SchemaOpt { return func(s *Schema) { s.allowIntrospection = func(context.Context) bool { return false } } } // SubscribeResolverTimeout is an option to control the amount of time // we allow for a single subscribe message resolver to complete it's job // before it times out and returns an error to the subscriber. func SubscribeResolverTimeout(timeout time.Duration) SchemaOpt { return func(s *Schema) { s.subscribeResolverTimeout = timeout } } // Response represents a typical response of a GraphQL server. It may be encoded to JSON directly or // it may be further processed to a custom response type, for example to include custom error data. // Errors are intentionally serialized first based on the advice in the [spec]. // // [spec]: https://github.com/facebook/graphql/commit/7b40390d48680b15cb93e02d46ac5eb249689876#diff-757cea6edf0288677a9eea4cfc801d87R107 type Response struct { Errors []*errors.QueryError `json:"errors,omitempty"` Data json.RawMessage `json:"data,omitempty"` Extensions map[string]interface{} `json:"extensions,omitempty"` } // Validate validates the given query with the schema. func (s *Schema) Validate(queryString string) []*errors.QueryError { return s.ValidateWithVariables(queryString, nil) } // ValidateWithVariables validates the given query with the schema and the input variables. func (s *Schema) ValidateWithVariables(queryString string, variables map[string]interface{}) []*errors.QueryError { doc, qErr := query.Parse(queryString) if qErr != nil { return []*errors.QueryError{qErr} } return validation.Validate(s.schema, doc, variables, s.maxDepth) } // Exec executes the given query with the schema's resolver. It panics if the schema was created // without a resolver. If the context get cancelled, no further resolvers will be called and a // the context error will be returned as soon as possible (not immediately). func (s *Schema) Exec(ctx context.Context, queryString string, operationName string, variables map[string]interface{}) *Response { if !s.res.QueryResolver.IsValid() { panic("schema created without resolver, can not exec") } return s.exec(ctx, queryString, operationName, variables, s.res) } func (s *Schema) exec(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, res *resolvable.Schema) *Response { if s.maxQueryLength > 0 && len(queryString) > s.maxQueryLength { return &Response{Errors: []*errors.QueryError{errors.Errorf("query length %d exceeds the maximum allowed query length of %d bytes", len(queryString), s.maxQueryLength)}} } doc, qErr := query.Parse(queryString) if qErr != nil { return &Response{Errors: []*errors.QueryError{qErr}} } validationFinish := s.validationTracer.TraceValidation(ctx) errs := validation.Validate(s.schema, doc, variables, s.maxDepth) validationFinish(errs) if len(errs) != 0 { return &Response{Errors: errs} } op, err := getOperation(doc, operationName) if err != nil { return &Response{Errors: []*errors.QueryError{errors.Errorf("%s", err)}} } // If the optional "operationName" POST parameter is not provided then // use the query's operation name for improved tracing. if operationName == "" { operationName = op.Name.Name } // Subscriptions are not valid in Exec. Use schema.Subscribe() instead. if op.Type == query.Subscription { return &Response{Errors: []*errors.QueryError{{Message: "graphql-ws protocol header is missing"}}} } if op.Type == query.Mutation { if _, ok := s.schema.RootOperationTypes["mutation"]; !ok { return &Response{Errors: []*errors.QueryError{{Message: "no mutations are offered by the schema"}}} } } // Fill in variables with the defaults from the operation if variables == nil { variables = make(map[string]interface{}, len(op.Vars)) } for _, v := range op.Vars { if _, ok := variables[v.Name.Name]; !ok && v.Default != nil { variables[v.Name.Name] = v.Default.Deserialize(nil) } } r := &exec.Request{ Request: selected.Request{ Doc: doc, Vars: variables, Schema: s.schema, AllowIntrospection: s.allowIntrospection == nil || s.allowIntrospection(ctx), // allow introspection by default, i.e. when allowIntrospection is nil }, Limiter: make(chan struct{}, s.maxParallelism), Tracer: s.tracer, Logger: s.logger, PanicHandler: s.panicHandler, } varTypes := make(map[string]*introspection.Type) for _, v := range op.Vars { t, err := common.ResolveType(v.Type, s.schema.Resolve) if err != nil { return &Response{Errors: []*errors.QueryError{err}} } varTypes[v.Name.Name] = introspection.WrapType(t) } traceCtx, finish := s.tracer.TraceQuery(ctx, queryString, operationName, variables, varTypes) data, errs := r.Execute(traceCtx, res, op) finish(errs) return &Response{ Data: data, Errors: errs, } } func (s *Schema) validateSchema() error { // https://graphql.github.io/graphql-spec/June2018/#sec-Root-Operation-Types // > The query root operation type must be provided and must be an Object type. if err := validateRootOp(s.schema, "query", true); err != nil { return err } // > The mutation root operation type is optional; if it is not provided, the service does not support mutations. // > If it is provided, it must be an Object type. if err := validateRootOp(s.schema, "mutation", false); err != nil { return err } // > Similarly, the subscription root operation type is also optional; if it is not provided, the service does not // > support subscriptions. If it is provided, it must be an Object type. if err := validateRootOp(s.schema, "subscription", false); err != nil { return err } return nil } type validationBridgingTracer struct { tracer tracer.LegacyValidationTracer //nolint:staticcheck } func (t *validationBridgingTracer) TraceValidation(context.Context) func([]*errors.QueryError) { return t.tracer.TraceValidation() } func validateRootOp(s *ast.Schema, name string, mandatory bool) error { t, ok := s.RootOperationTypes[name] if !ok { if mandatory { return fmt.Errorf("root operation %q must be defined", name) } return nil } if t.Kind() != "OBJECT" { return fmt.Errorf("root operation %q must be an OBJECT", name) } return nil } func getOperation(document *ast.ExecutableDefinition, operationName string) (*ast.OperationDefinition, error) { if len(document.Operations) == 0 { return nil, fmt.Errorf("no operations in query document") } if operationName == "" { if len(document.Operations) > 1 { return nil, fmt.Errorf("more than one operation in query document and no operation name given") } for _, op := range document.Operations { return op, nil // return the one and only operation } } op := document.Operations.Get(operationName) if op == nil { return nil, fmt.Errorf("no operation with name %q", operationName) } return op, nil } graphql-go-1.6.0/graphql_test.go000066400000000000000000002706531475633407000166310ustar00rootroot00000000000000package graphql_test import ( "context" "encoding/json" "errors" "fmt" "sync" "testing" "time" "github.com/graph-gophers/graphql-go" gqlerrors "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/example/social" "github.com/graph-gophers/graphql-go/example/starwars" "github.com/graph-gophers/graphql-go/gqltesting" "github.com/graph-gophers/graphql-go/introspection" "github.com/graph-gophers/graphql-go/trace/tracer" ) type helloWorldResolver1 struct{} func (r *helloWorldResolver1) Hello() string { return "Hello world!" } type helloWorldResolver2 struct{} func (r *helloWorldResolver2) Hello(ctx context.Context) (string, error) { return "Hello world!", nil } type helloSnakeResolver1 struct{} func (r *helloSnakeResolver1) HelloHTML() string { return "Hello snake!" } func (r *helloSnakeResolver1) SayHello(args struct{ FullName string }) string { return "Hello " + args.FullName + "!" } type helloSnakeResolver2 struct{} func (r *helloSnakeResolver2) HelloHTML(ctx context.Context) (string, error) { return "Hello snake!", nil } func (r *helloSnakeResolver2) SayHello(ctx context.Context, args struct{ FullName string }) (string, error) { return "Hello " + args.FullName + "!", nil } type structFieldResolver struct { Hello string } type theNumberResolver struct { number int32 } func (r *theNumberResolver) TheNumber() int32 { return r.number } func (r *theNumberResolver) ChangeTheNumber(args struct{ NewNumber int32 }) *theNumberResolver { r.number = args.NewNumber return r } type timeResolver struct{} func (r *timeResolver) AddHour(args struct{ Time graphql.Time }) graphql.Time { return graphql.Time{Time: args.Time.Add(time.Hour)} } type echoResolver struct{} func (r *echoResolver) Echo(args struct{ Value *string }) *string { return args.Value } var starwarsSchema = graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}) type ResolverError interface { error Extensions() map[string]interface{} } type resolverNotFoundError struct { Code string `json:"code"` Message string `json:"message"` } func (e resolverNotFoundError) Error() string { return fmt.Sprintf("Error [%s]: %s", e.Code, e.Message) } func (e resolverNotFoundError) Extensions() map[string]interface{} { return map[string]interface{}{ "code": e.Code, "message": e.Message, } } var ( droidNotFoundError = resolverNotFoundError{ Code: "NotFound", Message: "This is not the droid you are looking for", } errQuote = errors.New("bleep bloop") r2d2 = &droidResolver{name: "R2-D2"} c3po = &droidResolver{name: "C-3PO"} notFoundDroid = &droidResolver{err: droidNotFoundError} ) type findDroidsResolver struct{} func (r *findDroidsResolver) FindDroids(ctx context.Context) []*droidResolver { return []*droidResolver{r2d2, notFoundDroid, c3po} } func (r *findDroidsResolver) FindNilDroids(ctx context.Context) *[]*droidResolver { return &[]*droidResolver{r2d2, nil, c3po} } type findDroidOrHumanResolver struct{} func (r *findDroidOrHumanResolver) FindHuman(ctx context.Context) (*string, error) { human := "human" return &human, nil } func (r *findDroidOrHumanResolver) FindDroid(ctx context.Context) (*droidResolver, error) { return nil, notFoundDroid.err } type droidResolver struct { name string err error } func (d *droidResolver) Name() (string, error) { if d.err != nil { return "", d.err } return d.name, nil } func (d *droidResolver) Quotes() ([]string, error) { switch d.name { case r2d2.name: return nil, errQuote case c3po.name: return []string{"We're doomed!", "R2-D2, where are you?"}, nil } return nil, nil } type discussPlanResolver struct{} func (r *discussPlanResolver) DismissVader(ctx context.Context) (string, error) { return "", errors.New("I find your lack of faith disturbing") } func TestHelloWorld(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { hello: String! } `, &helloWorldResolver1{}), Query: ` { hello } `, ExpectedResult: ` { "hello": "Hello world!" } `, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { hello: String! } `, &helloWorldResolver2{}), Query: ` { hello } `, ExpectedResult: ` { "hello": "Hello world!" } `, }, }) } func TestHelloWorldStructFieldResolver(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { hello: String! } `, &structFieldResolver{Hello: "Hello world!"}, graphql.UseFieldResolvers()), Query: ` { hello } `, ExpectedResult: ` { "hello": "Hello world!" } `, }, }) } func TestHelloSnake(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { hello_html: String! } `, &helloSnakeResolver1{}), Query: ` { hello_html } `, ExpectedResult: ` { "hello_html": "Hello snake!" } `, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { hello_html: String! } `, &helloSnakeResolver2{}), Query: ` { hello_html } `, ExpectedResult: ` { "hello_html": "Hello snake!" } `, }, }) } func TestHelloSnakeArguments(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { say_hello(full_name: String!): String! } `, &helloSnakeResolver1{}), Query: ` { say_hello(full_name: "Rob Pike") } `, ExpectedResult: ` { "say_hello": "Hello Rob Pike!" } `, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { say_hello(full_name: String!): String! } `, &helloSnakeResolver2{}), Query: ` { say_hello(full_name: "Rob Pike") } `, ExpectedResult: ` { "say_hello": "Hello Rob Pike!" } `, }, }) } func TestRootOperations_invalidSchema(t *testing.T) { type args struct { Schema string } type want struct { Error string } testTable := map[string]struct { Args args Want want }{ "Empty schema": { Want: want{Error: `root operation "query" must be defined`}, }, "Query declared by schema, but type not present": { Args: args{ Schema: ` schema { query: Query } `, }, Want: want{Error: `graphql: type "Query" not found`}, }, "Query as incorrect type": { Args: args{ Schema: ` schema { query: String } `, }, Want: want{Error: `root operation "query" must be an OBJECT`}, }, "Query with custom name, schema omitted": { Args: args{ Schema: ` type QueryType { hello: String! } `, }, Want: want{Error: `root operation "query" must be defined`}, }, "Mutation as incorrect type": { Args: args{ Schema: ` schema { query: Query mutation: String } type Query { thing: String } `, }, Want: want{Error: `root operation "mutation" must be an OBJECT`}, }, "Mutation declared by schema, but type not present": { Args: args{ Schema: ` schema { query: Query mutation: Mutation } type Query { hello: String! } `, }, Want: want{Error: `graphql: type "Mutation" not found`}, }, } for name, tt := range testTable { tt := tt t.Run(name, func(t *testing.T) { t.Parallel() _, err := graphql.ParseSchema(tt.Args.Schema, nil) if err == nil || err.Error() != tt.Want.Error { t.Logf("got: %v", err) t.Logf("want: %s", tt.Want.Error) t.Fail() } }) } } func TestRootOperations_validSchema(t *testing.T) { type resolver struct { helloSaidResolver helloWorldResolver1 theNumberResolver } gqltesting.RunTests(t, []*gqltesting.Test{ { // Query only, default name with `schema` omitted Schema: graphql.MustParseSchema(` type Query { hello: String! } `, &resolver{}), Query: `{ hello }`, ExpectedResult: `{"hello": "Hello world!"}`, }, { // Query only, default name with `schema` present Schema: graphql.MustParseSchema(` schema { query: Query } type Query { hello: String! } `, &resolver{}), Query: `{ hello }`, ExpectedResult: `{"hello": "Hello world!"}`, }, { // Query only, custom name Schema: graphql.MustParseSchema(` schema { query: QueryType } type QueryType { hello: String! } `, &resolver{}), Query: `{ hello }`, ExpectedResult: `{"hello": "Hello world!"}`, }, { // Query+Mutation, default names with `schema` omitted Schema: graphql.MustParseSchema(` type Query { hello: String! } type Mutation { changeTheNumber(newNumber: Int!): ChangedNumber! } type ChangedNumber { theNumber: Int! } `, &resolver{}), Query: ` mutation { changeTheNumber(newNumber: 1) { theNumber } } `, ExpectedResult: `{"changeTheNumber": {"theNumber": 1}}`, }, { // Query+Mutation, custom names Schema: graphql.MustParseSchema(` schema { query: QueryType mutation: MutationType } type QueryType { hello: String! } type MutationType { changeTheNumber(newNumber: Int!): ChangedNumber! } type ChangedNumber { theNumber: Int! } `, &resolver{}), Query: ` mutation { changeTheNumber(newNumber: 1) { theNumber } } `, ExpectedResult: `{"changeTheNumber": {"theNumber": 1}}`, }, { // Mutation with custom name, schema omitted Schema: graphql.MustParseSchema(` type Query { hello: String! } type MutationType { changeTheNumber(newNumber: Int!): ChangedNumber! } type ChangedNumber { theNumber: Int! } `, &resolver{}), Query: ` mutation { changeTheNumber(newNumber: 1) { theNumber } } `, ExpectedErrors: []*gqlerrors.QueryError{{Message: "no mutations are offered by the schema"}}, }, { // Explicit schema without mutation field Schema: graphql.MustParseSchema(` schema { query: Query } type Query { hello: String! } type Mutation { changeTheNumber(newNumber: Int!): ChangedNumber! } type ChangedNumber { theNumber: Int! } `, &resolver{}), Query: ` mutation { changeTheNumber(newNumber: 1) { theNumber } } `, ExpectedErrors: []*gqlerrors.QueryError{{Message: "no mutations are offered by the schema"}}, }, }) } func TestBasic(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { hero { id name friends { name } } } `, ExpectedResult: ` { "hero": { "id": "2001", "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } `, }, }) } type testEmbeddedStructResolver struct{} func (*testEmbeddedStructResolver) Course() courseResolver { return courseResolver{ CourseMeta: CourseMeta{ Name: "Biology", Timestamps: Timestamps{CreatedAt: "yesterday", UpdatedAt: "today"}, }, Instructor: Instructor{Name: "Socrates"}, } } type courseResolver struct { CourseMeta Instructor Instructor } type CourseMeta struct { Name string Timestamps } type Instructor struct { Name string } type Timestamps struct { CreatedAt string UpdatedAt string } func TestEmbeddedStruct(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { course: Course! } type Course { name: String! createdAt: String! updatedAt: String! instructor: Instructor! } type Instructor { name: String! } `, &testEmbeddedStructResolver{}, graphql.UseFieldResolvers()), Query: ` { course{ name createdAt updatedAt instructor { name } } } `, ExpectedResult: ` { "course": { "name": "Biology", "createdAt": "yesterday", "updatedAt": "today", "instructor": { "name":"Socrates" } } } `, }, }) } type testNilInterfaceResolver struct{} func (r *testNilInterfaceResolver) A() interface{ Z() int32 } { return nil } func (r *testNilInterfaceResolver) B() (interface{ Z() int32 }, error) { return nil, errors.New("x") } func (r *testNilInterfaceResolver) C() (interface{ Z() int32 }, error) { return nil, nil } func TestNilInterface(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { a: T b: T c: T } type T { z: Int! } `, &testNilInterfaceResolver{}), Query: ` { a { z } b { z } c { z } } `, ExpectedResult: ` { "a": null, "b": null, "c": null } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: "x", Path: []interface{}{"b"}, ResolverError: errors.New("x"), }, }, }, }) } func TestErrorPropagationInLists(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { findDroids: [Droid!]! } type Droid { name: String! } `, &findDroidsResolver{}), Query: ` { findDroids { name } } `, ExpectedResult: ` null `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: droidNotFoundError.Error(), Path: []interface{}{"findDroids", 1, "name"}, ResolverError: droidNotFoundError, Extensions: map[string]interface{}{"code": droidNotFoundError.Code, "message": droidNotFoundError.Message}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { findDroids: [Droid]! } type Droid { name: String! } `, &findDroidsResolver{}), Query: ` { findDroids { name } } `, ExpectedResult: ` { "findDroids": [ { "name": "R2-D2" }, null, { "name": "C-3PO" } ] } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: droidNotFoundError.Error(), Path: []interface{}{"findDroids", 1, "name"}, ResolverError: droidNotFoundError, Extensions: map[string]interface{}{"code": droidNotFoundError.Code, "message": droidNotFoundError.Message}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { findNilDroids: [Droid!] } type Droid { name: String! } `, &findDroidsResolver{}), Query: ` { findNilDroids { name } } `, ExpectedResult: ` { "findNilDroids": null } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: `graphql: got nil for non-null "Droid"`, Path: []interface{}{"findNilDroids", 1}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { findNilDroids: [Droid] } type Droid { name: String! } `, &findDroidsResolver{}), Query: ` { findNilDroids { name } } `, ExpectedResult: ` { "findNilDroids": [ { "name": "R2-D2" }, null, { "name": "C-3PO" } ] } `, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { findDroids: [Droid]! } type Droid { quotes: [String!]! } `, &findDroidsResolver{}), Query: ` { findDroids { quotes } } `, ExpectedResult: ` { "findDroids": [ null, { "quotes": [] }, { "quotes": [ "We're doomed!", "R2-D2, where are you?" ] } ] } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: errQuote.Error(), ResolverError: errQuote, Path: []interface{}{"findDroids", 0, "quotes"}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { findNilDroids: [Droid!] } type Droid { name: String! quotes: [String!]! } `, &findDroidsResolver{}), Query: ` { findNilDroids { name quotes } } `, ExpectedResult: ` { "findNilDroids": null } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: errQuote.Error(), ResolverError: errQuote, Path: []interface{}{"findNilDroids", 0, "quotes"}, }, { Message: `graphql: got nil for non-null "Droid"`, Path: []interface{}{"findNilDroids", 1}, }, }, }, }) } func TestErrorWithExtensions(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { FindDroid: Droid! FindHuman: String } type Droid { Name: String! } `, &findDroidOrHumanResolver{}), Query: ` { FindDroid { Name } FindHuman } `, ExpectedResult: ` null `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: droidNotFoundError.Error(), Path: []interface{}{"FindDroid"}, ResolverError: droidNotFoundError, Extensions: map[string]interface{}{"code": droidNotFoundError.Code, "message": droidNotFoundError.Message}, }, }, }, }) } func TestErrorWithNoExtensions(t *testing.T) { t.Parallel() err := errors.New("I find your lack of faith disturbing") gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { DismissVader: String! } `, &discussPlanResolver{}), Query: ` { DismissVader } `, ExpectedResult: ` null `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: err.Error(), Path: []interface{}{"DismissVader"}, ResolverError: err, Extensions: nil, }, }, }, }) } func TestArguments(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { human(id: "1000") { name height } } `, ExpectedResult: ` { "human": { "name": "Luke Skywalker", "height": 1.72 } } `, }, { Schema: starwarsSchema, Query: ` { human(id: "1000") { name height(unit: FOOT) } } `, ExpectedResult: ` { "human": { "name": "Luke Skywalker", "height": 5.6430448 } } `, }, }) } func TestAliases(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { empireHero: hero(episode: EMPIRE) { name } jediHero: hero(episode: JEDI) { name } } `, ExpectedResult: ` { "empireHero": { "name": "Luke Skywalker" }, "jediHero": { "name": "R2-D2" } } `, }, }) } func TestFragments(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { leftComparison: hero(episode: EMPIRE) { ...comparisonFields ...height } rightComparison: hero(episode: JEDI) { ...comparisonFields ...height } } fragment comparisonFields on Character { name appearsIn friends { name } } fragment height on Human { height } `, ExpectedResult: ` { "leftComparison": { "name": "Luke Skywalker", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ], "friends": [ { "name": "Han Solo" }, { "name": "Leia Organa" }, { "name": "C-3PO" }, { "name": "R2-D2" } ], "height": 1.72 }, "rightComparison": { "name": "R2-D2", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ], "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } `, }, { Schema: starwarsSchema, Query: ` query { human(id: "1000") { id mass ...characterInfo } } fragment characterInfo on Character { name ...on Droid { primaryFunction } ...on Human { height } } `, ExpectedResult: ` { "human": { "id": "1000", "mass": 77, "name": "Luke Skywalker", "height": 1.72 } } `, }, }) } func TestVariables(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` query HeroNameAndFriends($episode: Episode) { hero(episode: $episode) { name } } `, Variables: map[string]interface{}{ "episode": "JEDI", }, ExpectedResult: ` { "hero": { "name": "R2-D2" } } `, }, { Schema: starwarsSchema, Query: ` query HeroNameAndFriends($episode: Episode) { hero(episode: $episode) { name } } `, Variables: map[string]interface{}{ "episode": "EMPIRE", }, ExpectedResult: ` { "hero": { "name": "Luke Skywalker" } } `, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { echo(value: String): String } `, &echoResolver{}), Query: ` query Echo($value:String = "default"){ echo(value:$value) } `, ExpectedResult: ` { "echo": "default" } `, }, }) } func TestSkipDirective(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` query Hero($episode: Episode, $withoutFriends: Boolean!) { hero(episode: $episode) { name friends @skip(if: $withoutFriends) { name } } } `, Variables: map[string]interface{}{ "episode": "JEDI", "withoutFriends": true, }, ExpectedResult: ` { "hero": { "name": "R2-D2" } } `, }, { Schema: starwarsSchema, Query: ` query Hero($episode: Episode, $withoutFriends: Boolean!) { hero(episode: $episode) { name friends @skip(if: $withoutFriends) { name } } } `, Variables: map[string]interface{}{ "episode": "JEDI", "withoutFriends": false, }, ExpectedResult: ` { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } `, }, }) } func TestIncludeDirective(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name ...friendsFragment @include(if: $withFriends) } } fragment friendsFragment on Character { friends { name } } `, Variables: map[string]interface{}{ "episode": "JEDI", "withFriends": false, }, ExpectedResult: ` { "hero": { "name": "R2-D2" } } `, }, { Schema: starwarsSchema, Query: ` query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name ...friendsFragment @include(if: $withFriends) } } fragment friendsFragment on Character { friends { name } } `, Variables: map[string]interface{}{ "episode": "JEDI", "withFriends": true, }, ExpectedResult: ` { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } `, }, }) } type testDeprecatedDirectiveResolver struct{} func (r *testDeprecatedDirectiveResolver) A() int32 { return 0 } func (r *testDeprecatedDirectiveResolver) B() int32 { return 0 } func (r *testDeprecatedDirectiveResolver) C() int32 { return 0 } func TestDeprecatedDirective(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { a: Int! b: Int! @deprecated c: Int! @deprecated(reason: "We don't like it") } `, &testDeprecatedDirectiveResolver{}), Query: ` { __type(name: "Query") { fields { name } allFields: fields(includeDeprecated: true) { name isDeprecated deprecationReason } } } `, ExpectedResult: ` { "__type": { "fields": [ { "name": "a" } ], "allFields": [ { "name": "a", "isDeprecated": false, "deprecationReason": null }, { "name": "b", "isDeprecated": true, "deprecationReason": "No longer supported" }, { "name": "c", "isDeprecated": true, "deprecationReason": "We don't like it" } ] } } `, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { } enum Test { A B @deprecated C @deprecated(reason: "We don't like it") } `, &testDeprecatedDirectiveResolver{}), Query: ` { __type(name: "Test") { enumValues { name } allEnumValues: enumValues(includeDeprecated: true) { name isDeprecated deprecationReason } } } `, ExpectedResult: ` { "__type": { "enumValues": [ { "name": "A" } ], "allEnumValues": [ { "name": "A", "isDeprecated": false, "deprecationReason": null }, { "name": "B", "isDeprecated": true, "deprecationReason": "No longer supported" }, { "name": "C", "isDeprecated": true, "deprecationReason": "We don't like it" } ] } } `, }, }) } func TestSpecifiedByDirective(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { } scalar UUID @specifiedBy( url: "https://tools.ietf.org/html/rfc4122" ) `, &struct{}{}), Query: ` query { __type(name: "UUID") { name specifiedByURL } } `, Variables: map[string]interface{}{}, ExpectedResult: ` { "__type": { "name": "UUID", "specifiedByURL": "https://tools.ietf.org/html/rfc4122" } } `, }, }) } type testBadEnumResolver struct{} func (r *testBadEnumResolver) Hero() *testBadEnumCharacterResolver { return &testBadEnumCharacterResolver{} } type testBadEnumCharacterResolver struct{} func (r *testBadEnumCharacterResolver) Name() string { return "Spock" } func (r *testBadEnumCharacterResolver) AppearsIn() []string { return []string{"STAR_TREK"} } func TestUnknownType(t *testing.T) { gqltesting.RunTest(t, &gqltesting.Test{ Schema: starwarsSchema, Query: ` query TypeInfo { __type(name: "unknown-type") { name } } `, ExpectedResult: ` { "__type": null } `, }) } func TestEnums(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ // Valid input enum supplied in query text { Schema: starwarsSchema, Query: ` query HeroForEpisode { hero(episode: EMPIRE) { name } } `, ExpectedResult: ` { "hero": { "name": "Luke Skywalker" } } `, }, // Invalid input enum supplied in query text { Schema: starwarsSchema, Query: ` query HeroForEpisode { hero(episode: WRATH_OF_KHAN) { name } } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: "Argument \"episode\" has invalid value WRATH_OF_KHAN.\nExpected type \"Episode\", found WRATH_OF_KHAN.", Locations: []gqlerrors.Location{{Column: 20, Line: 3}}, Rule: "ArgumentsOfCorrectType", }, }, }, // Valid input enum supplied in variables { Schema: starwarsSchema, Query: ` query HeroForEpisode($episode: Episode!) { hero(episode: $episode) { name } } `, Variables: map[string]interface{}{"episode": "JEDI"}, ExpectedResult: ` { "hero": { "name": "R2-D2" } } `, }, // Invalid input enum supplied in variables { Schema: starwarsSchema, Query: ` query HeroForEpisode($episode: Episode!) { hero(episode: $episode) { name } } `, Variables: map[string]interface{}{"episode": "FINAL_FRONTIER"}, ExpectedErrors: []*gqlerrors.QueryError{ { Message: "Variable \"episode\" has invalid value FINAL_FRONTIER.\nExpected type \"Episode\", found FINAL_FRONTIER.", Locations: []gqlerrors.Location{{Column: 26, Line: 2}}, Rule: "VariablesOfCorrectType", }, }, }, // Valid enum value in response { Schema: starwarsSchema, Query: ` query Hero { hero { name appearsIn } } `, ExpectedResult: ` { "hero": { "name": "R2-D2", "appearsIn": ["NEWHOPE","EMPIRE","JEDI"] } } `, }, // Invalid enum value in response { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { hero: Character } enum Episode { NEWHOPE EMPIRE JEDI } type Character { name: String! appearsIn: [Episode!]! } `, &testBadEnumResolver{}), Query: ` query Hero { hero { name appearsIn } } `, ExpectedResult: `{ "hero": null }`, ExpectedErrors: []*gqlerrors.QueryError{ { Message: "Invalid value STAR_TREK.\nExpected type Episode, found STAR_TREK.", Path: []interface{}{"hero", "appearsIn", 0}, }, }, }, }) } type testDeprecatedArgsResolver struct{} func (r *testDeprecatedArgsResolver) A(args struct{ B *string }) int32 { return 0 } func TestDeprecatedArgs(t *testing.T) { graphql.MustParseSchema(` schema { query: Query } type Query { a(b: String @deprecated): Int! } `, &testDeprecatedArgsResolver{}) } func TestInlineFragments(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` query HeroForEpisode($episode: Episode!) { hero(episode: $episode) { name ... on Droid { primaryFunction } ... on Human { height } } } `, Variables: map[string]interface{}{ "episode": "JEDI", }, ExpectedResult: ` { "hero": { "name": "R2-D2", "primaryFunction": "Astromech" } } `, }, { Schema: starwarsSchema, Query: ` query HeroForEpisode($episode: Episode!) { hero(episode: $episode) { name ... on Droid { primaryFunction } ... on Human { height } } } `, Variables: map[string]interface{}{ "episode": "EMPIRE", }, ExpectedResult: ` { "hero": { "name": "Luke Skywalker", "height": 1.72 } } `, }, { Schema: starwarsSchema, Query: ` query CharacterSearch { search(text: "C-3PO") { ... on Character { name } } } `, ExpectedResult: ` { "search": [ { "name": "C-3PO" } ] } `, }, { Schema: starwarsSchema, Query: ` query CharacterSearch { hero { ... on Character { ... on Human { name } ... on Droid { name } } } } `, ExpectedResult: ` { "hero": { "name": "R2-D2" } } `, }, { Schema: graphql.MustParseSchema(social.Schema, &social.Resolver{}, graphql.UseFieldResolvers()), Query: ` query { admin(id: "0x01") { ... on User { email } ... on Person { name } } } `, ExpectedResult: ` { "admin": { "email": "Albus@hogwarts.com", "name": "Albus Dumbledore" } } `, }, }) } func TestTypeName(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { search(text: "an") { __typename ... on Human { name } ... on Droid { name } ... on Starship { name } } } `, ExpectedResult: ` { "search": [ { "__typename": "Human", "name": "Han Solo" }, { "__typename": "Human", "name": "Leia Organa" }, { "__typename": "Starship", "name": "TIE Advanced x1" } ] } `, }, { Schema: starwarsSchema, Query: ` { human(id: "1000") { __typename name } } `, ExpectedResult: ` { "human": { "__typename": "Human", "name": "Luke Skywalker" } } `, }, { Schema: starwarsSchema, Query: ` { hero { __typename name ... on Character { ...Droid name __typename } } } fragment Droid on Droid { name __typename } `, RawResponse: true, ExpectedResult: `{"hero":{"__typename":"Droid","name":"R2-D2"}}`, }, }) } func TestConnections(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { hero { name friendsConnection { totalCount pageInfo { startCursor endCursor hasNextPage } edges { cursor node { name } } } } } `, ExpectedResult: ` { "hero": { "name": "R2-D2", "friendsConnection": { "totalCount": 3, "pageInfo": { "startCursor": "Y3Vyc29yMQ==", "endCursor": "Y3Vyc29yMw==", "hasNextPage": false }, "edges": [ { "cursor": "Y3Vyc29yMQ==", "node": { "name": "Luke Skywalker" } }, { "cursor": "Y3Vyc29yMg==", "node": { "name": "Han Solo" } }, { "cursor": "Y3Vyc29yMw==", "node": { "name": "Leia Organa" } } ] } } } `, }, { Schema: starwarsSchema, Query: ` { hero { name friendsConnection(first: 1, after: "Y3Vyc29yMQ==") { totalCount pageInfo { startCursor endCursor hasNextPage } edges { cursor node { name } } } }, moreFriends: hero { name friendsConnection(first: 1, after: "Y3Vyc29yMg==") { totalCount pageInfo { startCursor endCursor hasNextPage } edges { cursor node { name } } } } } `, ExpectedResult: ` { "hero": { "name": "R2-D2", "friendsConnection": { "totalCount": 3, "pageInfo": { "startCursor": "Y3Vyc29yMg==", "endCursor": "Y3Vyc29yMg==", "hasNextPage": true }, "edges": [ { "cursor": "Y3Vyc29yMg==", "node": { "name": "Han Solo" } } ] } }, "moreFriends": { "name": "R2-D2", "friendsConnection": { "totalCount": 3, "pageInfo": { "startCursor": "Y3Vyc29yMw==", "endCursor": "Y3Vyc29yMw==", "hasNextPage": false }, "edges": [ { "cursor": "Y3Vyc29yMw==", "node": { "name": "Leia Organa" } } ] } } } `, }, }) } func TestMutation(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { reviews(episode: JEDI) { stars commentary } } `, ExpectedResult: ` { "reviews": [] } `, }, { Schema: starwarsSchema, Query: ` mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { createReview(episode: $ep, review: $review) { stars commentary } } `, Variables: map[string]interface{}{ "ep": "JEDI", "review": map[string]interface{}{ "stars": 5, "commentary": "This is a great movie!", }, }, ExpectedResult: ` { "createReview": { "stars": 5, "commentary": "This is a great movie!" } } `, }, { Schema: starwarsSchema, Query: ` mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { createReview(episode: $ep, review: $review) { stars commentary } } `, Variables: map[string]interface{}{ "ep": "EMPIRE", "review": map[string]interface{}{ "stars": float64(4), }, }, ExpectedResult: ` { "createReview": { "stars": 4, "commentary": null } } `, }, { Schema: starwarsSchema, Query: ` { reviews(episode: JEDI) { stars commentary } } `, ExpectedResult: ` { "reviews": [{ "stars": 5, "commentary": "This is a great movie!" }] } `, }, }) } func TestIntrospection(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { __schema { types { name } } } `, ExpectedResult: ` { "__schema": { "types": [ { "name": "Boolean" }, { "name": "Character" }, { "name": "Droid" }, { "name": "Episode" }, { "name": "Float" }, { "name": "FriendsConnection" }, { "name": "FriendsEdge" }, { "name": "Human" }, { "name": "ID" }, { "name": "Int" }, { "name": "LengthUnit" }, { "name": "Mutation" }, { "name": "PageInfo" }, { "name": "Query" }, { "name": "Review" }, { "name": "ReviewInput" }, { "name": "SearchResult" }, { "name": "Starship" }, { "name": "String" }, { "name": "__Directive" }, { "name": "__DirectiveLocation" }, { "name": "__EnumValue" }, { "name": "__Field" }, { "name": "__InputValue" }, { "name": "__Schema" }, { "name": "__Type" }, { "name": "__TypeKind" } ] } } `, }, { Schema: starwarsSchema, Query: ` { __schema { queryType { name } } } `, ExpectedResult: ` { "__schema": { "queryType": { "name": "Query" } } } `, }, { Schema: starwarsSchema, Query: ` { a: __type(name: "Droid") { name kind interfaces { name } possibleTypes { name } }, b: __type(name: "Character") { name kind interfaces { name } possibleTypes { name } } c: __type(name: "SearchResult") { name kind interfaces { name } possibleTypes { name } } } `, ExpectedResult: ` { "a": { "name": "Droid", "kind": "OBJECT", "interfaces": [ { "name": "Character" } ], "possibleTypes": null }, "b": { "name": "Character", "kind": "INTERFACE", "interfaces": null, "possibleTypes": [ { "name": "Human" }, { "name": "Droid" } ] }, "c": { "name": "SearchResult", "kind": "UNION", "interfaces": null, "possibleTypes": [ { "name": "Human" }, { "name": "Droid" }, { "name": "Starship" } ] } } `, }, { Schema: starwarsSchema, Query: ` { __type(name: "Droid") { name fields { name args { name type { name } defaultValue } type { name kind } } } } `, ExpectedResult: ` { "__type": { "name": "Droid", "fields": [ { "name": "id", "args": [], "type": { "name": null, "kind": "NON_NULL" } }, { "name": "name", "args": [], "type": { "name": null, "kind": "NON_NULL" } }, { "name": "friends", "args": [], "type": { "name": null, "kind": "LIST" } }, { "name": "friendsConnection", "args": [ { "name": "first", "type": { "name": "Int" }, "defaultValue": null }, { "name": "after", "type": { "name": "ID" }, "defaultValue": null } ], "type": { "name": null, "kind": "NON_NULL" } }, { "name": "appearsIn", "args": [], "type": { "name": null, "kind": "NON_NULL" } }, { "name": "primaryFunction", "args": [], "type": { "name": "String", "kind": "SCALAR" } } ] } } `, }, { Schema: starwarsSchema, Query: ` { __type(name: "Episode") { enumValues { name } } } `, ExpectedResult: ` { "__type": { "enumValues": [ { "name": "NEWHOPE" }, { "name": "EMPIRE" }, { "name": "JEDI" } ] } } `, }, { Schema: starwarsSchema, Query: ` { __schema { directives { name description locations args { name description type { kind ofType { kind name } } } } } } `, ExpectedResult: ` { "__schema": { "directives": [ { "name": "deprecated", "description": "Marks an element of a GraphQL schema as no longer supported.", "locations": [ "FIELD_DEFINITION", "ENUM_VALUE", "ARGUMENT_DEFINITION" ], "args": [ { "name": "reason", "description": "Explains why this element was deprecated, usually also including a suggestion\nfor how to access supported similar data. Formatted in\n[Markdown](https://daringfireball.net/projects/markdown/).", "type": { "kind": "SCALAR", "ofType": null } } ] }, { "name": "include", "description": "Directs the executor to include this field or fragment only when the ` + "`" + `if` + "`" + ` argument is true.", "locations": [ "FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT" ], "args": [ { "name": "if", "description": "Included when true.", "type": { "kind": "NON_NULL", "ofType": { "kind": "SCALAR", "name": "Boolean" } } } ] }, { "name": "skip", "description": "Directs the executor to skip this field or fragment when the ` + "`" + `if` + "`" + ` argument is true.", "locations": [ "FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT" ], "args": [ { "name": "if", "description": "Skipped when true.", "type": { "kind": "NON_NULL", "ofType": { "kind": "SCALAR", "name": "Boolean" } } } ] }, { "name": "specifiedBy", "description": "Provides a scalar specification URL for specifying the behavior of custom scalar types.", "locations": [ "SCALAR" ], "args": [ { "name": "url", "description": "The URL should point to a human-readable specification of the data format, serialization, and coercion rules.", "type": { "kind": "NON_NULL", "ofType": { "kind": "SCALAR", "name": "String" } } } ] } ] } } `, }, }) } var starwarsSchemaNoIntrospection = graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}, []graphql.SchemaOpt{graphql.DisableIntrospection()}...) func TestIntrospectionDisableIntrospection(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchemaNoIntrospection, Query: ` { __schema { types { name } } } `, ExpectedResult: ` { } `, }, { Schema: starwarsSchemaNoIntrospection, Query: ` { __schema { queryType { name } } } `, ExpectedResult: ` { } `, }, { Schema: starwarsSchemaNoIntrospection, Query: ` { a: __type(name: "Droid") { name kind interfaces { name } possibleTypes { name } }, b: __type(name: "Character") { name kind interfaces { name } possibleTypes { name } } c: __type(name: "SearchResult") { name kind interfaces { name } possibleTypes { name } } } `, ExpectedResult: ` { } `, }, { Schema: starwarsSchemaNoIntrospection, Query: ` { __type(name: "Droid") { name fields { name args { name type { name } defaultValue } type { name kind } } } } `, ExpectedResult: ` { } `, }, { Schema: starwarsSchemaNoIntrospection, Query: ` { __type(name: "Episode") { enumValues { name } } } `, ExpectedResult: ` { } `, }, { Schema: starwarsSchemaNoIntrospection, Query: ` { __schema { directives { name description locations args { name description type { kind ofType { kind name } } } } } } `, ExpectedResult: ` { } `, }, { Schema: starwarsSchemaNoIntrospection, Query: ` { search(text: "an") { __typename ... on Human { name } ... on Droid { name } ... on Starship { name } } } `, ExpectedResult: ` { "search": [ { "__typename": "Human", "name": "Han Solo" }, { "__typename": "Human", "name": "Leia Organa" }, { "__typename": "Starship", "name": "TIE Advanced x1" } ] } `, }, }) } func TestMutationOrder(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query mutation: Mutation } type Query { theNumber: Int! } type Mutation { changeTheNumber(newNumber: Int!): Query } `, &theNumberResolver{}), Query: ` mutation { first: changeTheNumber(newNumber: 1) { theNumber } second: changeTheNumber(newNumber: 3) { theNumber } third: changeTheNumber(newNumber: 2) { theNumber } } `, ExpectedResult: ` { "first": { "theNumber": 1 }, "second": { "theNumber": 3 }, "third": { "theNumber": 2 } } `, }, }) } func TestTime(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { addHour(time: Time = "2001-02-03T04:05:06Z"): Time! } scalar Time `, &timeResolver{}), Query: ` query($t: Time!) { a: addHour(time: $t) b: addHour } `, Variables: map[string]interface{}{ "t": time.Date(2000, 2, 3, 4, 5, 6, 0, time.UTC), }, ExpectedResult: ` { "a": "2000-02-03T05:05:06Z", "b": "2001-02-03T05:05:06Z" } `, }, }) } type resolverWithUnexportedMethod struct{} func (r *resolverWithUnexportedMethod) changeTheNumber(args struct{ NewNumber int32 }) int32 { //lint:ignore U1000 ingore this for now return args.NewNumber } func TestUnexportedMethod(t *testing.T) { t.Parallel() _, err := graphql.ParseSchema(` schema { mutation: Mutation } type Mutation { changeTheNumber(newNumber: Int!): Int! } `, &resolverWithUnexportedMethod{}) if err == nil { t.Error("error expected") } } type resolverWithUnexportedField struct{} func (r *resolverWithUnexportedField) ChangeTheNumber(args struct{ newNumber int32 }) int32 { return args.newNumber } func TestUnexportedField(t *testing.T) { t.Parallel() _, err := graphql.ParseSchema(` schema { mutation: Mutation } type Mutation { changeTheNumber(newNumber: Int!): Int! } `, &resolverWithUnexportedField{}) if err == nil { t.Error("error expected") } } type StringEnum string const ( EnumOption1 StringEnum = "Option1" EnumOption2 StringEnum = "Option2" ) type IntEnum int const ( IntEnum0 IntEnum = iota IntEnum1 ) func (e IntEnum) String() string { switch int(e) { case 0: return "Int0" case 1: return "Int1" default: return "IntN" } } func (IntEnum) ImplementsGraphQLType(name string) bool { return name == "IntEnum" } func (e *IntEnum) UnmarshalGraphQL(input interface{}) error { if str, ok := input.(string); ok { switch str { case "Int0": *e = IntEnum(0) case "Int1": *e = IntEnum(1) default: *e = IntEnum(-1) } return nil } return fmt.Errorf("wrong type for IntEnum: %T", input) } type inputResolver struct{} func (r *inputResolver) Int(args struct{ Value int32 }) int32 { return args.Value } func (r *inputResolver) Float(args struct{ Value float64 }) float64 { return args.Value } func (r *inputResolver) String(args struct{ Value string }) string { return args.Value } func (r *inputResolver) Boolean(args struct{ Value bool }) bool { return args.Value } func (r *inputResolver) Nullable(args struct{ Value *int32 }) *int32 { return args.Value } func (r *inputResolver) List(args struct{ Value []*struct{ V int32 } }) []int32 { l := make([]int32, len(args.Value)) for i, entry := range args.Value { l[i] = entry.V } return l } func (r *inputResolver) NullableList(args struct{ Value *[]*struct{ V int32 } }) *[]*int32 { if args.Value == nil { return nil } l := make([]*int32, len(*args.Value)) for i, entry := range *args.Value { if entry != nil { l[i] = &entry.V } } return &l } func (r *inputResolver) StringEnumValue(args struct{ Value string }) string { return args.Value } func (r *inputResolver) NullableStringEnumValue(args struct{ Value *string }) *string { return args.Value } func (r *inputResolver) StringEnum(args struct{ Value StringEnum }) StringEnum { return args.Value } func (r *inputResolver) NullableStringEnum(args struct{ Value *StringEnum }) *StringEnum { return args.Value } func (r *inputResolver) IntEnumValue(args struct{ Value string }) string { return args.Value } func (r *inputResolver) NullableIntEnumValue(args struct{ Value *string }) *string { return args.Value } func (r *inputResolver) IntEnum(args struct{ Value IntEnum }) IntEnum { return args.Value } func (r *inputResolver) NullableIntEnum(args struct{ Value *IntEnum }) *IntEnum { return args.Value } type recursive struct { Next *recursive } func (r *inputResolver) Recursive(args struct{ Value *recursive }) int32 { var n int32 v := args.Value for v != nil { v = v.Next n++ } return n } func (r *inputResolver) ID(args struct{ Value graphql.ID }) graphql.ID { return args.Value } func TestInput(t *testing.T) { t.Parallel() coercionSchema := graphql.MustParseSchema(` schema { query: Query } type Query { int(value: Int!): Int! float(value: Float!): Float! string(value: String!): String! boolean(value: Boolean!): Boolean! nullable(value: Int): Int list(value: [Input!]!): [Int!]! nullableList(value: [Input]): [Int] stringEnumValue(value: StringEnum!): StringEnum! nullableStringEnumValue(value: StringEnum): StringEnum stringEnum(value: StringEnum!): StringEnum! nullableStringEnum(value: StringEnum): StringEnum intEnumValue(value: IntEnum!): IntEnum! nullableIntEnumValue(value: IntEnum): IntEnum intEnum(value: IntEnum!): IntEnum! nullableIntEnum(value: IntEnum): IntEnum recursive(value: RecursiveInput): Int! id(value: ID!): ID! } input Input { v: Int! } input RecursiveInput { next: RecursiveInput } enum StringEnum { Option1 Option2 } enum IntEnum { Int0 Int1 } `, &inputResolver{}) gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: coercionSchema, Query: ` { int(value: 42) float1: float(value: 42) float2: float(value: 42.5) string(value: "foo") boolean(value: true) nullable1: nullable(value: 42) nullable2: nullable(value: null) list1: list(value: [{v: 41}, {v: 42}, {v: 43}]) list2: list(value: {v: 42}) nullableList1: nullableList(value: [{v: 41}, null, {v: 43}]) nullableList2: nullableList(value: null) stringEnumValue(value: Option1) nullableStringEnumValue1: nullableStringEnum(value: Option1) nullableStringEnumValue2: nullableStringEnum(value: null) stringEnum(value: Option2) nullableStringEnum1: nullableStringEnum(value: Option2) nullableStringEnum2: nullableStringEnum(value: null) intEnumValue(value: Int1) nullableIntEnumValue1: nullableIntEnumValue(value: Int1) nullableIntEnumValue2: nullableIntEnumValue(value: null) intEnum(value: Int1) nullableIntEnum1: nullableIntEnum(value: Int1) nullableIntEnum2: nullableIntEnum(value: null) recursive(value: {next: {next: {}}}) intID: id(value: 1234) strID: id(value: "1234") } `, ExpectedResult: ` { "int": 42, "float1": 42, "float2": 42.5, "string": "foo", "boolean": true, "nullable1": 42, "nullable2": null, "list1": [41, 42, 43], "list2": [42], "nullableList1": [41, null, 43], "nullableList2": null, "stringEnumValue": "Option1", "nullableStringEnumValue1": "Option1", "nullableStringEnumValue2": null, "stringEnum": "Option2", "nullableStringEnum1": "Option2", "nullableStringEnum2": null, "intEnumValue": "Int1", "nullableIntEnumValue1": "Int1", "nullableIntEnumValue2": null, "intEnum": "Int1", "nullableIntEnum1": "Int1", "nullableIntEnum2": null, "recursive": 3, "intID": "1234", "strID": "1234" } `, }, }) } type inputArgumentsHello struct{} type inputArgumentsScalarMismatch1 struct{} type inputArgumentsScalarMismatch2 struct{} type inputArgumentsObjectMismatch1 struct{} type inputArgumentsObjectMismatch2 struct{} type inputArgumentsObjectMismatch3 struct{} type fieldNameMismatch struct{} type helloInput struct { Name string } type helloOutput struct { Name string } func (*fieldNameMismatch) Hello() helloOutput { return helloOutput{} } type helloInputMismatch struct { World string } func (r *inputArgumentsHello) Hello(args struct{ Input *helloInput }) string { return "Hello " + args.Input.Name + "!" } func (r *inputArgumentsScalarMismatch1) Hello(name string) string { return "Hello " + name + "!" } func (r *inputArgumentsScalarMismatch2) Hello(args struct{ World string }) string { return "Hello " + args.World + "!" } func (r *inputArgumentsObjectMismatch1) Hello(in helloInput) string { return "Hello " + in.Name + "!" } func (r *inputArgumentsObjectMismatch2) Hello(args struct{ Input *helloInputMismatch }) string { return "Hello " + args.Input.World + "!" } func (r *inputArgumentsObjectMismatch3) Hello(args struct{ Input *struct{ Thing string } }) string { return "Hello " + args.Input.Thing + "!" } func TestInputArguments_failSchemaParsing(t *testing.T) { type args struct { Resolver interface{} Schema string Opts []graphql.SchemaOpt } type want struct { Error string } testTable := map[string]struct { Args args Want want }{ "Non-input type used with field arguments": { Args: args{ Resolver: &inputArgumentsHello{}, Schema: ` schema { query: Query } type Query { hello(input: HelloInput): String! } type HelloInput { name: String } `, }, Want: want{Error: "field \"Input\": type of kind OBJECT can not be used as input\n\tused by (*graphql_test.inputArgumentsHello).Hello"}, }, "Missing Args Wrapper for scalar input": { Args: args{ Resolver: &inputArgumentsScalarMismatch1{}, Schema: ` schema { query: Query } type Query { hello(name: String): String! } input HelloInput { name: String } `, }, Want: want{Error: "expected struct or pointer to struct, got string (hint: missing `args struct { ... }` wrapper for field arguments?)\n\tused by (*graphql_test.inputArgumentsScalarMismatch1).Hello"}, }, "Mismatching field name for scalar input": { Args: args{ Resolver: &inputArgumentsScalarMismatch2{}, Schema: ` schema { query: Query } type Query { hello(name: String): String! } `, }, Want: want{Error: "struct { World string } does not define field \"name\" (hint: missing `args struct { ... }` wrapper for field arguments, or missing field on input struct)\n\tused by (*graphql_test.inputArgumentsScalarMismatch2).Hello"}, }, "Missing Args Wrapper for Input type": { Args: args{ Resolver: &inputArgumentsObjectMismatch1{}, Schema: ` schema { query: Query } type Query { hello(input: HelloInput): String! } input HelloInput { name: String } `, }, Want: want{Error: "graphql_test.helloInput does not define field \"input\" (hint: missing `args struct { ... }` wrapper for field arguments, or missing field on input struct)\n\tused by (*graphql_test.inputArgumentsObjectMismatch1).Hello"}, }, "Input struct missing field": { Args: args{ Resolver: &inputArgumentsObjectMismatch2{}, Schema: ` schema { query: Query } type Query { hello(input: HelloInput): String! } input HelloInput { name: String } `, }, Want: want{Error: "field \"Input\": *graphql_test.helloInputMismatch does not define field \"name\" (hint: missing `args struct { ... }` wrapper for field arguments, or missing field on input struct)\n\tused by (*graphql_test.inputArgumentsObjectMismatch2).Hello"}, }, "Inline Input struct missing field": { Args: args{ Resolver: &inputArgumentsObjectMismatch3{}, Schema: ` schema { query: Query } type Query { hello(input: HelloInput): String! } input HelloInput { name: String } `, }, Want: want{Error: "field \"Input\": *struct { Thing string } does not define field \"name\" (hint: missing `args struct { ... }` wrapper for field arguments, or missing field on input struct)\n\tused by (*graphql_test.inputArgumentsObjectMismatch3).Hello"}, }, "Struct field name inclusion": { Args: args{ Resolver: &fieldNameMismatch{}, Opts: []graphql.SchemaOpt{graphql.UseFieldResolvers()}, Schema: ` type Query { hello(): HelloOutput! } type HelloOutput { name: Int } `, }, Want: want{Error: "string is not a pointer\n\tused by (graphql_test.helloOutput).Name\n\tused by (*graphql_test.fieldNameMismatch).Hello"}, }, } for name, tt := range testTable { tt := tt t.Run(name, func(t *testing.T) { t.Parallel() _, err := graphql.ParseSchema(tt.Args.Schema, tt.Args.Resolver, tt.Args.Opts...) if err == nil || err.Error() != tt.Want.Error { t.Log("Schema parsing error mismatch") t.Logf("got: %s", err) t.Logf("exp: %s", tt.Want.Error) t.Fail() } }) } } func TestComposedFragments(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: starwarsSchema, Query: ` { composed: hero(episode: EMPIRE) { name ...friendsNames ...friendsIds } } fragment friendsNames on Character { name friends { name } } fragment friendsIds on Character { name friends { id } } `, ExpectedResult: ` { "composed": { "name": "Luke Skywalker", "friends": [ { "id": "1002", "name": "Han Solo" }, { "id": "1003", "name": "Leia Organa" }, { "id": "2000", "name": "C-3PO" }, { "id": "2001", "name": "R2-D2" } ] } } `, }, }) } var ( errExample = fmt.Errorf("this is an error") nilChildErrorString = `graphql: got nil for non-null "Child"` ) type childResolver struct{} func (r *childResolver) TriggerError() (string, error) { return "This will never be returned to the client", errExample } func (r *childResolver) NoError() string { return "no error" } func (r *childResolver) Child() *childResolver { return &childResolver{} } func (r *childResolver) NilChild() *childResolver { return nil } func TestErrorPropagation(t *testing.T) { t.Parallel() gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { noError: String! triggerError: String! } `, &childResolver{}), Query: ` { noError triggerError } `, ExpectedResult: ` null `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: errExample.Error(), ResolverError: errExample, Path: []interface{}{"triggerError"}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { noError: String! child: Child } type Child { noError: String! triggerError: String! } `, &childResolver{}), Query: ` { noError child { noError triggerError } } `, ExpectedResult: ` { "noError": "no error", "child": null } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: errExample.Error(), ResolverError: errExample, Path: []interface{}{"child", "triggerError"}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { noError: String! child: Child } type Child { noError: String! triggerError: String! child: Child! } `, &childResolver{}), Query: ` { noError child { noError child { noError triggerError } } } `, ExpectedResult: ` { "noError": "no error", "child": null } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: errExample.Error(), ResolverError: errExample, Path: []interface{}{"child", "child", "triggerError"}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { noError: String! child: Child } type Child { noError: String! triggerError: String! child: Child } `, &childResolver{}), Query: ` { noError child { noError child { noError triggerError } } } `, ExpectedResult: ` { "noError": "no error", "child": { "noError": "no error", "child": null } } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: errExample.Error(), ResolverError: errExample, Path: []interface{}{"child", "child", "triggerError"}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { noError: String! child: Child! } type Child { noError: String! nilChild: Child! } `, &childResolver{}), Query: ` { noError child { nilChild { noError } } } `, ExpectedResult: ` null `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: nilChildErrorString, Path: []interface{}{"child", "nilChild"}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { noError: String! child: Child } type Child { noError: String! nilChild: Child! } `, &childResolver{}), Query: ` { noError child { noError nilChild { noError } } } `, ExpectedResult: ` { "noError": "no error", "child": null } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: nilChildErrorString, Path: []interface{}{"child", "nilChild"}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { child: Child } type Child { triggerError: String! child: Child nilChild: Child! } `, &childResolver{}), Query: ` { child { child { triggerError child { nilChild { triggerError } } } } } `, ExpectedResult: ` { "child": { "child": null } } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: nilChildErrorString, Path: []interface{}{"child", "child", "child", "nilChild"}, }, { Message: errExample.Error(), ResolverError: errExample, Path: []interface{}{"child", "child", "triggerError"}, }, }, }, { Schema: graphql.MustParseSchema(` schema { query: Query } type Query { child: Child } type Child { noError: String! child: Child! nilChild: Child! } `, &childResolver{}), Query: ` { child { child { nilChild { noError } } } } `, ExpectedResult: ` { "child": null } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: nilChildErrorString, Path: []interface{}{"child", "child", "nilChild"}, }, }, }, }) } type assertionResolver struct{} func (r *assertionResolver) ToHuman() (*struct{ Name string }, bool) { return &struct{ Name string }{Name: "Luke Skywalker"}, true } type assertionQueryResolver struct{} func (*assertionQueryResolver) Character() *assertionResolver { return &assertionResolver{} } type badAssertionResolver struct{} func (r *badAssertionResolver) ToHuman(ctx context.Context) (*struct{ Name string }, bool) { return &struct{ Name string }{Name: "Luke Skywalker"}, true } type badAssertionQueryResolver struct{} func (*badAssertionQueryResolver) Character() *badAssertionResolver { return &badAssertionResolver{} } func TestTypeAssertions(t *testing.T) { assertionSchema := ` schema { query: Query } type Query { character: Character! } type Human { name: String! } union Character = Human ` query := ` query { character { ... on Human { name } } } ` gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(assertionSchema, &assertionQueryResolver{}, graphql.UseFieldResolvers()), Query: query, ExpectedResult: ` { "character": { "name": "Luke Skywalker" } } `, }, }) } func TestPanicTypeAssertionArguments(t *testing.T) { panicMessage := `*graphql_test.badAssertionResolver does not resolve "Character": method "ToHuman" should't have any arguments used by (*graphql_test.badAssertionQueryResolver).Character` defer func() { r := recover() if r == nil { t.Fatal("expected schema parse to panic") } if r.(error).Error() != panicMessage { t.Logf("got: %s", r) t.Logf("want: %s", panicMessage) t.Fail() } }() schema := ` schema { query: Query } type Query { character: Character! } type Human { name: String! } union Character = Human ` graphql.MustParseSchema(schema, &badAssertionQueryResolver{}, graphql.UseFieldResolvers()) } type ambiguousResolver struct { Name string // ambiguous University } type University struct { Name string // ambiguous } func TestPanicAmbiguity(t *testing.T) { panicMessage := `*graphql_test.ambiguousResolver does not resolve "Query": ambiguous field "name"` defer func() { r := recover() if r == nil { t.Fatal("expected schema parse to panic") } if r.(error).Error() != panicMessage { t.Logf("got: %s", r) t.Logf("want: %s", panicMessage) t.Fail() } }() schema := ` schema { query: Query } type Query { name: String! university: University! } type University { name: String! } ` graphql.MustParseSchema(schema, &ambiguousResolver{}, graphql.UseFieldResolvers()) } func TestSchema_Exec_without_resolver(t *testing.T) { t.Parallel() type args struct { Query string Schema string } type want struct { Panic interface{} } testTable := []struct { Name string Args args Want want }{ { Name: "schema_without_resolver_errors", Args: args{ Query: ` query { hero { id name friends { name } } } `, Schema: starwars.Schema, }, Want: want{Panic: "schema created without resolver, can not exec"}, }, } for _, tt := range testTable { t.Run(tt.Name, func(t *testing.T) { s := graphql.MustParseSchema(tt.Args.Schema, nil) defer func() { r := recover() if r == nil { t.Fatal("expected query to panic") } if r != tt.Want.Panic { t.Logf("got: %s", r) t.Logf("want: %s", tt.Want.Panic) t.Fail() } }() _ = s.Exec(context.Background(), tt.Args.Query, "", map[string]interface{}{}) }) } } type subscriptionsInExecResolver struct{} func (r *subscriptionsInExecResolver) AppUpdated() <-chan string { return make(chan string) } func TestSubscriptions_In_Exec(t *testing.T) { r := &struct { *helloResolver *subscriptionsInExecResolver }{ helloResolver: &helloResolver{}, subscriptionsInExecResolver: &subscriptionsInExecResolver{}, } gqltesting.RunTest(t, &gqltesting.Test{ Schema: graphql.MustParseSchema(` type Query { hello: String! } type Subscription { appUpdated : String! } `, r), Query: ` subscription { appUpdated } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: "graphql-ws protocol header is missing", }, }, }) } type nilPointerReturnValue struct{} func (r *nilPointerReturnValue) Value() *string { return nil } type nilPointerReturnResolver struct{} func (r *nilPointerReturnResolver) PointerReturn() *nilPointerReturnValue { return &nilPointerReturnValue{} } func TestPointerReturnForNonNull(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` type Query { pointerReturn: PointerReturnValue } type PointerReturnValue { value: Hello! } enum Hello { WORLD } `, &nilPointerReturnResolver{}), Query: ` query { pointerReturn { value } } `, ExpectedResult: ` { "pointerReturn": null } `, ExpectedErrors: []*gqlerrors.QueryError{ { Message: `graphql: got nil for non-null "Hello"`, Path: []interface{}{"pointerReturn", "value"}, }, }, }, }) } type nullableInput struct { String graphql.NullString Int graphql.NullInt Bool graphql.NullBool Time graphql.NullTime Float graphql.NullFloat } type nullableResult struct { String string Int string Bool string Time string Float string } type nullableResolver struct { } func (r *nullableResolver) TestNullables(args struct { Input *nullableInput }) nullableResult { var res nullableResult if args.Input.String.Set { if args.Input.String.Value == nil { res.String = "" } else { res.String = *args.Input.String.Value } } if args.Input.Int.Set { if args.Input.Int.Value == nil { res.Int = "" } else { res.Int = fmt.Sprintf("%d", *args.Input.Int.Value) } } if args.Input.Float.Set { if args.Input.Float.Value == nil { res.Float = "" } else { res.Float = fmt.Sprintf("%.2f", *args.Input.Float.Value) } } if args.Input.Bool.Set { if args.Input.Bool.Value == nil { res.Bool = "" } else { res.Bool = fmt.Sprintf("%t", *args.Input.Bool.Value) } } if args.Input.Time.Set { if args.Input.Time.Value == nil { res.Time = "" } else { res.Time = args.Input.Time.Value.Format(time.RFC3339) } } return res } func TestNullable(t *testing.T) { schema := ` scalar Time input MyInput { string: String int: Int float: Float bool: Boolean time: Time } type Result { string: String! int: String! float: String! bool: String! time: String! } type Query { testNullables(input: MyInput): Result! } ` gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(schema, &nullableResolver{}, graphql.UseFieldResolvers()), Query: ` query { testNullables(input: { string: "test" int: 1234 float: 42.42 bool: true time: "2021-01-02T15:04:05Z" }) { string int float bool time } } `, ExpectedResult: ` { "testNullables": { "string": "test", "int": "1234", "float": "42.42", "bool": "true", "time": "2021-01-02T15:04:05Z" } } `, }, { Schema: graphql.MustParseSchema(schema, &nullableResolver{}, graphql.UseFieldResolvers()), Query: ` query { testNullables(input: { string: null int: null float: null bool: null time: null }) { string int float bool time } } `, ExpectedResult: ` { "testNullables": { "string": "", "int": "", "float": "", "bool": "", "time": "" } } `, }, { Schema: graphql.MustParseSchema(schema, &nullableResolver{}, graphql.UseFieldResolvers()), Query: ` query { testNullables(input: {}) { string int float bool time } } `, ExpectedResult: ` { "testNullables": { "string": "", "int": "", "float": "", "bool": "", "time": "" } } `, }, }) } type testTracer struct { mu *sync.Mutex fields []fieldTrace queries []queryTrace } type fieldTrace struct { label string typeName string fieldName string isTrivial bool args map[string]interface{} err *gqlerrors.QueryError } type queryTrace struct { document string opName string variables map[string]interface{} varTypes map[string]*introspection.Type errors []*gqlerrors.QueryError } func (t *testTracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*gqlerrors.QueryError)) { return ctx, func(qe *gqlerrors.QueryError) { t.mu.Lock() defer t.mu.Unlock() ft := fieldTrace{ label: label, typeName: typeName, fieldName: fieldName, isTrivial: trivial, args: args, err: qe, } t.fields = append(t.fields, ft) } } func (t *testTracer) TraceQuery(ctx context.Context, document string, opName string, vars map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*gqlerrors.QueryError)) { return ctx, func(qe []*gqlerrors.QueryError) { t.mu.Lock() defer t.mu.Unlock() qt := queryTrace{ document: document, opName: opName, variables: vars, varTypes: varTypes, errors: qe, } t.queries = append(t.queries, qt) } } var _ tracer.Tracer = (*testTracer)(nil) func TestTracer(t *testing.T) { t.Parallel() tt := &testTracer{mu: &sync.Mutex{}} schema, err := graphql.ParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.Tracer(tt)) if err != nil { t.Fatalf("graphql.ParseSchema: %s", err) } ctx := context.Background() doc := ` query TestTracer($id: ID!) { HanSolo: human(id: $id) { __typename name } } ` opName := "TestTracer" variables := map[string]interface{}{ "id": "1002", } _ = schema.Exec(ctx, doc, opName, variables) tt.mu.Lock() defer tt.mu.Unlock() if len(tt.queries) != 1 { t.Fatalf("expected one query trace, but got %d: %#v", len(tt.queries), tt.queries) } qt := tt.queries[0] if qt.document != doc { t.Errorf("mismatched query trace document:\nwant: %q\ngot : %q", doc, qt.document) } if qt.opName != opName { t.Errorf("mismated query trace operationName:\nwant: %q\ngot : %q", opName, qt.opName) } expectedFieldTraces := []fieldTrace{ {fieldName: "human", typeName: "Query"}, {fieldName: "__typename", typeName: "Human"}, {fieldName: "name", typeName: "Human"}, } checkFieldTraces(t, expectedFieldTraces, tt.fields) } func checkFieldTraces(t *testing.T, want, have []fieldTrace) { if len(want) != len(have) { t.Errorf("mismatched field traces: expected %d but got %d: %#v", len(want), len(have), have) } type comparison struct { want fieldTrace have fieldTrace } m := map[string]comparison{} for _, ft := range want { m[ft.fieldName] = comparison{want: ft} } for _, ft := range have { c := m[ft.fieldName] c.have = ft m[ft.fieldName] = c } for _, c := range m { if err := stringsEqual(c.want.fieldName, c.have.fieldName); err != "" { t.Error("mismatched field name:", err) } if err := stringsEqual(c.want.typeName, c.have.typeName); err != "" { t.Error("mismatched field parent type:", err) } } } func stringsEqual(want, have string) string { if want != have { return fmt.Sprintf("mismatched values:\nwant: %q\nhave: %q", want, have) } return "" } type queryVarResolver struct{} type filterArgs struct { Required string Optional *string } type filterSearchResults struct { Match *string } func (r *queryVarResolver) Search(ctx context.Context, args *struct{ Filter filterArgs }) []filterSearchResults { return []filterSearchResults{} } func TestQueryVariablesValidation(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{{ Schema: graphql.MustParseSchema(` input SearchFilter { required: String! optional: String } type SearchResults { match: String } type Query { search(filter: SearchFilter!): [SearchResults!]! }`, &queryVarResolver{}, graphql.UseFieldResolvers()), Query: ` query { search(filter: {}) { match } }`, ExpectedErrors: []*gqlerrors.QueryError{{ Message: "Argument \"filter\" has invalid value {}.\nIn field \"required\": Expected \"String!\", found null.", Locations: []gqlerrors.Location{{Line: 3, Column: 27}}, Rule: "ArgumentsOfCorrectType", }}, }, { Schema: graphql.MustParseSchema(` input SearchFilter { required: String! optional: String } type SearchResults { match: String } type Query { search(filter: SearchFilter!): [SearchResults!]! }`, &queryVarResolver{}, graphql.UseFieldResolvers()), Query: ` query q($filter: SearchFilter!) { search(filter: $filter) { match } }`, Variables: map[string]interface{}{"filter": map[string]interface{}{}}, ExpectedErrors: []*gqlerrors.QueryError{{ Message: "Variable \"required\" has invalid value null.\nExpected type \"String!\", found null.", Locations: []gqlerrors.Location{{Line: 3, Column: 5}}, Rule: "VariablesOfCorrectType", }}, }}) } type interfaceImplementingInterfaceResolver struct{} type interfaceImplementingInterfaceExample struct { A string B string C bool } func (r *interfaceImplementingInterfaceResolver) Hey() *interfaceImplementingInterfaceExample { return &interfaceImplementingInterfaceExample{ A: "testing", B: "test", C: true, } } func TestInterfaceImplementingInterface(t *testing.T) { gqltesting.RunTests(t, []*gqltesting.Test{{ Schema: graphql.MustParseSchema(` interface A { a: String! } interface B implements A { a: String! b: String! } interface C implements B & A { a: String! b: String! c: Boolean! } type ABC implements C { a: String! b: String! c: Boolean! } type Query { hey: ABC }`, &interfaceImplementingInterfaceResolver{}, graphql.UseFieldResolvers(), graphql.UseFieldResolvers()), Query: `query {hey { a b c }}`, ExpectedResult: ` { "hey": { "a": "testing", "b": "test", "c": true } } `, }}) } func TestCircularFragmentMaxDepth(t *testing.T) { withMaxDepth := graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.MaxDepth(2)) gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: withMaxDepth, Query: ` query { ...X } fragment X on Query { ...Y } fragment Y on Query { ...X } `, ExpectedErrors: []*gqlerrors.QueryError{{ Message: `Cannot spread fragment "X" within itself via "Y".`, Rule: "NoFragmentCyclesRule", Locations: []gqlerrors.Location{ {Line: 7, Column: 20}, {Line: 10, Column: 20}, }, }}, }, }) } func TestMaxQueryLength(t *testing.T) { withMaxQueryLen := graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.MaxQueryLength(75)) gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: withMaxQueryLen, // Query length is 69 bytes Query: ` query { hero(episode: EMPIRE) { name } } `, ExpectedResult: `{"hero":{"name":"Luke Skywalker"}}`, }, { Schema: withMaxQueryLen, Query: ` query HeroForEpisode { hero(episode: WRATH_OF_KHAN) { name } } `, ExpectedErrors: []*gqlerrors.QueryError{{ Message: `query length 91 exceeds the maximum allowed query length of 75 bytes`, }}, }, }) } type RootResolver struct{} type QueryResolver struct{} type MutationResolver struct{} type SubscriptionResolver struct { err error upstream <-chan *helloEventResolver } func (r *RootResolver) Query() *QueryResolver { return &QueryResolver{} } func (r *RootResolver) Mutation() *MutationResolver { return &MutationResolver{} } type helloEventResolver struct { msg string err error } func (r *helloEventResolver) Msg() (string, error) { return r.msg, r.err } func closedHelloEventUpstream(rr ...*helloEventResolver) <-chan *helloEventResolver { c := make(chan *helloEventResolver, len(rr)) for _, r := range rr { c <- r } close(c) return c } func (r *RootResolver) Subscription() *SubscriptionResolver { return &SubscriptionResolver{ upstream: closedHelloEventUpstream( &helloEventResolver{msg: "Hello subscription!"}, &helloEventResolver{err: errors.New("resolver error")}, &helloEventResolver{msg: "Hello again!"}, ), } } func (qr *QueryResolver) Hello() string { return "Hello query!" } func (mr *MutationResolver) Hello() string { return "Hello mutation!" } func (sr *SubscriptionResolver) Hello(ctx context.Context) (chan *helloEventResolver, error) { if sr.err != nil { return nil, sr.err } c := make(chan *helloEventResolver) go func() { for r := range sr.upstream { select { case <-ctx.Done(): close(c) return case c <- r: } } close(c) }() return c, nil } type errRootResolver1 struct { RootResolver } // Query is invalid because it doesn't have a return value. func (*errRootResolver1) Query() {} type errRootResolver2 struct { RootResolver } // Query is invalid because it has more than 1 return value func (*errRootResolver2) Query() (*QueryResolver, error) { return nil, nil } type errRootResolver3 struct { RootResolver } // Mutation is invalid because it returns nil func (*errRootResolver3) Mutation() *MutationResolver { return nil } type errRootResolver4 struct { RootResolver } // Query is invalid because it doesn't return a pointer. func (*errRootResolver4) Query() MutationResolver { return MutationResolver{} } type errRootResolver5 struct { RootResolver } // Query is invalid because it returns *[]int instead of a resolver. func (*errRootResolver5) Query() *[]int { return &[]int{1, 2} } type errRootResolver6 struct { RootResolver } // Mutation is invalid because it returns a map[string]int instead of a resolver. func (*errRootResolver6) Mutation() map[string]int { return map[string]int{"key": 3} } type errRootResolver7 struct { RootResolver } // Subscription is invalid because it returns an invalid resolver. func (*errRootResolver7) Subscription() interface{} { a := struct { Name string }{Name: "invalid"} return &a } type errRootResolver8 struct { RootResolver } // Query is invalid because it accepts arguments. func (*errRootResolver8) Query(ctx context.Context) *QueryResolver { return &QueryResolver{} } // TestSeparateResolvers ensures that a field with the same name is allowed in different operations func TestSeparateResolvers(t *testing.T) { helloEverywhere := ` schema { query: Query mutation: Mutation subscription: Subscription } type Query { hello: String! } type Mutation { hello: String! } type Subscription { hello: HelloEvent! } type HelloEvent { msg: String! } ` separateSchema := graphql.MustParseSchema(helloEverywhere, &RootResolver{}) gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: separateSchema, Query: ` query { hello } `, ExpectedResult: ` { "hello": "Hello query!" } `, }, { Schema: separateSchema, Query: ` mutation { hello } `, ExpectedResult: ` { "hello": "Hello mutation!" } `, }, }) gqltesting.RunSubscribes(t, []*gqltesting.TestSubscription{ { Name: "ok", Schema: separateSchema, Query: ` subscription { hello { msg } } `, ExpectedResults: []gqltesting.TestResponse{ { Data: json.RawMessage(` { "hello": { "msg": "Hello subscription!" } } `), }, { // null propagates all the way up because msg is non-null Data: json.RawMessage(`null`), Errors: []*gqlerrors.QueryError{gqlerrors.Errorf("%s", errResolver)}, }, { Data: json.RawMessage(` { "hello": { "msg": "Hello again!" } } `), }, }, }, }) // test errors with invalid resolvers tests := []struct { name string resolver interface{} opts []graphql.SchemaOpt wantErr string }{ { name: "query_method_has_no_return_val", resolver: &errRootResolver1{}, wantErr: "method \"Query\" of *graphql_test.errRootResolver1 must have 1 return value, got 0", }, { name: "query_method_returns_too_many_vals", resolver: &errRootResolver2{}, wantErr: "method \"Query\" of *graphql_test.errRootResolver2 must have 1 return value, got 2", }, { name: "mutation_method_returns_nil", resolver: &errRootResolver3{}, wantErr: "method \"Mutation\" of *graphql_test.errRootResolver3 must return a non-nil result, got ", }, { name: "query_method_does_not_return_a_pointer", resolver: &errRootResolver4{}, wantErr: "method \"Query\" of *graphql_test.errRootResolver4 must return an interface or a pointer, got graphql_test.MutationResolver", }, { name: "query_method_returns_invalid_resolver_type", resolver: &errRootResolver5{}, wantErr: "*[]int does not resolve \"Query\": missing method for field \"hello\"", }, { name: "mutation_method_returns_invalid_resolver_type", resolver: &errRootResolver6{}, wantErr: "method \"Mutation\" of *graphql_test.errRootResolver6 must return an interface or a pointer, got map[string]int", }, { name: "query_subscription_returns_invalid_resolver_type", resolver: &errRootResolver7{}, wantErr: "*struct { Name string } does not resolve \"Subscription\": missing method for field \"hello\"", }, { name: "mutation_method_returns_invalid_resolver_type", resolver: &errRootResolver8{}, wantErr: "method \"Query\" of *graphql_test.errRootResolver8 must not accept any arguments, got 1", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := graphql.ParseSchema(helloEverywhere, tt.resolver, tt.opts...) if err == nil { t.Fatalf("want err: %q, got: ", tt.wantErr) } if err.Error() != tt.wantErr { t.Fatalf("want err: %q, got: %q", tt.wantErr, err.Error()) } }) } } func TestSchemaExtension(t *testing.T) { t.Parallel() sdl := ` directive @awesome on SCHEMA schema { query: Query } type Query { hello: String! } extend schema @awesome ` schema := graphql.MustParseSchema(sdl, &helloResolver{}) gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: schema, Query: ` { hello } `, ExpectedResult: ` { "hello": "Hello world!" } `, }, }) ast := schema.AST() dirs := ast.SchemaDefinition.Directives if len(dirs) != 1 { t.Fatalf("expected 1 schema directive, got %d", len(dirs)) } name := dirs[0].Name.Name if name != "awesome" { t.Fatalf(`expected an "awesome" schema directive, got %q`, dirs[0].Name.Name) } } func TestGraphqlNames(t *testing.T) { t.Parallel() sdl1 := ` type Query { hello: String! } ` type invalidResolver1 struct { Field1 string `graphql:"hello"` Field2 string `graphql:"hello"` } wantErr := fmt.Errorf(`*graphql_test.invalidResolver1 does not resolve "Query": multiple fields have a graphql reflect tag "hello"`) _, err := graphql.ParseSchema(sdl1, &invalidResolver1{}, graphql.UseFieldResolvers()) if err == nil || err.Error() != wantErr.Error() { t.Fatalf("want err %q, got %q", wantErr, err) } gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(` type Query { _hello: String! hello: String! Hello: String! HELLO: String! }`, func() interface{} { type helloTagResolver struct { Hello string HelloUnderscore string `graphql:"_hello"` HelloLower string `graphql:"hello"` HelloTitle string `graphql:"Hello"` HelloUpper string `graphql:"HELLO"` } return &helloTagResolver{ Hello: "This field will not be used during query execution!", HelloLower: "Hello, graphql!", HelloTitle: "Hello, GraphQL!", HelloUnderscore: "Hello, _!", HelloUpper: "Hello, GRAPHQL!", } }(), graphql.UseFieldResolvers()), Query: ` { _hello hello Hello HELLO } `, ExpectedResult: ` { "_hello": "Hello, _!", "hello": "Hello, graphql!", "Hello": "Hello, GraphQL!", "HELLO": "Hello, GRAPHQL!" } `, }, }) } func Test_fieldFunc(t *testing.T) { sdl := ` type Query { hello(name: String!): String! } ` gqltesting.RunTests(t, []*gqltesting.Test{ { Schema: graphql.MustParseSchema(sdl, func() interface{} { type helloTagResolver struct { Hello func(args struct{ Name string }) string } fn := func(args struct{ Name string }) string { return "Hello, " + args.Name + "!" } return &helloTagResolver{ Hello: fn, } }(), graphql.UseFieldResolvers()), Query: ` { hello(name: "GraphQL") } `, ExpectedResult: ` { "hello": "Hello, GraphQL!" } `, }, { Schema: graphql.MustParseSchema(sdl, func() interface{} { type helloTagResolver struct { Greet func(ctx context.Context, args struct{ Name string }) (string, error) `graphql:"hello"` } fn := func(_ context.Context, args struct{ Name string }) (string, error) { return "Hello, " + args.Name + "!", nil } return &helloTagResolver{ Greet: fn, } }(), graphql.UseFieldResolvers()), Query: ` { hello(name: "GraphQL") } `, ExpectedResult: ` { "hello": "Hello, GraphQL!" } `, }, }) } graphql-go-1.6.0/id.go000066400000000000000000000011061475633407000145110ustar00rootroot00000000000000package graphql import ( "fmt" "strconv" ) // ID represents GraphQL's "ID" scalar type. A custom type may be used instead. type ID string func (ID) ImplementsGraphQLType(name string) bool { return name == "ID" } func (id *ID) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: *id = ID(input) case int32: *id = ID(strconv.Itoa(int(input))) default: err = fmt.Errorf("wrong type for ID: %T", input) } return err } func (id ID) MarshalJSON() ([]byte, error) { return strconv.AppendQuote(nil, string(id)), nil } graphql-go-1.6.0/internal/000077500000000000000000000000001475633407000154045ustar00rootroot00000000000000graphql-go-1.6.0/internal/common/000077500000000000000000000000001475633407000166745ustar00rootroot00000000000000graphql-go-1.6.0/internal/common/blockstring.go000066400000000000000000000056401475633407000215510ustar00rootroot00000000000000// MIT License // // Copyright (c) 2019 GraphQL Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // This implementation has been adapted from the graphql-js reference implementation // https://github.com/graphql/graphql-js/blob/5eb7c4ded7ceb83ac742149cbe0dae07a8af9a30/src/language/blockString.js // which is released under the MIT License above. package common import ( "strings" ) // Produces the value of a block string from its parsed raw value, similar to // CoffeeScript's block string, Python's docstring trim or Ruby's strip_heredoc. // // This implements the GraphQL spec's BlockStringValue() static algorithm. func blockString(raw string) string { lines := strings.Split(raw, "\n") // Remove common indentation from all lines except the first (which has none) ind := blockStringIndentation(lines) if ind > 0 { for i := 1; i < len(lines); i++ { l := lines[i] if len(l) < ind { lines[i] = "" continue } lines[i] = l[ind:] } } // Remove leading and trailing blank lines trimStart := 0 for i := 0; i < len(lines) && isBlank(lines[i]); i++ { trimStart++ } lines = lines[trimStart:] trimEnd := 0 for i := len(lines) - 1; i > 0 && isBlank(lines[i]); i-- { trimEnd++ } lines = lines[:len(lines)-trimEnd] return strings.Join(lines, "\n") } func blockStringIndentation(lines []string) int { var commonIndent *int for i := 1; i < len(lines); i++ { l := lines[i] indent := leadingWhitespace(l) if indent == len(l) { // don't consider blank/empty lines continue } if indent == 0 { return 0 } if commonIndent == nil || indent < *commonIndent { commonIndent = &indent } } if commonIndent == nil { return 0 } return *commonIndent } func isBlank(s string) bool { return len(s) == 0 || leadingWhitespace(s) == len(s) } func leadingWhitespace(s string) int { i := 0 for _, r := range s { if r != '\t' && r != ' ' { break } i++ } return i } graphql-go-1.6.0/internal/common/directive.go000066400000000000000000000006231475633407000212020ustar00rootroot00000000000000package common import "github.com/graph-gophers/graphql-go/ast" func ParseDirectives(l *Lexer) ast.DirectiveList { var directives ast.DirectiveList for l.Peek() == '@' { l.ConsumeToken('@') d := &ast.Directive{} d.Name = l.ConsumeIdentWithLoc() d.Name.Loc.Column-- if l.Peek() == '(' { d.Arguments = ParseArgumentList(l) } directives = append(directives, d) } return directives } graphql-go-1.6.0/internal/common/lexer.go000066400000000000000000000130411475633407000203410ustar00rootroot00000000000000package common import ( "bytes" "fmt" "strconv" "strings" "text/scanner" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" ) type syntaxError string type Lexer struct { sc *scanner.Scanner next rune comment bytes.Buffer useStringDescriptions bool } type Ident struct { Name string Loc errors.Location } func NewLexer(s string, useStringDescriptions bool) *Lexer { sc := &scanner.Scanner{ Mode: scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings, } sc.Init(strings.NewReader(s)) l := Lexer{sc: sc, useStringDescriptions: useStringDescriptions} l.sc.Error = l.CatchScannerError return &l } func (l *Lexer) CatchSyntaxError(f func()) (errRes *errors.QueryError) { defer func() { if err := recover(); err != nil { if err, ok := err.(syntaxError); ok { errRes = errors.Errorf("syntax error: %s", err) errRes.Locations = []errors.Location{l.Location()} return } panic(err) } }() f() return } func (l *Lexer) Peek() rune { return l.next } // ConsumeWhitespace consumes whitespace and tokens equivalent to whitespace (e.g. commas and comments). // // Consumed comment characters will build the description for the next type or field encountered. // The description is available from `DescComment()`, and will be reset every time `ConsumeWhitespace()` is // executed unless l.useStringDescriptions is set. func (l *Lexer) ConsumeWhitespace() { l.comment.Reset() for { l.next = l.sc.Scan() if l.next == ',' { // Similar to white space and line terminators, commas (',') are used to improve the // legibility of source text and separate lexical tokens but are otherwise syntactically and // semantically insignificant within GraphQL documents. // // http://facebook.github.io/graphql/draft/#sec-Insignificant-Commas continue } if l.next == '#' { // GraphQL source documents may contain single-line comments, starting with the '#' marker. // // A comment can contain any Unicode code point except `LineTerminator` so a comment always // consists of all code points starting with the '#' character up to but not including the // line terminator. l.consumeComment() continue } break } } // consumeDescription optionally consumes a description based on the June 2018 graphql spec if any are present. // // Single quote strings are also single line. Triple quote strings can be multi-line. Triple quote strings // whitespace trimmed on both ends. // If a description is found, consume any following comments as well // // http://facebook.github.io/graphql/June2018/#sec-Descriptions func (l *Lexer) consumeDescription() string { // If the next token is not a string, we don't consume it if l.next != scanner.String { return "" } // Triple quote string is an empty "string" followed by an open quote due to the way the parser treats strings as one token var desc string if l.sc.Peek() == '"' { desc = l.consumeTripleQuoteComment() } else { desc = l.consumeStringComment() } l.ConsumeWhitespace() return desc } func (l *Lexer) ConsumeIdent() string { name := l.sc.TokenText() l.ConsumeToken(scanner.Ident) return name } func (l *Lexer) ConsumeIdentWithLoc() ast.Ident { loc := l.Location() name := l.sc.TokenText() l.ConsumeToken(scanner.Ident) return ast.Ident{Name: name, Loc: loc} } func (l *Lexer) ConsumeKeyword(keyword string) { if l.next != scanner.Ident || l.sc.TokenText() != keyword { l.SyntaxError(fmt.Sprintf("unexpected %q, expecting %q", l.sc.TokenText(), keyword)) } l.ConsumeWhitespace() } func (l *Lexer) ConsumeLiteral() *ast.PrimitiveValue { lit := &ast.PrimitiveValue{Type: l.next, Text: l.sc.TokenText()} l.ConsumeWhitespace() return lit } func (l *Lexer) ConsumeToken(expected rune) { if l.next != expected { l.SyntaxError(fmt.Sprintf("unexpected %q, expecting %s", l.sc.TokenText(), scanner.TokenString(expected))) } l.ConsumeWhitespace() } func (l *Lexer) DescComment() string { comment := l.comment.String() desc := l.consumeDescription() if l.useStringDescriptions { return desc } return comment } func (l *Lexer) SyntaxError(message string) { panic(syntaxError(message)) } func (l *Lexer) Location() errors.Location { return errors.Location{ Line: l.sc.Line, Column: l.sc.Column, } } func (l *Lexer) consumeTripleQuoteComment() string { l.next = l.sc.Next() if l.next != '"' { panic("consumeTripleQuoteComment used in wrong context: no third quote?") } var buf bytes.Buffer var numQuotes int for { l.next = l.sc.Next() if l.next == '"' { numQuotes++ } else { numQuotes = 0 } buf.WriteRune(l.next) if numQuotes == 3 || l.next == scanner.EOF { break } } val := buf.String() val = val[:len(val)-numQuotes] return blockString(val) } func (l *Lexer) consumeStringComment() string { val, err := strconv.Unquote(l.sc.TokenText()) if err != nil { panic(err) } return val } // consumeComment consumes all characters from `#` to the first encountered line terminator. // The characters are appended to `l.comment`. func (l *Lexer) consumeComment() { if l.next != '#' { panic("consumeComment used in wrong context") } // TODO: count and trim whitespace so we can dedent any following lines. if l.sc.Peek() == ' ' { l.sc.Next() } if l.comment.Len() > 0 { l.comment.WriteRune('\n') } for { next := l.sc.Next() if next == '\r' || next == '\n' || next == scanner.EOF { break } l.comment.WriteRune(next) } } func (l *Lexer) CatchScannerError(s *scanner.Scanner, msg string) { l.SyntaxError(msg) } graphql-go-1.6.0/internal/common/lexer_test.go000066400000000000000000000066211475633407000214060ustar00rootroot00000000000000package common_test import ( "testing" "github.com/graph-gophers/graphql-go/internal/common" ) type consumeTestCase struct { description string definition string expected string // expected description failureExpected bool useStringDescriptions bool } // Note that these tests stop as soon as they parse the comments, so even though the rest of the file will fail to parse sometimes, the tests still pass var consumeTests = []consumeTestCase{{ description: "no string descriptions allowed in old mode", definition: ` # Comment line 1 #Comment line 2 ,,,,,, # Commas are insignificant "New style comments" type Hello { world: String! }`, expected: "Comment line 1\nComment line 2\nCommas are insignificant", useStringDescriptions: false, }, { description: "simple string descriptions allowed in new mode", definition: ` # Comment line 1 #Comment line 2 ,,,,,, # Commas are insignificant "New style comments" type Hello { world: String! }`, expected: "New style comments", useStringDescriptions: true, }, { description: "comment after description works", definition: ` # Comment line 1 #Comment line 2 ,,,,,, # Commas are insignificant type Hello { world: String! }`, expected: "", useStringDescriptions: true, }, { description: "triple quote descriptions allowed in new mode", definition: ` # Comment line 1 #Comment line 2 ,,,,,, # Commas are insignificant """ New style comments Another line """ type Hello { world: String! }`, expected: "New style comments\nAnother line", useStringDescriptions: true, }} func TestConsume(t *testing.T) { for _, test := range consumeTests { t.Run(test.description, func(t *testing.T) { lex := common.NewLexer(test.definition, test.useStringDescriptions) err := lex.CatchSyntaxError(func() { lex.ConsumeWhitespace() }) if test.failureExpected { if err == nil { t.Fatalf("schema should have been invalid; comment: %s", lex.DescComment()) } } else { if err != nil { t.Fatal(err) } } if test.expected != lex.DescComment() { t.Errorf("wrong description value:\nwant: %q\ngot : %q", test.expected, lex.DescComment()) } }) } } var multilineStringTests = []consumeTestCase{ { description: "Oneline strings are okay", definition: `"Hello World"`, expected: "", failureExpected: false, useStringDescriptions: true, }, { description: "Multiline strings are not allowed", definition: `"Hello World"`, expected: `graphql: syntax error: literal not terminated (line 1, column 1)`, failureExpected: true, useStringDescriptions: true, }, } func TestMultilineString(t *testing.T) { for _, test := range multilineStringTests { t.Run(test.description, func(t *testing.T) { lex := common.NewLexer(test.definition, test.useStringDescriptions) err := lex.CatchSyntaxError(func() { lex.ConsumeWhitespace() }) if test.failureExpected && err == nil { t.Fatalf("Test '%s' should fail", test.description) } else if test.failureExpected && err != nil { if test.expected != err.Error() { t.Fatalf("Test '%s' failed with wrong error: '%s'. Error should be: '%s'", test.description, err.Error(), test.expected) } } if !test.failureExpected && err != nil { t.Fatalf("Test '%s' failed with error: '%s'", test.description, err.Error()) } }) } } graphql-go-1.6.0/internal/common/literals.go000066400000000000000000000024341475633407000210450ustar00rootroot00000000000000package common import ( "text/scanner" "github.com/graph-gophers/graphql-go/ast" ) func ParseLiteral(l *Lexer, constOnly bool) ast.Value { loc := l.Location() switch l.Peek() { case '$': if constOnly { l.SyntaxError("variable not allowed") panic("unreachable") } l.ConsumeToken('$') return &ast.Variable{Name: l.ConsumeIdent(), Loc: loc} case scanner.Int, scanner.Float, scanner.String, scanner.Ident: lit := l.ConsumeLiteral() if lit.Type == scanner.Ident && lit.Text == "null" { return &ast.NullValue{Loc: loc} } lit.Loc = loc return lit case '-': l.ConsumeToken('-') lit := l.ConsumeLiteral() lit.Text = "-" + lit.Text lit.Loc = loc return lit case '[': l.ConsumeToken('[') var list []ast.Value for l.Peek() != ']' { list = append(list, ParseLiteral(l, constOnly)) } l.ConsumeToken(']') return &ast.ListValue{Values: list, Loc: loc} case '{': l.ConsumeToken('{') var fields []*ast.ObjectField for l.Peek() != '}' { name := l.ConsumeIdentWithLoc() l.ConsumeToken(':') value := ParseLiteral(l, constOnly) fields = append(fields, &ast.ObjectField{Name: name, Value: value}) } l.ConsumeToken('}') return &ast.ObjectValue{Fields: fields, Loc: loc} default: l.SyntaxError("invalid value") panic("unreachable") } } graphql-go-1.6.0/internal/common/types.go000066400000000000000000000032301475633407000203650ustar00rootroot00000000000000package common import ( "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" ) func ParseType(l *Lexer) ast.Type { t := parseNullType(l) if l.Peek() == '!' { l.ConsumeToken('!') return &ast.NonNull{OfType: t} } return t } func parseNullType(l *Lexer) ast.Type { if l.Peek() == '[' { l.ConsumeToken('[') ofType := ParseType(l) l.ConsumeToken(']') return &ast.List{OfType: ofType} } return &ast.TypeName{Ident: l.ConsumeIdentWithLoc()} } type Resolver func(name string) ast.Type // ResolveType attempts to resolve a type's name against a resolving function. // This function is used when one needs to check if a TypeName exists in the resolver (typically a Schema). // // In the example below, ResolveType would be used to check if the resolving function // returns a valid type for Dimension: // // type Profile { // picture(dimensions: Dimension): Url // } // // ResolveType recursively unwraps List and NonNull types until a NamedType is reached. func ResolveType(t ast.Type, resolver Resolver) (ast.Type, *errors.QueryError) { switch t := t.(type) { case *ast.List: ofType, err := ResolveType(t.OfType, resolver) if err != nil { return nil, err } return &ast.List{OfType: ofType}, nil case *ast.NonNull: ofType, err := ResolveType(t.OfType, resolver) if err != nil { return nil, err } return &ast.NonNull{OfType: ofType}, nil case *ast.TypeName: refT := resolver(t.Name) if refT == nil { err := errors.Errorf("Unknown type %q.", t.Name) err.Rule = "KnownTypeNamesRule" err.Locations = []errors.Location{t.Loc} return nil, err } return refT, nil default: return t, nil } } graphql-go-1.6.0/internal/common/values.go000066400000000000000000000015161475633407000205250ustar00rootroot00000000000000package common import ( "github.com/graph-gophers/graphql-go/ast" ) func ParseInputValue(l *Lexer) *ast.InputValueDefinition { p := &ast.InputValueDefinition{} p.Loc = l.Location() p.Desc = l.DescComment() p.Name = l.ConsumeIdentWithLoc() l.ConsumeToken(':') p.TypeLoc = l.Location() p.Type = ParseType(l) if l.Peek() == '=' { l.ConsumeToken('=') p.Default = ParseLiteral(l, true) } p.Directives = ParseDirectives(l) return p } func ParseArgumentList(l *Lexer) ast.ArgumentList { var args ast.ArgumentList l.ConsumeToken('(') for l.Peek() != ')' { name := l.ConsumeIdentWithLoc() l.ConsumeToken(':') value := ParseLiteral(l, false) directives := ParseDirectives(l) args = append(args, &ast.Argument{ Name: name, Value: value, Directives: directives, }) } l.ConsumeToken(')') return args } graphql-go-1.6.0/internal/exec/000077500000000000000000000000001475633407000163305ustar00rootroot00000000000000graphql-go-1.6.0/internal/exec/exec.go000066400000000000000000000244101475633407000176040ustar00rootroot00000000000000package exec import ( "bytes" "context" "encoding/json" "fmt" "reflect" "sync" "time" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/exec/resolvable" "github.com/graph-gophers/graphql-go/internal/exec/selected" "github.com/graph-gophers/graphql-go/internal/query" "github.com/graph-gophers/graphql-go/log" "github.com/graph-gophers/graphql-go/trace/tracer" ) type Request struct { selected.Request Limiter chan struct{} Tracer tracer.Tracer Logger log.Logger PanicHandler errors.PanicHandler SubscribeResolverTimeout time.Duration } func (r *Request) handlePanic(ctx context.Context) { if value := recover(); value != nil { r.Logger.LogPanic(ctx, value) r.AddError(r.PanicHandler.MakePanicError(ctx, value)) } } type extensionser interface { Extensions() map[string]interface{} } func (r *Request) Execute(ctx context.Context, s *resolvable.Schema, op *ast.OperationDefinition) ([]byte, []*errors.QueryError) { var out bytes.Buffer func() { defer r.handlePanic(ctx) sels := selected.ApplyOperation(&r.Request, s, op) var resolver reflect.Value switch op.Type { case query.Query: resolver = s.QueryResolver case query.Mutation: resolver = s.MutationResolver case query.Subscription: resolver = s.SubscriptionResolver default: panic("unknown query operation") } if errs := validateSelections(ctx, sels, nil, s); errs != nil { r.Errs = errs out.Write([]byte("null")) return } r.execSelections(ctx, sels, nil, s, resolver, &out, op.Type == query.Mutation) }() if err := ctx.Err(); err != nil { return nil, []*errors.QueryError{errors.Errorf("%s", err)} } return out.Bytes(), r.Errs } type fieldToValidate struct { field *selected.SchemaField sels []selected.Selection } type fieldToExec struct { field *selected.SchemaField sels []selected.Selection resolver reflect.Value out *bytes.Buffer } func (f *fieldToExec) resolve(ctx context.Context) (output interface{}, err error) { return f.field.Resolve(ctx, f.resolver) } func resolvedToNull(b *bytes.Buffer) bool { return bytes.Equal(b.Bytes(), []byte("null")) } func (r *Request) execSelections(ctx context.Context, sels []selected.Selection, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer, serially bool) { async := !serially && selected.HasAsyncSel(sels) var fields []*fieldToExec collectFieldsToResolve(sels, s, resolver, &fields, make(map[string]*fieldToExec)) if async { var wg sync.WaitGroup wg.Add(len(fields)) for _, f := range fields { go func(f *fieldToExec) { defer wg.Done() defer r.handlePanic(ctx) f.out = new(bytes.Buffer) execFieldSelection(ctx, r, s, f, &pathSegment{path, f.field.Alias}, true) }(f) } wg.Wait() } else { for _, f := range fields { f.out = new(bytes.Buffer) execFieldSelection(ctx, r, s, f, &pathSegment{path, f.field.Alias}, true) } } out.WriteByte('{') for i, f := range fields { // If a non-nullable child resolved to null, an error was added to the // "errors" list in the response, so this field resolves to null. // If this field is non-nullable, the error is propagated to its parent. if _, ok := f.field.Type.(*ast.NonNull); ok && resolvedToNull(f.out) { out.Reset() out.Write([]byte("null")) return } if i > 0 { out.WriteByte(',') } out.WriteByte('"') out.WriteString(f.field.Alias) out.WriteByte('"') out.WriteByte(':') out.Write(f.out.Bytes()) } out.WriteByte('}') } func collectFieldsToResolve(sels []selected.Selection, s *resolvable.Schema, resolver reflect.Value, fields *[]*fieldToExec, fieldByAlias map[string]*fieldToExec) { for _, sel := range sels { switch sel := sel.(type) { case *selected.SchemaField: field, ok := fieldByAlias[sel.Alias] if !ok { // validation already checked for conflict (TODO) field = &fieldToExec{field: sel, resolver: resolver} fieldByAlias[sel.Alias] = field *fields = append(*fields, field) } field.sels = append(field.sels, sel.Sels...) case *selected.TypenameField: _, ok := fieldByAlias[sel.Alias] if !ok { res := reflect.ValueOf(typeOf(sel, resolver)) f := s.FieldTypename f.TypeName = res.String() sf := &selected.SchemaField{ Field: f, Alias: sel.Alias, FixedResult: res, } field := &fieldToExec{field: sf, resolver: resolver} *fields = append(*fields, field) fieldByAlias[sel.Alias] = field } case *selected.TypeAssertion: out := resolver.Method(sel.MethodIndex).Call(nil) if !out[1].Bool() { continue } collectFieldsToResolve(sel.Sels, s, out[0], fields, fieldByAlias) default: panic("unreachable") } } } func typeOf(tf *selected.TypenameField, resolver reflect.Value) string { if len(tf.TypeAssertions) == 0 { return tf.Name } for name, a := range tf.TypeAssertions { out := resolver.Method(a.MethodIndex).Call(nil) if out[1].Bool() { return name } } return "" } func execFieldSelection(ctx context.Context, r *Request, s *resolvable.Schema, f *fieldToExec, path *pathSegment, applyLimiter bool) { if applyLimiter { r.Limiter <- struct{}{} } var result reflect.Value var err *errors.QueryError traceCtx, finish := r.Tracer.TraceField(ctx, f.field.TraceLabel, f.field.TypeName, f.field.Name, !f.field.Async, f.field.Args) defer func() { finish(err) }() err = func() (err *errors.QueryError) { defer func() { if panicValue := recover(); panicValue != nil { r.Logger.LogPanic(ctx, panicValue) err = r.PanicHandler.MakePanicError(ctx, panicValue) err.Path = path.toSlice() } }() if f.field.FixedResult.IsValid() { result = f.field.FixedResult return nil } if err := traceCtx.Err(); err != nil { return errors.Errorf("%s", err) // don't execute any more resolvers if context got cancelled } res, resolverErr := f.resolve(ctx) if resolverErr != nil { err := errors.Errorf("%s", resolverErr) err.Path = path.toSlice() err.ResolverError = resolverErr if ex, ok := resolverErr.(extensionser); ok { err.Extensions = ex.Extensions() } return err } result = reflect.ValueOf(res) return nil }() if applyLimiter { <-r.Limiter } if err != nil { // If an error occurred while resolving a field, it should be treated as though the field // returned null, and an error must be added to the "errors" list in the response. r.AddError(err) f.out.WriteString("null") return } r.execSelectionSet(traceCtx, f.sels, f.field.Type, path, s, result, f.out) } func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selection, typ ast.Type, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer) { t, nonNull := unwrapNonNull(typ) // a reflect.Value of a nil interface will show up as an Invalid value if resolver.Kind() == reflect.Invalid || ((resolver.Kind() == reflect.Ptr || resolver.Kind() == reflect.Interface) && resolver.IsNil()) { // If a field of a non-null type resolves to null (either because the // function to resolve the field returned null or because an error occurred), // add an error to the "errors" list in the response. if nonNull { err := errors.Errorf("graphql: got nil for non-null %q", t) err.Path = path.toSlice() r.AddError(err) } out.WriteString("null") return } switch t.(type) { case *ast.ObjectTypeDefinition, *ast.InterfaceTypeDefinition, *ast.Union: r.execSelections(ctx, sels, path, s, resolver, out, false) return } // Any pointers or interfaces at this point should be non-nil, so we can get the actual value of them // for serialization if resolver.Kind() == reflect.Ptr || resolver.Kind() == reflect.Interface { resolver = resolver.Elem() } switch t := t.(type) { case *ast.List: r.execList(ctx, sels, t, path, s, resolver, out) case *ast.ScalarTypeDefinition: v := resolver.Interface() data, err := json.Marshal(v) if err != nil { panic(errors.Errorf("could not marshal %v: %s", v, err)) } out.Write(data) case *ast.EnumTypeDefinition: var stringer fmt.Stringer = resolver if s, ok := resolver.Interface().(fmt.Stringer); ok { stringer = s } name := stringer.String() var valid bool for _, v := range t.EnumValuesDefinition { if v.EnumValue == name { valid = true break } } if !valid { err := errors.Errorf("Invalid value %s.\nExpected type %s, found %s.", name, t.Name, name) err.Path = path.toSlice() r.AddError(err) out.WriteString("null") return } out.WriteByte('"') out.WriteString(name) out.WriteByte('"') default: panic("unreachable") } } func (r *Request) execList(ctx context.Context, sels []selected.Selection, typ *ast.List, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer) { l := resolver.Len() entryouts := make([]bytes.Buffer, l) if selected.HasAsyncSel(sels) { // Limit the number of concurrent goroutines spawned as it can lead to large // memory spikes for large lists. concurrency := cap(r.Limiter) sem := make(chan struct{}, concurrency) for i := 0; i < l; i++ { sem <- struct{}{} go func(i int) { defer func() { <-sem }() defer r.handlePanic(ctx) r.execSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, i}, s, resolver.Index(i), &entryouts[i]) }(i) } for i := 0; i < concurrency; i++ { sem <- struct{}{} } } else { for i := 0; i < l; i++ { r.execSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, i}, s, resolver.Index(i), &entryouts[i]) } } _, listOfNonNull := typ.OfType.(*ast.NonNull) out.WriteByte('[') for i, entryout := range entryouts { // If the list wraps a non-null type and one of the list elements // resolves to null, then the entire list resolves to null. if listOfNonNull && resolvedToNull(&entryout) { out.Reset() out.WriteString("null") return } if i > 0 { out.WriteByte(',') } out.Write(entryout.Bytes()) } out.WriteByte(']') } func unwrapNonNull(t ast.Type) (ast.Type, bool) { if nn, ok := t.(*ast.NonNull); ok { return nn.OfType, true } return t, false } type pathSegment struct { parent *pathSegment value interface{} } func (p *pathSegment) toSlice() []interface{} { if p == nil { return nil } return append(p.parent.toSlice(), p.value) } graphql-go-1.6.0/internal/exec/packer/000077500000000000000000000000001475633407000175755ustar00rootroot00000000000000graphql-go-1.6.0/internal/exec/packer/packer.go000066400000000000000000000223161475633407000213750ustar00rootroot00000000000000package packer import ( "fmt" "math" "reflect" "strings" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/decode" "github.com/graph-gophers/graphql-go/errors" ) type packer interface { Pack(value interface{}) (reflect.Value, error) } type Builder struct { packerMap map[typePair]*packerMapEntry structPackers []*StructPacker } type typePair struct { graphQLType ast.Type resolverType reflect.Type } type packerMapEntry struct { packer packer targets []*packer } func NewBuilder() *Builder { return &Builder{ packerMap: make(map[typePair]*packerMapEntry), } } func (b *Builder) Finish() error { for _, entry := range b.packerMap { for _, target := range entry.targets { *target = entry.packer } } for _, p := range b.structPackers { p.defaultStruct = reflect.New(p.structType).Elem() for _, f := range p.fields { if defaultVal := f.def; defaultVal != nil { v, err := f.packer.Pack(defaultVal.Deserialize(nil)) if err != nil { return err } p.defaultStruct.FieldByIndex(f.index).Set(v) } } } return nil } func (b *Builder) assignPacker(target *packer, schemaType ast.Type, reflectType reflect.Type) error { k := typePair{schemaType, reflectType} ref, ok := b.packerMap[k] if !ok { ref = &packerMapEntry{} b.packerMap[k] = ref var err error ref.packer, err = b.makePacker(schemaType, reflectType) if err != nil { return err } } ref.targets = append(ref.targets, target) return nil } func (b *Builder) makePacker(schemaType ast.Type, reflectType reflect.Type) (packer, error) { t, nonNull := unwrapNonNull(schemaType) if !nonNull { if reflectType.Kind() == reflect.Ptr { elemType := reflectType.Elem() addPtr := true if _, ok := t.(*ast.InputObject); ok { elemType = reflectType // keep pointer for input objects addPtr = false } elem, err := b.makeNonNullPacker(t, elemType) if err != nil { return nil, err } return &nullPacker{ elemPacker: elem, valueType: reflectType, addPtr: addPtr, }, nil } else if isNullable(reflectType) { elemType := reflectType addPtr := false elem, err := b.makeNonNullPacker(t, elemType) if err != nil { return nil, err } return &nullPacker{ elemPacker: elem, valueType: reflectType, addPtr: addPtr, }, nil } else { return nil, fmt.Errorf("%s is not a pointer or a nullable type", reflectType) } } return b.makeNonNullPacker(t, reflectType) } func (b *Builder) makeNonNullPacker(schemaType ast.Type, reflectType reflect.Type) (packer, error) { if u, ok := reflect.New(reflectType).Interface().(decode.Unmarshaler); ok { if !u.ImplementsGraphQLType(schemaType.String()) { return nil, fmt.Errorf("can not unmarshal %s into %s", schemaType, reflectType) } return &unmarshalerPacker{ ValueType: reflectType, }, nil } switch t := schemaType.(type) { case *ast.ScalarTypeDefinition: return &ValuePacker{ ValueType: reflectType, }, nil case *ast.EnumTypeDefinition: if reflectType.Kind() != reflect.String { return nil, fmt.Errorf("wrong type, expected %s", reflect.String) } return &ValuePacker{ ValueType: reflectType, }, nil case *ast.InputObject: e, err := b.MakeStructPacker(t.Values, reflectType) if err != nil { return nil, err } return e, nil case *ast.List: if reflectType.Kind() != reflect.Slice { return nil, fmt.Errorf("expected slice, got %s", reflectType) } p := &listPacker{ sliceType: reflectType, } if err := b.assignPacker(&p.elem, t.OfType, reflectType.Elem()); err != nil { return nil, err } return p, nil case *ast.ObjectTypeDefinition, *ast.InterfaceTypeDefinition, *ast.Union: return nil, fmt.Errorf("type of kind %s can not be used as input", t.Kind()) default: panic("unreachable") } } func (b *Builder) MakeStructPacker(values []*ast.InputValueDefinition, typ reflect.Type) (*StructPacker, error) { structType := typ usePtr := false if typ.Kind() == reflect.Ptr { structType = typ.Elem() usePtr = true } if structType.Kind() != reflect.Struct { return nil, fmt.Errorf("expected struct or pointer to struct, got %s (hint: missing `args struct { ... }` wrapper for field arguments?)", typ) } var fields []*structPackerField for _, v := range values { name := v.Name.Name fe := &structPackerField{name: name, def: v.Default} fx := func(n string) bool { return strings.EqualFold(stripUnderscore(n), stripUnderscore(name)) } sf, ok := structType.FieldByNameFunc(fx) if !ok { return nil, fmt.Errorf("%s does not define field %q (hint: missing `args struct { ... }` wrapper for field arguments, or missing field on input struct)", typ, name) } if sf.PkgPath != "" { return nil, fmt.Errorf("field %q must be exported", sf.Name) } if _, ok := v.Type.(*ast.NonNull); ok { if sf.Type.Kind() == reflect.Ptr { return nil, fmt.Errorf("field %q must be a non-pointer since the parameter is required", sf.Name) } } fe.index = sf.Index ft := v.Type if v.Default != nil { ft, _ = unwrapNonNull(ft) ft = &ast.NonNull{OfType: ft} } if err := b.assignPacker(&fe.packer, ft, sf.Type); err != nil { return nil, fmt.Errorf("field %q: %s", sf.Name, err) } fields = append(fields, fe) } p := &StructPacker{ structType: structType, usePtr: usePtr, fields: fields, } b.structPackers = append(b.structPackers, p) return p, nil } type StructPacker struct { structType reflect.Type usePtr bool defaultStruct reflect.Value fields []*structPackerField } type structPackerField struct { name string index []int def ast.Value packer packer } func (p *StructPacker) Pack(value interface{}) (reflect.Value, error) { if value == nil { return reflect.Value{}, errors.Errorf("got null for non-null") } values := value.(map[string]interface{}) v := reflect.New(p.structType) v.Elem().Set(p.defaultStruct) for _, f := range p.fields { if value, ok := values[f.name]; ok { packed, err := f.packer.Pack(value) if err != nil { return reflect.Value{}, err } v.Elem().FieldByIndex(f.index).Set(packed) } } if !p.usePtr { return v.Elem(), nil } return v, nil } type listPacker struct { sliceType reflect.Type elem packer } func (e *listPacker) Pack(value interface{}) (reflect.Value, error) { list, ok := value.([]interface{}) if !ok { list = []interface{}{value} } v := reflect.MakeSlice(e.sliceType, len(list), len(list)) for i := range list { packed, err := e.elem.Pack(list[i]) if err != nil { return reflect.Value{}, err } v.Index(i).Set(packed) } return v, nil } type nullPacker struct { elemPacker packer valueType reflect.Type addPtr bool } func (p *nullPacker) Pack(value interface{}) (reflect.Value, error) { if value == nil && !isNullable(p.valueType) { return reflect.Zero(p.valueType), nil } v, err := p.elemPacker.Pack(value) if err != nil { return reflect.Value{}, err } if p.addPtr { ptr := reflect.New(p.valueType.Elem()) ptr.Elem().Set(v) return ptr, nil } return v, nil } type ValuePacker struct { ValueType reflect.Type } func (p *ValuePacker) Pack(value interface{}) (reflect.Value, error) { if value == nil { return reflect.Value{}, errors.Errorf("got null for non-null") } coerced, err := unmarshalInput(p.ValueType, value) if err != nil { return reflect.Value{}, fmt.Errorf("could not unmarshal %#v (%T) into %s: %s", value, value, p.ValueType, err) } return reflect.ValueOf(coerced), nil } type unmarshalerPacker struct { ValueType reflect.Type } func (p *unmarshalerPacker) Pack(value interface{}) (reflect.Value, error) { if value == nil && !isNullable(p.ValueType) { return reflect.Value{}, errors.Errorf("got null for non-null") } v := reflect.New(p.ValueType) if err := v.Interface().(decode.Unmarshaler).UnmarshalGraphQL(value); err != nil { return reflect.Value{}, err } return v.Elem(), nil } func unmarshalInput(typ reflect.Type, input interface{}) (interface{}, error) { if reflect.TypeOf(input) == typ { return input, nil } switch typ.Kind() { case reflect.Int32: switch input := input.(type) { case int: if input < math.MinInt32 || input > math.MaxInt32 { return nil, fmt.Errorf("not a 32-bit integer") } return int32(input), nil case float64: coerced := int32(input) if input < math.MinInt32 || input > math.MaxInt32 || float64(coerced) != input { return nil, fmt.Errorf("not a 32-bit integer") } return coerced, nil } case reflect.Float64: switch input := input.(type) { case int32: return float64(input), nil case int: return float64(input), nil } case reflect.String: if reflect.TypeOf(input).ConvertibleTo(typ) { return reflect.ValueOf(input).Convert(typ).Interface(), nil } } return nil, fmt.Errorf("incompatible type: %s", reflect.TypeOf(input)) } func unwrapNonNull(t ast.Type) (ast.Type, bool) { if nn, ok := t.(*ast.NonNull); ok { return nn.OfType, true } return t, false } func stripUnderscore(s string) string { return strings.Replace(s, "_", "", -1) } // NullUnmarshaller is an unmarshaller that can handle a nil input type NullUnmarshaller interface { decode.Unmarshaler Nullable() } func isNullable(t reflect.Type) bool { _, ok := reflect.New(t).Interface().(NullUnmarshaller) return ok } graphql-go-1.6.0/internal/exec/resolvable/000077500000000000000000000000001475633407000204665ustar00rootroot00000000000000graphql-go-1.6.0/internal/exec/resolvable/meta.go000066400000000000000000000031071475633407000217440ustar00rootroot00000000000000package resolvable import ( "reflect" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/introspection" ) // Meta defines the details of the metadata schema for introspection. type Meta struct { FieldSchema Field FieldType Field FieldTypename Field FieldService Field Schema *Object Type *Object Service *Object } func newMeta(s *ast.Schema) *Meta { var err error b := newBuilder(s, false) metaSchema := s.Types["__Schema"].(*ast.ObjectTypeDefinition) so, err := b.makeObjectExec(metaSchema.Name, metaSchema.Fields, nil, nil, false, reflect.TypeOf(&introspection.Schema{})) if err != nil { panic(err) } metaType := s.Types["__Type"].(*ast.ObjectTypeDefinition) t, err := b.makeObjectExec(metaType.Name, metaType.Fields, nil, nil, false, reflect.TypeOf(&introspection.Type{})) if err != nil { panic(err) } if err := b.finish(); err != nil { panic(err) } fieldTypename := Field{ FieldDefinition: ast.FieldDefinition{ Name: "__typename", Type: &ast.NonNull{OfType: s.Types["String"]}, }, TraceLabel: "GraphQL field: __typename", } fieldSchema := Field{ FieldDefinition: ast.FieldDefinition{ Name: "__schema", Type: s.Types["__Schema"], }, TraceLabel: "GraphQL field: __schema", } fieldType := Field{ FieldDefinition: ast.FieldDefinition{ Name: "__type", Type: s.Types["__Type"], }, TraceLabel: "GraphQL field: __type", } return &Meta{ FieldSchema: fieldSchema, FieldTypename: fieldTypename, FieldType: fieldType, Schema: so, Type: t, } } graphql-go-1.6.0/internal/exec/resolvable/resolvable.go000066400000000000000000000377021475633407000231640ustar00rootroot00000000000000package resolvable import ( "context" "fmt" "reflect" "strings" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/decode" "github.com/graph-gophers/graphql-go/internal/exec/packer" ) const ( Query = "Query" Mutation = "Mutation" Subscription = "Subscription" ) type Schema struct { *Meta ast.Schema Query Resolvable Mutation Resolvable Subscription Resolvable QueryResolver reflect.Value MutationResolver reflect.Value SubscriptionResolver reflect.Value } type Resolvable interface { isResolvable() } type Object struct { Name string Fields map[string]*Field TypeAssertions map[string]*TypeAssertion Interfaces map[string]struct{} } type Field struct { ast.FieldDefinition TypeName string MethodIndex int FieldIndex []int HasContext bool HasError bool IsFieldFunc bool ArgsPacker *packer.StructPacker ValueExec Resolvable TraceLabel string } func (f *Field) UseMethodResolver() bool { return f.MethodIndex != -1 || f.IsFieldFunc } func (f *Field) Resolve(ctx context.Context, resolver reflect.Value, args interface{}) (output interface{}, err error) { if !f.UseMethodResolver() { res := resolver // TODO extract out unwrapping ptr logic to a common place if res.Kind() == reflect.Ptr { res = res.Elem() } return res.FieldByIndex(f.FieldIndex).Interface(), nil } var in []reflect.Value var callOut []reflect.Value if f.HasContext { in = append(in, reflect.ValueOf(ctx)) } if f.ArgsPacker != nil { in = append(in, reflect.ValueOf(args)) } if f.IsFieldFunc { // resolver is a struct field of type func res := resolver if res.Kind() == reflect.Pointer { res = resolver.Elem() } callOut = res.FieldByIndex(f.FieldIndex).Call(in) } else { callOut = resolver.Method(f.MethodIndex).Call(in) } result := callOut[0] if f.HasError && !callOut[1].IsNil() { resolverErr := callOut[1].Interface().(error) return result.Interface(), resolverErr } return result.Interface(), nil } type TypeAssertion struct { MethodIndex int TypeExec Resolvable } type List struct { Elem Resolvable } type Scalar struct{} func (*Object) isResolvable() {} func (*List) isResolvable() {} func (*Scalar) isResolvable() {} func ApplyResolver(s *ast.Schema, resolver interface{}, useFieldResolvers bool) (*Schema, error) { if resolver == nil { return &Schema{Meta: newMeta(s), Schema: *s}, nil } b := newBuilder(s, useFieldResolvers) var query, mutation, subscription Resolvable resolvers := map[string]interface{}{} rv := reflect.ValueOf(resolver) // use separate resolvers in case Query, Mutation and/or Subscription methods are defined for _, op := range [...]string{Query, Mutation, Subscription} { m := rv.MethodByName(op) if m.IsValid() { // if the root resolver has a method for the current operation mt := m.Type() if mt.NumIn() != 0 { return nil, fmt.Errorf("method %q of %v must not accept any arguments, got %d", op, rv.Type(), mt.NumIn()) } if mt.NumOut() != 1 { return nil, fmt.Errorf("method %q of %v must have 1 return value, got %d", op, rv.Type(), mt.NumOut()) } ot := mt.Out(0) if ot.Kind() != reflect.Pointer && ot.Kind() != reflect.Interface { return nil, fmt.Errorf("method %q of %v must return an interface or a pointer, got %+v", op, rv.Type(), ot) } out := m.Call(nil) res := out[0] if res.IsNil() { return nil, fmt.Errorf("method %q of %v must return a non-nil result, got %v", op, rv.Type(), res) } switch res.Kind() { case reflect.Pointer: resolvers[op] = res.Elem().Addr().Interface() case reflect.Interface: resolvers[op] = res.Elem().Interface() default: panic("ureachable") } } // If a method for the current operation is not defined in the root resolver, // then use the root resolver for the operation. if resolvers[op] == nil { resolvers[op] = resolver } } if t, ok := s.RootOperationTypes["query"]; ok { if err := b.assignExec(&query, t, reflect.TypeOf(resolvers[Query])); err != nil { return nil, err } } if t, ok := s.RootOperationTypes["mutation"]; ok { if err := b.assignExec(&mutation, t, reflect.TypeOf(resolvers[Mutation])); err != nil { return nil, err } } if t, ok := s.RootOperationTypes["subscription"]; ok { if err := b.assignExec(&subscription, t, reflect.TypeOf(resolvers[Subscription])); err != nil { return nil, err } } if err := b.finish(); err != nil { return nil, err } return &Schema{ Meta: newMeta(s), Schema: *s, QueryResolver: reflect.ValueOf(resolvers[Query]), MutationResolver: reflect.ValueOf(resolvers[Mutation]), SubscriptionResolver: reflect.ValueOf(resolvers[Subscription]), Query: query, Mutation: mutation, Subscription: subscription, }, nil } type execBuilder struct { schema *ast.Schema resMap map[typePair]*resMapEntry packerBuilder *packer.Builder useFieldResolvers bool } type typePair struct { graphQLType ast.Type resolverType reflect.Type } type resMapEntry struct { exec Resolvable targets []*Resolvable } func newBuilder(s *ast.Schema, useFieldResolvers bool) *execBuilder { return &execBuilder{ schema: s, resMap: make(map[typePair]*resMapEntry), packerBuilder: packer.NewBuilder(), useFieldResolvers: useFieldResolvers, } } func (b *execBuilder) finish() error { for _, entry := range b.resMap { for _, target := range entry.targets { *target = entry.exec } } return b.packerBuilder.Finish() } func (b *execBuilder) assignExec(target *Resolvable, t ast.Type, resolverType reflect.Type) error { k := typePair{t, resolverType} ref, ok := b.resMap[k] if !ok { ref = &resMapEntry{} b.resMap[k] = ref var err error ref.exec, err = b.makeExec(t, resolverType) if err != nil { return err } } ref.targets = append(ref.targets, target) return nil } func (b *execBuilder) makeExec(t ast.Type, resolverType reflect.Type) (Resolvable, error) { var nonNull bool t, nonNull = unwrapNonNull(t) switch t := t.(type) { case *ast.ObjectTypeDefinition: return b.makeObjectExec(t.Name, t.Fields, nil, t.Interfaces, nonNull, resolverType) case *ast.InterfaceTypeDefinition: return b.makeObjectExec(t.Name, t.Fields, t.PossibleTypes, nil, nonNull, resolverType) case *ast.Union: return b.makeObjectExec(t.Name, nil, t.UnionMemberTypes, nil, nonNull, resolverType) } if !nonNull { if resolverType.Kind() != reflect.Ptr { return nil, fmt.Errorf("%s is not a pointer", resolverType) } resolverType = resolverType.Elem() } switch t := t.(type) { case *ast.ScalarTypeDefinition: return makeScalarExec(t, resolverType) case *ast.EnumTypeDefinition: return &Scalar{}, nil case *ast.List: if resolverType.Kind() != reflect.Slice { return nil, fmt.Errorf("%s is not a slice", resolverType) } e := &List{} if err := b.assignExec(&e.Elem, t.OfType, resolverType.Elem()); err != nil { return nil, err } return e, nil default: panic("invalid type: " + t.String()) } } func makeScalarExec(t *ast.ScalarTypeDefinition, resolverType reflect.Type) (Resolvable, error) { implementsType := false switch r := reflect.New(resolverType).Interface().(type) { case *int32: implementsType = t.Name == "Int" case *float64: implementsType = t.Name == "Float" case *string: implementsType = t.Name == "String" case *bool: implementsType = t.Name == "Boolean" case decode.Unmarshaler: implementsType = r.ImplementsGraphQLType(t.Name) } if !implementsType { return nil, fmt.Errorf("can not use %s as %s", resolverType, t.Name) } return &Scalar{}, nil } func (b *execBuilder) makeObjectExec(typeName string, fields ast.FieldsDefinition, possibleTypes []*ast.ObjectTypeDefinition, interfaces []*ast.InterfaceTypeDefinition, nonNull bool, resolverType reflect.Type) (*Object, error) { if !nonNull { if resolverType.Kind() != reflect.Ptr && resolverType.Kind() != reflect.Interface { return nil, fmt.Errorf("%s is not a pointer or interface", resolverType) } } methodHasReceiver := resolverType.Kind() != reflect.Interface Fields := make(map[string]*Field) rt := unwrapPtr(resolverType) fieldsCount, fieldTagsCount := fieldCount(rt, map[string]int{}, map[string]int{}) for _, f := range fields { var fieldIndex []int methodIndex := findMethod(resolverType, f.Name) if b.useFieldResolvers && methodIndex == -1 { // If a resolver field is ambiguous thrown an error unless there is exactly one field with the given graphql // reflect tag. In that case use the field with the reflect tag. if fieldTagsCount[f.Name] > 1 { return nil, fmt.Errorf("%s does not resolve %q: multiple fields have a graphql reflect tag %q", resolverType, typeName, f.Name) } else if fieldsCount[strings.ToLower(stripUnderscore(f.Name))] > 1 && fieldTagsCount[f.Name] != 1 { return nil, fmt.Errorf("%s does not resolve %q: ambiguous field %q", resolverType, typeName, f.Name) } fieldIndex = findField(rt, f.Name, []int{}, fieldTagsCount) } if methodIndex == -1 && len(fieldIndex) == 0 { var hint string if findMethod(reflect.PtrTo(resolverType), f.Name) != -1 { hint = " (hint: the method exists on the pointer type)" } return nil, fmt.Errorf("%s does not resolve %q: missing method for field %q%s", resolverType, typeName, f.Name, hint) } var m reflect.Method var sf reflect.StructField if methodIndex != -1 { m = resolverType.Method(methodIndex) } else { sf = rt.FieldByIndex(fieldIndex) } fe, err := b.makeFieldExec(typeName, f, m, sf, methodIndex, fieldIndex, methodHasReceiver) if err != nil { var resolverName string if methodIndex != -1 { resolverName = m.Name } else { resolverName = sf.Name } return nil, fmt.Errorf("%s\n\tused by (%s).%s", err, resolverType, resolverName) } Fields[f.Name] = fe } // Check type assertions when // 1) using method resolvers // 2) Or resolver is not an interface type typeAssertions := make(map[string]*TypeAssertion) if !b.useFieldResolvers || resolverType.Kind() != reflect.Interface { for _, impl := range possibleTypes { methodIndex := findMethod(resolverType, "To"+impl.Name) if methodIndex == -1 { return nil, fmt.Errorf("%s does not resolve %q: missing method %q to convert to %q", resolverType, typeName, "To"+impl.Name, impl.Name) } m := resolverType.Method(methodIndex) expectedIn := 0 if methodHasReceiver { expectedIn = 1 } if m.Type.NumIn() != expectedIn { return nil, fmt.Errorf("%s does not resolve %q: method %q should't have any arguments", resolverType, typeName, "To"+impl.Name) } if m.Type.NumOut() != 2 { return nil, fmt.Errorf("%s does not resolve %q: method %q should return a value and a bool indicating success", resolverType, typeName, "To"+impl.Name) } a := &TypeAssertion{ MethodIndex: methodIndex, } if err := b.assignExec(&a.TypeExec, impl, resolverType.Method(methodIndex).Type.Out(0)); err != nil { return nil, err } typeAssertions[impl.Name] = a } } ifaces := make(map[string]struct{}) for _, iface := range interfaces { ifaces[iface.Name] = struct{}{} } return &Object{ Name: typeName, Fields: Fields, TypeAssertions: typeAssertions, Interfaces: ifaces, }, nil } var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() var errorType = reflect.TypeOf((*error)(nil)).Elem() func (b *execBuilder) makeFieldExec(typeName string, f *ast.FieldDefinition, m reflect.Method, sf reflect.StructField, methodIndex int, fieldIndex []int, methodHasReceiver bool) (*Field, error) { var argsPacker *packer.StructPacker var hasError bool var hasContext bool var isFieldFunc bool if methodIndex == -1 && len(fieldIndex) > 0 { if sf.Type.Kind() == reflect.Func { m.Type = sf.Type methodHasReceiver = false isFieldFunc = true } } // Validate resolver method only when there is one if methodIndex != -1 || isFieldFunc { in := make([]reflect.Type, m.Type.NumIn()) for i := range in { in[i] = m.Type.In(i) } if methodHasReceiver { in = in[1:] // first parameter is receiver } hasContext = len(in) > 0 && in[0] == contextType if hasContext { in = in[1:] } if len(f.Arguments) > 0 { if len(in) == 0 { return nil, fmt.Errorf("must have `args struct { ... }` argument for field arguments") } var err error argsPacker, err = b.packerBuilder.MakeStructPacker(f.Arguments, in[0]) if err != nil { return nil, err } in = in[1:] } if len(in) > 0 { return nil, fmt.Errorf("too many arguments") } maxNumOfReturns := 2 if m.Type.NumOut() < maxNumOfReturns-1 { return nil, fmt.Errorf("too few return values") } if m.Type.NumOut() > maxNumOfReturns { return nil, fmt.Errorf("too many return values") } hasError = m.Type.NumOut() == maxNumOfReturns if hasError { if m.Type.Out(maxNumOfReturns-1) != errorType { return nil, fmt.Errorf(`must have "error" as its last return value`) } } } fe := &Field{ FieldDefinition: *f, TypeName: typeName, MethodIndex: methodIndex, FieldIndex: fieldIndex, IsFieldFunc: isFieldFunc, HasContext: hasContext, ArgsPacker: argsPacker, HasError: hasError, TraceLabel: fmt.Sprintf("GraphQL field: %s.%s", typeName, f.Name), } var out reflect.Type if methodIndex != -1 || isFieldFunc { out = m.Type.Out(0) sub, ok := b.schema.RootOperationTypes["subscription"] if ok && typeName == sub.TypeName() && out.Kind() == reflect.Chan { out = m.Type.Out(0).Elem() } } else { out = sf.Type } if err := b.assignExec(&fe.ValueExec, f.Type, out); err != nil { return nil, err } return fe, nil } func findMethod(t reflect.Type, name string) int { for i := 0; i < t.NumMethod(); i++ { if strings.EqualFold(stripUnderscore(name), stripUnderscore(t.Method(i).Name)) { return i } } return -1 } func findField(t reflect.Type, name string, index []int, matchingTagsCount map[string]int) []int { for i := 0; i < t.NumField(); i++ { field := t.Field(i) if field.Type.Kind() == reflect.Struct && field.Anonymous { newIndex := findField(field.Type, name, []int{i}, matchingTagsCount) if len(newIndex) > 1 { return append(index, newIndex...) } } if gt, ok := field.Tag.Lookup("graphql"); ok { if name == gt { return append(index, i) } } // The current field's tag didn't match, however, if the tag of another field matches, // then skip the name matching until we find the desired field with the correct tag. if matchingTagsCount[name] > 0 { continue } if strings.EqualFold(stripUnderscore(name), stripUnderscore(field.Name)) { return append(index, i) } } return index } // fieldCount helps resolve ambiguity when more than one embedded struct contains fields with the same name. // or when a field has a `graphql` reflect tag with the same name as some other field causing name collision. func fieldCount(t reflect.Type, count, tagsCount map[string]int) (map[string]int, map[string]int) { if t.Kind() != reflect.Struct { return nil, nil } for i := 0; i < t.NumField(); i++ { field := t.Field(i) var fieldName, gt string var hasTag bool if gt, hasTag = field.Tag.Lookup("graphql"); hasTag && gt != "" { fieldName = gt } else { fieldName = strings.ToLower(stripUnderscore(field.Name)) } if field.Type.Kind() == reflect.Struct && field.Anonymous { count, tagsCount = fieldCount(field.Type, count, tagsCount) } else { if _, ok := count[fieldName]; !ok { count[fieldName] = 0 } count[fieldName]++ if !hasTag { continue } if _, ok := count[gt]; !ok { tagsCount[gt] = 0 } tagsCount[gt]++ } } return count, tagsCount } func unwrapNonNull(t ast.Type) (ast.Type, bool) { if nn, ok := t.(*ast.NonNull); ok { return nn.OfType, true } return t, false } func stripUnderscore(s string) string { return strings.Replace(s, "_", "", -1) } func unwrapPtr(t reflect.Type) reflect.Type { if t.Kind() == reflect.Ptr { return t.Elem() } return t } graphql-go-1.6.0/internal/exec/selected/000077500000000000000000000000001475633407000201205ustar00rootroot00000000000000graphql-go-1.6.0/internal/exec/selected/selected.go000066400000000000000000000205371475633407000222460ustar00rootroot00000000000000package selected import ( "context" "fmt" "reflect" "sync" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/exec/packer" "github.com/graph-gophers/graphql-go/internal/exec/resolvable" "github.com/graph-gophers/graphql-go/internal/query" "github.com/graph-gophers/graphql-go/introspection" ) type Request struct { Schema *ast.Schema Doc *ast.ExecutableDefinition Vars map[string]interface{} Mu sync.Mutex Errs []*errors.QueryError AllowIntrospection bool } func (r *Request) AddError(err *errors.QueryError) { r.Mu.Lock() r.Errs = append(r.Errs, err) r.Mu.Unlock() } func ApplyOperation(r *Request, s *resolvable.Schema, op *ast.OperationDefinition) []Selection { var obj *resolvable.Object switch op.Type { case query.Query: obj = s.Query.(*resolvable.Object) case query.Mutation: obj = s.Mutation.(*resolvable.Object) case query.Subscription: obj = s.Subscription.(*resolvable.Object) } return applySelectionSet(r, s, obj, op.Selections) } type Selection interface { isSelection() } type SchemaField struct { resolvable.Field Alias string Args map[string]interface{} PackedArgs reflect.Value Sels []Selection Async bool FixedResult reflect.Value } func (f *SchemaField) Resolve(ctx context.Context, resolver reflect.Value) (output interface{}, err error) { var args interface{} if f.ArgsPacker != nil { args = f.PackedArgs.Interface() } return f.Field.Resolve(ctx, resolver, args) } type TypeAssertion struct { resolvable.TypeAssertion Sels []Selection } type TypenameField struct { resolvable.Object Alias string } func (*SchemaField) isSelection() {} func (*TypeAssertion) isSelection() {} func (*TypenameField) isSelection() {} func applySelectionSet(r *Request, s *resolvable.Schema, e *resolvable.Object, sels []ast.Selection) (flattenedSels []Selection) { for _, sel := range sels { switch sel := sel.(type) { case *ast.Field: field := sel if skipByDirective(r, field.Directives) { continue } switch field.Name.Name { case "__typename": // __typename is available even though r.AllowIntrospection == false // because it is necessary when using union types and interfaces: https://graphql.org/learn/schema/#union-types flattenedSels = append(flattenedSels, &TypenameField{ Object: *e, Alias: field.Alias.Name, }) case "__schema": if r.AllowIntrospection { flattenedSels = append(flattenedSels, &SchemaField{ Field: s.Meta.FieldSchema, Alias: field.Alias.Name, Sels: applySelectionSet(r, s, s.Meta.Schema, field.SelectionSet), Async: true, FixedResult: reflect.ValueOf(introspection.WrapSchema(r.Schema)), }) } case "__type": if r.AllowIntrospection { p := packer.ValuePacker{ValueType: reflect.TypeOf("")} v, err := p.Pack(field.Arguments.MustGet("name").Deserialize(r.Vars)) if err != nil { r.AddError(errors.Errorf("%s", err)) return nil } var resolvedType *introspection.Type t, ok := r.Schema.Types[v.String()] if ok { resolvedType = introspection.WrapType(t) } flattenedSels = append(flattenedSels, &SchemaField{ Field: s.Meta.FieldType, Alias: field.Alias.Name, Sels: applySelectionSet(r, s, s.Meta.Type, field.SelectionSet), Async: true, FixedResult: reflect.ValueOf(resolvedType), }) } default: fe := e.Fields[field.Name.Name] var args map[string]interface{} var packedArgs reflect.Value if fe.ArgsPacker != nil { args = make(map[string]interface{}) for _, arg := range field.Arguments { args[arg.Name.Name] = arg.Value.Deserialize(r.Vars) } var err error packedArgs, err = fe.ArgsPacker.Pack(args) if err != nil { r.AddError(errors.Errorf("%s", err)) return } } fieldSels := applyField(r, s, fe.ValueExec, field.SelectionSet) flattenedSels = append(flattenedSels, &SchemaField{ Field: *fe, Alias: field.Alias.Name, Args: args, PackedArgs: packedArgs, Sels: fieldSels, Async: fe.HasContext || fe.ArgsPacker != nil || fe.HasError || HasAsyncSel(fieldSels), }) } case *ast.InlineFragment: frag := sel if skipByDirective(r, frag.Directives) { continue } flattenedSels = append(flattenedSels, applyFragment(r, s, e, &frag.Fragment)...) case *ast.FragmentSpread: spread := sel if skipByDirective(r, spread.Directives) { continue } flattenedSels = append(flattenedSels, applyFragment(r, s, e, &r.Doc.Fragments.Get(spread.Name.Name).Fragment)...) default: panic("invalid type") } } return } func applyFragment(r *Request, s *resolvable.Schema, e *resolvable.Object, frag *ast.Fragment) []Selection { if frag.On.Name != e.Name { t := r.Schema.Resolve(frag.On.Name) face, ok := t.(*ast.InterfaceTypeDefinition) if !ok && frag.On.Name != "" { a, ok2 := e.TypeAssertions[frag.On.Name] if !ok2 { panic(fmt.Errorf("%q does not implement %q", frag.On, e.Name)) // TODO proper error handling } return []Selection{&TypeAssertion{ TypeAssertion: *a, Sels: applySelectionSet(r, s, a.TypeExec.(*resolvable.Object), frag.Selections), }} } // check if the fragment is on an interface which the current resolvable type implements // see the second test in [TestFragments] in the graphql_test.go file. if _, found := e.Interfaces[frag.On.Name]; found { return applyInterfaceFragment(r, s, e, frag) } if ok && len(face.PossibleTypes) > 0 { sels := []Selection{} for _, t := range face.PossibleTypes { if t.Name == e.Name { return applySelectionSet(r, s, e, frag.Selections) } if a, ok := e.TypeAssertions[t.Name]; ok { sels = append(sels, &TypeAssertion{ TypeAssertion: *a, Sels: applySelectionSet(r, s, a.TypeExec.(*resolvable.Object), frag.Selections), }) } } if len(sels) == 0 { panic(fmt.Errorf("%q does not implement %q", e.Name, frag.On)) // TODO proper error handling } return sels } } return applySelectionSet(r, s, e, frag.Selections) } func applyInterfaceFragment(r *Request, s *resolvable.Schema, e *resolvable.Object, frag *ast.Fragment) []Selection { // if the fragment is on an interface the object type implements, then filter out // selections for any fragments that don't match this type. var sels []ast.Selection for _, sel := range frag.Selections { switch sel := sel.(type) { case *ast.Field: sels = append(sels, sel) case *ast.InlineFragment: if sel.On.Name != e.Name { if _, ok := e.Interfaces[sel.On.Name]; !ok { continue } } sels = append(sels, sel) case *ast.FragmentSpread: f := &r.Doc.Fragments.Get(sel.Name.Name).Fragment if f.On.Name != e.Name { if _, ok := e.Interfaces[f.On.Name]; !ok { continue } } sels = append(sels, sel) } } return applySelectionSet(r, s, e, sels) } func applyField(r *Request, s *resolvable.Schema, e resolvable.Resolvable, sels []ast.Selection) []Selection { switch e := e.(type) { case *resolvable.Object: return applySelectionSet(r, s, e, sels) case *resolvable.List: return applyField(r, s, e.Elem, sels) case *resolvable.Scalar: return nil default: panic("unreachable") } } func skipByDirective(r *Request, directives ast.DirectiveList) bool { if d := directives.Get("skip"); d != nil { p := packer.ValuePacker{ValueType: reflect.TypeOf(false)} v, err := p.Pack(d.Arguments.MustGet("if").Deserialize(r.Vars)) if err != nil { r.AddError(errors.Errorf("%s", err)) } if err == nil && v.Bool() { return true } } if d := directives.Get("include"); d != nil { p := packer.ValuePacker{ValueType: reflect.TypeOf(false)} v, err := p.Pack(d.Arguments.MustGet("if").Deserialize(r.Vars)) if err != nil { r.AddError(errors.Errorf("%s", err)) } if err == nil && !v.Bool() { return true } } return false } func HasAsyncSel(sels []Selection) bool { for _, sel := range sels { switch sel := sel.(type) { case *SchemaField: if sel.Async { return true } case *TypeAssertion: if HasAsyncSel(sel.Sels) { return true } case *TypenameField: // sync default: panic("unreachable") } } return false } graphql-go-1.6.0/internal/exec/subscribe.go000066400000000000000000000104361475633407000206440ustar00rootroot00000000000000package exec import ( "bytes" "context" "encoding/json" "fmt" "reflect" "time" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/exec/resolvable" "github.com/graph-gophers/graphql-go/internal/exec/selected" ) type Response struct { Data json.RawMessage Errors []*errors.QueryError } func (r *Request) Subscribe(ctx context.Context, s *resolvable.Schema, op *ast.OperationDefinition) <-chan *Response { var result reflect.Value var f *fieldToExec var err *errors.QueryError func() { defer r.handlePanic(ctx) sels := selected.ApplyOperation(&r.Request, s, op) var fields []*fieldToExec collectFieldsToResolve(sels, s, s.SubscriptionResolver, &fields, make(map[string]*fieldToExec)) // TODO: move this check into validation.Validate if len(fields) != 1 { err = errors.Errorf("%s", "can subscribe to at most one subscription at a time") return } f = fields[0] var in []reflect.Value if f.field.HasContext { in = append(in, reflect.ValueOf(ctx)) } if f.field.ArgsPacker != nil { in = append(in, f.field.PackedArgs) } callOut := f.resolver.Method(f.field.MethodIndex).Call(in) result = callOut[0] if f.field.HasError && !callOut[1].IsNil() { switch resolverErr := callOut[1].Interface().(type) { case *errors.QueryError: err = resolverErr case error: err = errors.Errorf("%s", resolverErr) err.ResolverError = resolverErr default: panic(fmt.Errorf("can only deal with *QueryError and error types, got %T", resolverErr)) } } }() // Handles the case where the locally executed func above panicked if len(r.Request.Errs) > 0 { return sendAndReturnClosed(&Response{Errors: r.Request.Errs}) } if f == nil { return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{err}}) } if err != nil { if _, nonNullChild := f.field.Type.(*ast.NonNull); nonNullChild { return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{err}}) } return sendAndReturnClosed(&Response{Data: []byte(fmt.Sprintf(`{"%s":null}`, f.field.Alias)), Errors: []*errors.QueryError{err}}) } if ctxErr := ctx.Err(); ctxErr != nil { return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{errors.Errorf("%s", ctxErr)}}) } c := make(chan *Response) // TODO: handle resolver nil channel better? if result.IsZero() { close(c) return c } go func() { for { // Check subscription context chosen, resp, ok := reflect.Select([]reflect.SelectCase{ { Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ctx.Done()), }, { Dir: reflect.SelectRecv, Chan: result, }, }) switch chosen { // subscription context done case 0: close(c) return // upstream received case 1: // upstream closed if !ok { close(c) return } subR := &Request{ Request: selected.Request{ Doc: r.Request.Doc, Vars: r.Request.Vars, Schema: r.Request.Schema, }, Limiter: r.Limiter, Tracer: r.Tracer, Logger: r.Logger, } var out bytes.Buffer func() { timeout := r.SubscribeResolverTimeout if timeout == 0 { timeout = time.Second } subCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() // resolve response func() { defer subR.handlePanic(subCtx) var buf bytes.Buffer subR.execSelectionSet(subCtx, f.sels, f.field.Type, &pathSegment{nil, f.field.Alias}, s, resp, &buf) propagateChildError := false if _, nonNullChild := f.field.Type.(*ast.NonNull); nonNullChild && resolvedToNull(&buf) { propagateChildError = true } if !propagateChildError { out.WriteString(fmt.Sprintf(`{"%s":`, f.field.Alias)) out.Write(buf.Bytes()) out.WriteString(`}`) } }() if err := subCtx.Err(); err != nil { c <- &Response{Errors: []*errors.QueryError{errors.Errorf("%s", err)}} return } // Send response within timeout // TODO: maybe block until sent? select { case <-subCtx.Done(): case c <- &Response{Data: out.Bytes(), Errors: subR.Errs}: } }() } } }() return c } func sendAndReturnClosed(resp *Response) chan *Response { c := make(chan *Response, 1) c <- resp close(c) return c } graphql-go-1.6.0/internal/exec/validate.go000066400000000000000000000052201475633407000204470ustar00rootroot00000000000000package exec import ( "context" "fmt" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/exec/resolvable" "github.com/graph-gophers/graphql-go/internal/exec/selected" ) func collectFieldsToValidate(sels []selected.Selection, s *resolvable.Schema, fields *[]*fieldToValidate, fieldByAlias map[string]*fieldToValidate) { for _, sel := range sels { switch sel := sel.(type) { case *selected.SchemaField: field, ok := fieldByAlias[sel.Alias] if !ok { // validation already checked for conflict (TODO) field = &fieldToValidate{field: sel} fieldByAlias[sel.Alias] = field *fields = append(*fields, field) } field.sels = append(field.sels, sel.Sels...) case *selected.TypenameField: // Ignore __typename, which has no directives case *selected.TypeAssertion: collectFieldsToValidate(sel.Sels, s, fields, fieldByAlias) default: panic(fmt.Sprintf("unexpected selection type %T", sel)) } } } func validateFieldSelection(ctx context.Context, s *resolvable.Schema, f *fieldToValidate, path *pathSegment) []*errors.QueryError { if f.field.FixedResult.IsValid() { // Skip fixed result meta fields like __TypeName return nil } return validateSelectionSet(ctx, f.sels, f.field.Type, path, s) } func validateSelectionSet(ctx context.Context, sels []selected.Selection, typ ast.Type, path *pathSegment, s *resolvable.Schema) []*errors.QueryError { t, _ := unwrapNonNull(typ) switch t.(type) { case *ast.ObjectTypeDefinition, *ast.InterfaceTypeDefinition, *ast.Union: return validateSelections(ctx, sels, path, s) } switch t := t.(type) { case *ast.List: return validateList(ctx, sels, t, path, s) case *ast.ScalarTypeDefinition, *ast.EnumTypeDefinition: // Field resolution already validated, don't need to check the value default: panic(fmt.Sprintf("unexpected type %T", t)) } return nil } func validateSelections(ctx context.Context, sels []selected.Selection, path *pathSegment, s *resolvable.Schema) (errs []*errors.QueryError) { var fields []*fieldToValidate collectFieldsToValidate(sels, s, &fields, make(map[string]*fieldToValidate)) for _, f := range fields { errs = append(errs, validateFieldSelection(ctx, s, f, &pathSegment{path, f.field.Alias})...) } return errs } func validateList(ctx context.Context, sels []selected.Selection, typ *ast.List, path *pathSegment, s *resolvable.Schema) []*errors.QueryError { // For lists, we only need to apply validation once. Nothing has been evaluated, so we have no list, and need to use '0' as the path index return validateSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, 0}, s) } graphql-go-1.6.0/internal/query/000077500000000000000000000000001475633407000165515ustar00rootroot00000000000000graphql-go-1.6.0/internal/query/query.go000066400000000000000000000072301475633407000202470ustar00rootroot00000000000000package query import ( "fmt" "text/scanner" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/common" ) const ( Query ast.OperationType = "QUERY" Mutation ast.OperationType = "MUTATION" Subscription ast.OperationType = "SUBSCRIPTION" ) func Parse(queryString string) (*ast.ExecutableDefinition, *errors.QueryError) { l := common.NewLexer(queryString, false) var execDef *ast.ExecutableDefinition err := l.CatchSyntaxError(func() { execDef = parseExecutableDefinition(l) }) if err != nil { return nil, err } return execDef, nil } func parseExecutableDefinition(l *common.Lexer) *ast.ExecutableDefinition { ed := &ast.ExecutableDefinition{} l.ConsumeWhitespace() for l.Peek() != scanner.EOF { if l.Peek() == '{' { op := &ast.OperationDefinition{Type: Query, Loc: l.Location()} op.Selections = parseSelectionSet(l) ed.Operations = append(ed.Operations, op) continue } loc := l.Location() switch x := l.ConsumeIdent(); x { case "query": op := parseOperation(l, Query) op.Loc = loc ed.Operations = append(ed.Operations, op) case "mutation": ed.Operations = append(ed.Operations, parseOperation(l, Mutation)) case "subscription": ed.Operations = append(ed.Operations, parseOperation(l, Subscription)) case "fragment": frag := parseFragment(l) frag.Loc = loc ed.Fragments = append(ed.Fragments, frag) default: l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "fragment"`, x)) } } return ed } func parseOperation(l *common.Lexer, opType ast.OperationType) *ast.OperationDefinition { op := &ast.OperationDefinition{Type: opType} op.Name.Loc = l.Location() if l.Peek() == scanner.Ident { op.Name = l.ConsumeIdentWithLoc() } if l.Peek() == '(' { l.ConsumeToken('(') for l.Peek() != ')' { loc := l.Location() l.ConsumeToken('$') iv := common.ParseInputValue(l) iv.Loc = loc op.Vars = append(op.Vars, iv) } l.ConsumeToken(')') } op.Directives = common.ParseDirectives(l) op.Selections = parseSelectionSet(l) return op } func parseFragment(l *common.Lexer) *ast.FragmentDefinition { f := &ast.FragmentDefinition{} f.Name = l.ConsumeIdentWithLoc() l.ConsumeKeyword("on") f.On = ast.TypeName{Ident: l.ConsumeIdentWithLoc()} f.Directives = common.ParseDirectives(l) f.Selections = parseSelectionSet(l) return f } func parseSelectionSet(l *common.Lexer) []ast.Selection { var sels []ast.Selection l.ConsumeToken('{') for l.Peek() != '}' { sels = append(sels, parseSelection(l)) } l.ConsumeToken('}') return sels } func parseSelection(l *common.Lexer) ast.Selection { if l.Peek() == '.' { return parseSpread(l) } return parseFieldDef(l) } func parseFieldDef(l *common.Lexer) *ast.Field { f := &ast.Field{} f.Alias = l.ConsumeIdentWithLoc() f.Name = f.Alias if l.Peek() == ':' { l.ConsumeToken(':') f.Name = l.ConsumeIdentWithLoc() } if l.Peek() == '(' { f.Arguments = common.ParseArgumentList(l) } f.Directives = common.ParseDirectives(l) if l.Peek() == '{' { f.SelectionSetLoc = l.Location() f.SelectionSet = parseSelectionSet(l) } return f } func parseSpread(l *common.Lexer) ast.Selection { loc := l.Location() l.ConsumeToken('.') l.ConsumeToken('.') l.ConsumeToken('.') f := &ast.InlineFragment{Loc: loc} if l.Peek() == scanner.Ident { ident := l.ConsumeIdentWithLoc() if ident.Name != "on" { fs := &ast.FragmentSpread{ Name: ident, Loc: loc, } fs.Directives = common.ParseDirectives(l) return fs } f.On = ast.TypeName{Ident: l.ConsumeIdentWithLoc()} } f.Directives = common.ParseDirectives(l) f.Selections = parseSelectionSet(l) return f } graphql-go-1.6.0/internal/schema/000077500000000000000000000000001475633407000166445ustar00rootroot00000000000000graphql-go-1.6.0/internal/schema/meta.go000066400000000000000000000173741475633407000201350ustar00rootroot00000000000000package schema import ( "github.com/graph-gophers/graphql-go/ast" ) func init() { _ = newMeta() } // newMeta initializes an instance of the meta Schema. func newMeta() *ast.Schema { s := &ast.Schema{ Types: make(map[string]ast.NamedType), Directives: make(map[string]*ast.DirectiveDefinition), } err := Parse(s, metaSrc, false) if err != nil { panic(err) } return s } var metaSrc = ` # The ` + "`" + `Int` + "`" + ` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. scalar Int # The ` + "`" + `Float` + "`" + ` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). scalar Float # The ` + "`" + `String` + "`" + ` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. scalar String # The ` + "`" + `Boolean` + "`" + ` scalar type represents ` + "`" + `true` + "`" + ` or ` + "`" + `false` + "`" + `. scalar Boolean # The ` + "`" + `ID` + "`" + ` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as ` + "`" + `"4"` + "`" + `) or integer (such as ` + "`" + `4` + "`" + `) input value will be accepted as an ID. scalar ID # Directs the executor to include this field or fragment only when the ` + "`" + `if` + "`" + ` argument is true. directive @include( # Included when true. if: Boolean! ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT # Directs the executor to skip this field or fragment when the ` + "`" + `if` + "`" + ` argument is true. directive @skip( # Skipped when true. if: Boolean! ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT # Marks an element of a GraphQL schema as no longer supported. directive @deprecated( # Explains why this element was deprecated, usually also including a suggestion # for how to access supported similar data. Formatted in # [Markdown](https://daringfireball.net/projects/markdown/). reason: String = "No longer supported" ) on FIELD_DEFINITION | ENUM_VALUE | ARGUMENT_DEFINITION # Provides a scalar specification URL for specifying the behavior of custom scalar types. directive @specifiedBy( # The URL should point to a human-readable specification of the data format, serialization, and coercion rules. url: String! ) on SCALAR # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. # # In some cases, you need to provide options to alter GraphQL's execution behavior # in ways field arguments will not suffice, such as conditionally including or # skipping a field. Directives provide this by describing additional information # to the executor. type __Directive { name: String! description: String locations: [__DirectiveLocation!]! args: [__InputValue!]! } # A Directive can be adjacent to many parts of the GraphQL language, a # __DirectiveLocation describes one such possible adjacencies. enum __DirectiveLocation { # Location adjacent to a query operation. QUERY # Location adjacent to a mutation operation. MUTATION # Location adjacent to a subscription operation. SUBSCRIPTION # Location adjacent to a field. FIELD # Location adjacent to a fragment definition. FRAGMENT_DEFINITION # Location adjacent to a fragment spread. FRAGMENT_SPREAD # Location adjacent to an inline fragment. INLINE_FRAGMENT # Location adjacent to a schema definition. SCHEMA # Location adjacent to a scalar definition. SCALAR # Location adjacent to an object type definition. OBJECT # Location adjacent to a field definition. FIELD_DEFINITION # Location adjacent to an argument definition. ARGUMENT_DEFINITION # Location adjacent to an interface definition. INTERFACE # Location adjacent to a union definition. UNION # Location adjacent to an enum definition. ENUM # Location adjacent to an enum value definition. ENUM_VALUE # Location adjacent to an input object type definition. INPUT_OBJECT # Location adjacent to an input object field definition. INPUT_FIELD_DEFINITION } # One possible value for a given Enum. Enum values are unique values, not a # placeholder for a string or numeric value. However an Enum value is returned in # a JSON response as a string. type __EnumValue { name: String! description: String isDeprecated: Boolean! deprecationReason: String } # Object and Interface types are described by a list of Fields, each of which has # a name, potentially a list of arguments, and a return type. type __Field { name: String! description: String args: [__InputValue!]! type: __Type! isDeprecated: Boolean! deprecationReason: String } # Arguments provided to Fields or Directives and the input fields of an # InputObject are represented as Input Values which describe their type and # optionally a default value. type __InputValue { name: String! description: String type: __Type! # A GraphQL-formatted string representing the default value for this input value. defaultValue: String isDeprecated: Boolean! deprecationReason: String } # A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all # available types and directives on the server, as well as the entry points for # query, mutation, and subscription operations. type __Schema { # A list of all types supported by this server. types: [__Type!]! # The type that query operations will be rooted at. queryType: __Type! # If this server supports mutation, the type that mutation operations will be rooted at. mutationType: __Type # If this server support subscription, the type that subscription operations will be rooted at. subscriptionType: __Type # A list of all directives supported by this server. directives: [__Directive!]! } # The fundamental unit of any GraphQL Schema is the type. There are many kinds of # types in GraphQL as represented by the ` + "`" + `__TypeKind` + "`" + ` enum. # # Depending on the kind of a type, certain fields describe information about that # type. Scalar types provide no information beyond a name and description, while # Enum types provide their values. Object and Interface types provide the fields # they describe. Abstract types, Union and Interface, provide the Object types # possible at runtime. List and NonNull types compose other types. type __Type { kind: __TypeKind! name: String description: String fields(includeDeprecated: Boolean = false): [__Field!] interfaces: [__Type!] possibleTypes: [__Type!] enumValues(includeDeprecated: Boolean = false): [__EnumValue!] inputFields: [__InputValue!] ofType: __Type specifiedByURL: String } # An enum describing what kind of type a given ` + "`" + `__Type` + "`" + ` is. enum __TypeKind { # Indicates this type is a scalar. SCALAR # Indicates this type is an object. ` + "`" + `fields` + "`" + ` and ` + "`" + `interfaces` + "`" + ` are valid fields. OBJECT # Indicates this type is an interface. ` + "`" + `fields` + "`" + ` and ` + "`" + `possibleTypes` + "`" + ` are valid fields. INTERFACE # Indicates this type is a union. ` + "`" + `possibleTypes` + "`" + ` is a valid field. UNION # Indicates this type is an enum. ` + "`" + `enumValues` + "`" + ` is a valid field. ENUM # Indicates this type is an input object. ` + "`" + `inputFields` + "`" + ` is a valid field. INPUT_OBJECT # Indicates this type is a list. ` + "`" + `ofType` + "`" + ` is a valid field. LIST # Indicates this type is a non-null. ` + "`" + `ofType` + "`" + ` is a valid field. NON_NULL } ` graphql-go-1.6.0/internal/schema/schema.go000066400000000000000000000417761475633407000204520ustar00rootroot00000000000000package schema import ( "fmt" "text/scanner" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/common" ) // New initializes an instance of Schema. func New() *ast.Schema { s := &ast.Schema{ SchemaDefinition: ast.SchemaDefinition{ EntryPointNames: make(map[string]string), }, Types: make(map[string]ast.NamedType), Directives: make(map[string]*ast.DirectiveDefinition), } m := newMeta() for n, t := range m.Types { s.Types[n] = t } for n, d := range m.Directives { s.Directives[n] = d } return s } func Parse(s *ast.Schema, schemaString string, useStringDescriptions bool) error { l := common.NewLexer(schemaString, useStringDescriptions) err := l.CatchSyntaxError(func() { parseSchema(s, l) }) if err != nil { return err } if err := mergeExtensions(s); err != nil { return err } for _, t := range s.Types { if err := resolveNamedType(s, t); err != nil { return err } } for _, d := range s.Directives { for _, arg := range d.Arguments { t, err := common.ResolveType(arg.Type, s.Resolve) if err != nil { return err } arg.Type = t } } // https://graphql.github.io/graphql-spec/June2018/#sec-Root-Operation-Types // > While any type can be the root operation type for a GraphQL operation, the type system definition language can // > omit the schema definition when the query, mutation, and subscription root types are named Query, Mutation, // > and Subscription respectively. if len(s.EntryPointNames) == 0 { if _, ok := s.Types["Query"]; ok { s.EntryPointNames["query"] = "Query" } if _, ok := s.Types["Mutation"]; ok { s.EntryPointNames["mutation"] = "Mutation" } if _, ok := s.Types["Subscription"]; ok { s.EntryPointNames["subscription"] = "Subscription" } } s.RootOperationTypes = make(map[string]ast.NamedType) for key, name := range s.EntryPointNames { t, ok := s.Types[name] if !ok { return errors.Errorf("type %q not found", name) } s.RootOperationTypes[key] = t } // Interface types need validation: https://spec.graphql.org/draft/#sec-Interfaces.Interfaces-Implementing-Interfaces for _, typeDef := range s.Types { switch t := typeDef.(type) { case *ast.InterfaceTypeDefinition: for i, implements := range t.Interfaces { typ, ok := s.Types[implements.Name] if !ok { return errors.Errorf("interface %q not found", implements) } inteface, ok := typ.(*ast.InterfaceTypeDefinition) if !ok { return errors.Errorf("type %q is not an interface", inteface) } for _, f := range inteface.Fields.Names() { if t.Fields.Get(f) == nil { return errors.Errorf("interface %q expects field %q but %q does not provide it", inteface.Name, f, t.Name) } } t.Interfaces[i] = inteface } default: continue } } for _, obj := range s.Objects { obj.Interfaces = make([]*ast.InterfaceTypeDefinition, len(obj.InterfaceNames)) if err := resolveDirectives(s, obj.Directives, "OBJECT"); err != nil { return err } for _, field := range obj.Fields { if err := resolveDirectives(s, field.Directives, "FIELD_DEFINITION"); err != nil { return err } } for i, intfName := range obj.InterfaceNames { t, ok := s.Types[intfName] if !ok { return errors.Errorf("interface %q not found", intfName) } intf, ok := t.(*ast.InterfaceTypeDefinition) if !ok { return errors.Errorf("type %q is not an interface", intfName) } for _, f := range intf.Fields.Names() { if obj.Fields.Get(f) == nil { return errors.Errorf("interface %q expects field %q but %q does not provide it", intfName, f, obj.Name) } } obj.Interfaces[i] = intf intf.PossibleTypes = append(intf.PossibleTypes, obj) } } for _, union := range s.Unions { if err := resolveDirectives(s, union.Directives, "UNION"); err != nil { return err } union.UnionMemberTypes = make([]*ast.ObjectTypeDefinition, len(union.TypeNames)) for i, name := range union.TypeNames { t, ok := s.Types[name] if !ok { return errors.Errorf("object type %q not found", name) } obj, ok := t.(*ast.ObjectTypeDefinition) if !ok { return errors.Errorf("type %q is not an object", name) } union.UnionMemberTypes[i] = obj } } for _, enum := range s.Enums { if err := resolveDirectives(s, enum.Directives, "ENUM"); err != nil { return err } for _, value := range enum.EnumValuesDefinition { if err := resolveDirectives(s, value.Directives, "ENUM_VALUE"); err != nil { return err } } } s.SchemaString = schemaString return nil } func ParseSchema(schemaString string, useStringDescriptions bool) (*ast.Schema, error) { s := New() err := Parse(s, schemaString, useStringDescriptions) return s, err } func mergeExtensions(s *ast.Schema) error { for _, ext := range s.Extensions { typ := s.Types[ext.Type.TypeName()] if typ == nil { return fmt.Errorf("trying to extend unknown type %q", ext.Type.TypeName()) } if typ.Kind() != ext.Type.Kind() { return fmt.Errorf("trying to extend type %q with type %q", typ.Kind(), ext.Type.Kind()) } switch og := typ.(type) { case *ast.ObjectTypeDefinition: e := ext.Type.(*ast.ObjectTypeDefinition) for _, field := range e.Fields { if og.Fields.Get(field.Name) != nil { return fmt.Errorf("extended field %q already exists", field.Name) } } og.Fields = append(og.Fields, e.Fields...) for _, en := range e.InterfaceNames { for _, on := range og.InterfaceNames { if on == en { return fmt.Errorf("interface %q implemented in the extension is already implemented in %q", on, og.Name) } } } og.InterfaceNames = append(og.InterfaceNames, e.InterfaceNames...) case *ast.InputObject: e := ext.Type.(*ast.InputObject) for _, field := range e.Values { if og.Values.Get(field.Name.Name) != nil { return fmt.Errorf("extended field %q already exists", field.Name) } } og.Values = append(og.Values, e.Values...) case *ast.InterfaceTypeDefinition: e := ext.Type.(*ast.InterfaceTypeDefinition) for _, field := range e.Fields { if og.Fields.Get(field.Name) != nil { return fmt.Errorf("extended field %s already exists", field.Name) } } og.Fields = append(og.Fields, e.Fields...) case *ast.Union: e := ext.Type.(*ast.Union) for _, en := range e.TypeNames { for _, on := range og.TypeNames { if on == en { return fmt.Errorf("union type %q already declared in %q", on, og.Name) } } } og.TypeNames = append(og.TypeNames, e.TypeNames...) case *ast.EnumTypeDefinition: e := ext.Type.(*ast.EnumTypeDefinition) for _, en := range e.EnumValuesDefinition { for _, on := range og.EnumValuesDefinition { if on.EnumValue == en.EnumValue { return fmt.Errorf("enum value %q already declared in %q", on.EnumValue, og.Name) } } } og.EnumValuesDefinition = append(og.EnumValuesDefinition, e.EnumValuesDefinition...) default: return fmt.Errorf(`unexpected %q, expecting "schema", "type", "enum", "interface", "union" or "input"`, og.TypeName()) } } return nil } func resolveNamedType(s *ast.Schema, t ast.NamedType) error { switch t := t.(type) { case *ast.ObjectTypeDefinition: for _, f := range t.Fields { if err := resolveField(s, f); err != nil { return err } } case *ast.InterfaceTypeDefinition: for _, f := range t.Fields { if err := resolveField(s, f); err != nil { return err } } if err := resolveDirectives(s, t.Directives, "INTERFACE"); err != nil { return err } case *ast.InputObject: if err := resolveInputObject(s, t.Values); err != nil { return err } if err := resolveDirectives(s, t.Directives, "INPUT_OBJECT"); err != nil { return err } case *ast.ScalarTypeDefinition: if err := resolveDirectives(s, t.Directives, "SCALAR"); err != nil { return err } } return nil } func resolveField(s *ast.Schema, f *ast.FieldDefinition) error { t, err := common.ResolveType(f.Type, s.Resolve) if err != nil { return err } f.Type = t if err := resolveDirectives(s, f.Directives, "FIELD_DEFINITION"); err != nil { return err } return resolveInputObject(s, f.Arguments) } func resolveDirectives(s *ast.Schema, directives ast.DirectiveList, loc string) error { alreadySeenNonRepeatable := make(map[string]struct{}) for _, d := range directives { dirName := d.Name.Name dd, ok := s.Directives[dirName] if !ok { return errors.Errorf("directive %q not found", dirName) } validLoc := false for _, l := range dd.Locations { if l == loc { validLoc = true break } } if !validLoc { return errors.Errorf("invalid location %q for directive %q (must be one of %v)", loc, dirName, dd.Locations) } for _, arg := range d.Arguments { if dd.Arguments.Get(arg.Name.Name) == nil { return errors.Errorf("invalid argument %q for directive %q", arg.Name.Name, dirName) } } for _, arg := range dd.Arguments { if _, ok := d.Arguments.Get(arg.Name.Name); !ok { d.Arguments = append(d.Arguments, &ast.Argument{Name: arg.Name, Value: arg.Default}) } } if dd.Repeatable { continue } if _, seen := alreadySeenNonRepeatable[dirName]; seen { return errors.Errorf(`non repeatable directive %q can not be repeated. Consider adding "repeatable".`, dirName) } alreadySeenNonRepeatable[dirName] = struct{}{} } return nil } func resolveInputObject(s *ast.Schema, values ast.ArgumentsDefinition) error { for _, v := range values { t, err := common.ResolveType(v.Type, s.Resolve) if err != nil { return err } v.Type = t if err := resolveDirectives(s, v.Directives, "ARGUMENT_DEFINITION"); err != nil { return err } } return nil } func parseSchema(s *ast.Schema, l *common.Lexer) { l.ConsumeWhitespace() for l.Peek() != scanner.EOF { desc := l.DescComment() switch x := l.ConsumeIdent(); x { case "schema": s.SchemaDefinition.Present = true s.SchemaDefinition.Loc = l.Location() s.SchemaDefinition.Desc = desc s.SchemaDefinition.Directives = common.ParseDirectives(l) l.ConsumeToken('{') for l.Peek() != '}' { name := l.ConsumeIdent() l.ConsumeToken(':') typ := l.ConsumeIdent() s.EntryPointNames[name] = typ } l.ConsumeToken('}') case "type": obj := parseObjectDef(l) obj.Desc = desc s.Types[obj.Name] = obj s.Objects = append(s.Objects, obj) case "interface": iface := parseInterfaceDef(l) iface.Desc = desc s.Types[iface.Name] = iface case "union": union := parseUnionDef(l) union.Desc = desc s.Types[union.Name] = union s.Unions = append(s.Unions, union) case "enum": enum := parseEnumDef(l) enum.Desc = desc s.Types[enum.Name] = enum s.Enums = append(s.Enums, enum) case "input": input := parseInputDef(l) input.Desc = desc s.Types[input.Name] = input case "scalar": loc := l.Location() name := l.ConsumeIdent() directives := common.ParseDirectives(l) s.Types[name] = &ast.ScalarTypeDefinition{Name: name, Desc: desc, Directives: directives, Loc: loc} case "directive": directive := parseDirectiveDef(l) directive.Desc = desc s.Directives[directive.Name] = directive case "extend": parseExtension(s, l) default: l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "schema", "type", "enum", "interface", "union", "input", "scalar" or "directive"`, x)) } } } func parseObjectDef(l *common.Lexer) *ast.ObjectTypeDefinition { object := &ast.ObjectTypeDefinition{Loc: l.Location(), Name: l.ConsumeIdent()} for { if l.Peek() == '{' { break } if l.Peek() == '@' { object.Directives = common.ParseDirectives(l) continue } if l.Peek() != scanner.Ident { break } l.ConsumeKeyword("implements") for l.Peek() != '{' && l.Peek() != '@' { if l.Peek() == '&' { l.ConsumeToken('&') } object.InterfaceNames = append(object.InterfaceNames, l.ConsumeIdent()) } } l.ConsumeToken('{') object.Fields = parseFieldsDef(l) l.ConsumeToken('}') return object } func parseInterfaceDef(l *common.Lexer) *ast.InterfaceTypeDefinition { i := &ast.InterfaceTypeDefinition{Loc: l.Location(), Name: l.ConsumeIdent()} if l.Peek() == scanner.Ident { l.ConsumeKeyword("implements") i.Interfaces = append(i.Interfaces, &ast.InterfaceTypeDefinition{Name: l.ConsumeIdent()}) for l.Peek() == '&' { l.ConsumeToken('&') i.Interfaces = append(i.Interfaces, &ast.InterfaceTypeDefinition{Name: l.ConsumeIdent()}) } } i.Directives = common.ParseDirectives(l) l.ConsumeToken('{') i.Fields = parseFieldsDef(l) l.ConsumeToken('}') return i } func parseUnionDef(l *common.Lexer) *ast.Union { union := &ast.Union{Loc: l.Location(), Name: l.ConsumeIdent()} union.Directives = common.ParseDirectives(l) l.ConsumeToken('=') if l.Peek() == '|' { l.ConsumeToken('|') } union.TypeNames = []string{l.ConsumeIdent()} for l.Peek() == '|' { l.ConsumeToken('|') union.TypeNames = append(union.TypeNames, l.ConsumeIdent()) } return union } func parseInputDef(l *common.Lexer) *ast.InputObject { i := &ast.InputObject{} i.Loc = l.Location() i.Name = l.ConsumeIdent() i.Directives = common.ParseDirectives(l) l.ConsumeToken('{') for l.Peek() != '}' { i.Values = append(i.Values, common.ParseInputValue(l)) } l.ConsumeToken('}') return i } func parseEnumDef(l *common.Lexer) *ast.EnumTypeDefinition { enum := &ast.EnumTypeDefinition{Loc: l.Location(), Name: l.ConsumeIdent()} enum.Directives = common.ParseDirectives(l) l.ConsumeToken('{') for l.Peek() != '}' { v := &ast.EnumValueDefinition{ Desc: l.DescComment(), Loc: l.Location(), EnumValue: l.ConsumeIdent(), Directives: common.ParseDirectives(l), } enum.EnumValuesDefinition = append(enum.EnumValuesDefinition, v) } l.ConsumeToken('}') return enum } func parseDirectiveDef(l *common.Lexer) *ast.DirectiveDefinition { l.ConsumeToken('@') loc := l.Location() d := &ast.DirectiveDefinition{Name: l.ConsumeIdent(), Loc: loc} if l.Peek() == '(' { l.ConsumeToken('(') for l.Peek() != ')' { v := common.ParseInputValue(l) d.Arguments = append(d.Arguments, v) } l.ConsumeToken(')') } switch x := l.ConsumeIdent(); x { case "on": // no-op; Go doesn't fallthrough by default case "repeatable": d.Repeatable = true l.ConsumeKeyword("on") default: l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "on" or "repeatable"`, x)) } for { loc := l.ConsumeIdent() if _, ok := legalDirectiveLocationNames[loc]; !ok { l.SyntaxError(fmt.Sprintf("%q is not a legal directive location (options: %v)", loc, legalDirectiveLocationNames)) } d.Locations = append(d.Locations, loc) if l.Peek() != '|' { break } l.ConsumeToken('|') } return d } func parseExtension(s *ast.Schema, l *common.Lexer) { loc := l.Location() switch x := l.ConsumeIdent(); x { case "schema": s.SchemaDefinition.Present = true s.SchemaDefinition.Directives = append(s.SchemaDefinition.Directives, common.ParseDirectives(l)...) if l.Peek() == '{' { // in schema extensions the body is optional l.ConsumeToken('{') for l.Peek() != '}' { name := l.ConsumeIdent() l.ConsumeToken(':') typ := l.ConsumeIdent() s.EntryPointNames[name] = typ } l.ConsumeToken('}') } case "type": obj := parseObjectDef(l) s.Extensions = append(s.Extensions, &ast.Extension{Type: obj, Loc: loc}) case "interface": iface := parseInterfaceDef(l) s.Extensions = append(s.Extensions, &ast.Extension{Type: iface, Loc: loc}) case "union": union := parseUnionDef(l) s.Extensions = append(s.Extensions, &ast.Extension{Type: union, Loc: loc}) case "enum": enum := parseEnumDef(l) s.Extensions = append(s.Extensions, &ast.Extension{Type: enum, Loc: loc}) case "input": input := parseInputDef(l) s.Extensions = append(s.Extensions, &ast.Extension{Type: input, Loc: loc}) default: // TODO: Add ScalarTypeDefinition when adding directives l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "schema", "type", "enum", "interface", "union" or "input"`, x)) } } func parseFieldsDef(l *common.Lexer) ast.FieldsDefinition { var fields ast.FieldsDefinition for l.Peek() != '}' { f := &ast.FieldDefinition{} f.Desc = l.DescComment() f.Loc = l.Location() f.Name = l.ConsumeIdent() if l.Peek() == '(' { l.ConsumeToken('(') for l.Peek() != ')' { f.Arguments = append(f.Arguments, common.ParseInputValue(l)) } l.ConsumeToken(')') } l.ConsumeToken(':') f.Type = common.ParseType(l) f.Directives = common.ParseDirectives(l) fields = append(fields, f) } return fields } var legalDirectiveLocationNames = map[string]struct{}{ "SCHEMA": {}, "SCALAR": {}, "OBJECT": {}, "FIELD_DEFINITION": {}, "ARGUMENT_DEFINITION": {}, "INTERFACE": {}, "UNION": {}, "ENUM": {}, "ENUM_VALUE": {}, "INPUT_OBJECT": {}, "INPUT_FIELD_DEFINITION": {}, "QUERY": {}, "MUTATION": {}, "SUBSCRIPTION": {}, "FIELD": {}, "FRAGMENT_DEFINITION": {}, "FRAGMENT_SPREAD": {}, "INLINE_FRAGMENT": {}, "VARIABLE_DEFINITION": {}, } graphql-go-1.6.0/internal/schema/schema_internal_test.go000066400000000000000000000411241475633407000233700ustar00rootroot00000000000000package schema import ( "reflect" "testing" "text/scanner" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/common" ) func TestParseSchemaDef(t *testing.T) { type testCase struct { description string definition string expected *ast.SchemaDefinition err *errors.QueryError } tests := []testCase{ { description: "Parses sdl without schema definition", definition: ` type Query { hello: String! } `, expected: &ast.SchemaDefinition{Present: false}, }, { description: "Schema definition present", definition: ` schema { query: Query } type Query{ hello: String! } `, expected: &ast.SchemaDefinition{Present: true, Loc: errors.Location{Line: 2, Column: 11}}, }, { description: "Schema definition present and has comment", definition: ` """ My cool schema. """ schema { query: Query } type Query{ hello: String! } `, expected: &ast.SchemaDefinition{ Desc: "My cool schema.", Present: true, Loc: errors.Location{Line: 5, Column: 11}, }, }, { description: "Schema definition present with comment and directives", definition: ` """ My cool schema. """ schema @dir1(arg1: "Val1", arg2: 5) { query: Query } type Query{ hello: String! } `, expected: &ast.SchemaDefinition{ Desc: "My cool schema.", Directives: ast.DirectiveList{ &ast.Directive{ Arguments: ast.ArgumentList{ { Name: ast.Ident{ Name: "arg1", Loc: errors.Location{Line: 5, Column: 17}, }, Value: &ast.PrimitiveValue{ Type: scanner.String, Text: `"Val1"`, Loc: errors.Location{Line: 5, Column: 23}, }, }, { Name: ast.Ident{ Name: "arg2", Loc: errors.Location{Line: 5, Column: 31}, }, Value: &ast.PrimitiveValue{ Type: scanner.Int, Text: "5", Loc: errors.Location{Line: 5, Column: 37}, }, }, }, Name: ast.Ident{ Name: "dir1", Loc: errors.Location{Line: 5, Column: 11}, }, }, }, Loc: errors.Location{Line: 5, Column: 11}, Present: true, }, }, { description: "Schema definition present with directives", definition: ` schema @dir3(a: 5) @dir4(b: 1) { query: Query } type Query{ hello: String! } `, expected: &ast.SchemaDefinition{ Directives: ast.DirectiveList{ &ast.Directive{ Arguments: ast.ArgumentList{ { Name: ast.Ident{ Name: "a", Loc: errors.Location{Line: 2, Column: 17}, }, Value: &ast.PrimitiveValue{ Type: scanner.Int, Text: "5", Loc: errors.Location{Line: 2, Column: 20}, }, }, }, Name: ast.Ident{ Name: "dir3", Loc: errors.Location{Line: 2, Column: 11}, }, }, &ast.Directive{ Arguments: ast.ArgumentList{ { Name: ast.Ident{ Name: "b", Loc: errors.Location{Line: 2, Column: 29}, }, Value: &ast.PrimitiveValue{ Type: scanner.Int, Text: "1", Loc: errors.Location{Line: 2, Column: 32}, }, }, }, Name: ast.Ident{ Name: "dir4", Loc: errors.Location{Line: 2, Column: 23}, }, }, }, Loc: errors.Location{Line: 2, Column: 11}, Present: true, }, }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { var actual *ast.SchemaDefinition lex := common.NewLexer(test.definition, true) parse := func() { s := New() parseSchema(s, lex) actual = &s.SchemaDefinition } err := lex.CatchSyntaxError(parse) compareErrors(t, test.err, err) compareSchemaDefinitions(t, test.expected, actual) }) } } func TestParseInterfaceDef(t *testing.T) { type testCase struct { description string definition string expected *ast.InterfaceTypeDefinition err *errors.QueryError } tests := []testCase{{ description: "Parses simple interface", definition: "Greeting { field: String }", expected: &ast.InterfaceTypeDefinition{ Name: "Greeting", Loc: errors.Location{Line: 1, Column: 1}, Fields: ast.FieldsDefinition{&ast.FieldDefinition{Name: "field"}}}, }} for _, test := range tests { t.Run(test.description, func(t *testing.T) { var actual *ast.InterfaceTypeDefinition lex := setup(t, test.definition) parse := func() { actual = parseInterfaceDef(lex) } err := lex.CatchSyntaxError(parse) compareErrors(t, test.err, err) compareInterfaces(t, test.expected, actual) }) } } // TestParseObjectDef tests the logic for parsing object types from the schema definition as // written in `parseObjectDef()`. func TestParseObjectDef(t *testing.T) { type testCase struct { description string definition string expected *ast.ObjectTypeDefinition err *errors.QueryError } tests := []testCase{{ description: "Parses type inheriting single interface", definition: "Hello implements World { field: String }", expected: &ast.ObjectTypeDefinition{Name: "Hello", Loc: errors.Location{Line: 1, Column: 1}, InterfaceNames: []string{"World"}}, }, { description: "Parses type inheriting multiple interfaces", definition: "Hello implements Wo & rld { field: String }", expected: &ast.ObjectTypeDefinition{Name: "Hello", Loc: errors.Location{Line: 1, Column: 1}, InterfaceNames: []string{"Wo", "rld"}}, }, { description: "Parses type inheriting multiple interfaces with leading ampersand", definition: "Hello implements & Wo & rld { field: String }", expected: &ast.ObjectTypeDefinition{Name: "Hello", Loc: errors.Location{Line: 1, Column: 1}, InterfaceNames: []string{"Wo", "rld"}}, }, { description: "Allows legacy SDL interfaces", definition: "Hello implements Wo, rld { field: String }", expected: &ast.ObjectTypeDefinition{Name: "Hello", Loc: errors.Location{Line: 1, Column: 1}, InterfaceNames: []string{"Wo", "rld"}}, }} for _, test := range tests { t.Run(test.description, func(t *testing.T) { var actual *ast.ObjectTypeDefinition lex := setup(t, test.definition) parse := func() { actual = parseObjectDef(lex) } err := lex.CatchSyntaxError(parse) compareErrors(t, test.err, err) compareObjects(t, test.expected, actual) }) } } func TestParseUnionDef(t *testing.T) { type testCase struct { description string definition string expected *ast.Union err *errors.QueryError } tests := []testCase{ { description: "Parses a union", definition: "Foo = Bar | Qux | Quux", expected: &ast.Union{ Name: "Foo", TypeNames: []string{"Bar", "Qux", "Quux"}, Loc: errors.Location{Line: 1, Column: 1}, }, }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { var actual *ast.Union lex := setup(t, test.definition) parse := func() { actual = parseUnionDef(lex) } err := lex.CatchSyntaxError(parse) compareErrors(t, test.err, err) compareUnions(t, test.expected, actual) }) } } func TestParseEnumDef(t *testing.T) { type testCase struct { description string definition string expected *ast.EnumTypeDefinition err *errors.QueryError } tests := []testCase{ { description: "parses EnumTypeDefinition on single line", definition: "Foo { BAR QUX }", expected: &ast.EnumTypeDefinition{ Name: "Foo", EnumValuesDefinition: []*ast.EnumValueDefinition{ { EnumValue: "BAR", Loc: errors.Location{Line: 1, Column: 7}, }, { EnumValue: "QUX", Loc: errors.Location{Line: 1, Column: 11}, }, }, Loc: errors.Location{Line: 1, Column: 1}, }, }, { description: "parses EnumtypeDefinition with new lines", definition: `Foo { BAR QUX }`, expected: &ast.EnumTypeDefinition{ Name: "Foo", EnumValuesDefinition: []*ast.EnumValueDefinition{ { EnumValue: "BAR", Loc: errors.Location{Line: 2, Column: 5}, }, { EnumValue: "QUX", Loc: errors.Location{Line: 3, Column: 5}, }, }, Loc: errors.Location{Line: 1, Column: 1}, }, }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { var actual *ast.EnumTypeDefinition lex := setup(t, test.definition) parse := func() { actual = parseEnumDef(lex) } err := lex.CatchSyntaxError(parse) compareErrors(t, test.err, err) compareEnumTypeDefs(t, test.expected, actual) }) } } func TestParseDirectiveDef(t *testing.T) { type testCase struct { description string definition string expected *ast.DirectiveDefinition err *errors.QueryError } tests := []*testCase{ { description: "parses DirectiveDefinition", definition: "@Foo on FIELD", expected: &ast.DirectiveDefinition{ Name: "Foo", Loc: errors.Location{Line: 1, Column: 2}, Locations: []string{"FIELD"}, }, }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { var actual *ast.DirectiveDefinition lex := setup(t, test.definition) parse := func() { actual = parseDirectiveDef(lex) } err := lex.CatchSyntaxError(parse) compareErrors(t, test.err, err) compareDirectiveDefinitions(t, test.expected, actual) }) } } func TestParseInputDef(t *testing.T) { type testCase struct { description string definition string expected *ast.InputObject err *errors.QueryError } tests := []testCase{ { description: "parses an input object type definition", definition: "Foo { qux: String }", expected: &ast.InputObject{ Name: "Foo", Values: nil, Loc: errors.Location{Line: 1, Column: 1}, }, }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { var actual *ast.InputObject lex := setup(t, test.definition) parse := func() { actual = parseInputDef(lex) } err := lex.CatchSyntaxError(parse) compareErrors(t, test.err, err) compareInputObjectTypeDefinition(t, test.expected, actual) }) } } func compareDirectiveDefinitions(t *testing.T, expected *ast.DirectiveDefinition, actual *ast.DirectiveDefinition) { t.Helper() if expected.Name != actual.Name { t.Fatalf("wrong DirectiveDefinition name: want %q, got %q", expected.Name, actual.Name) } if !reflect.DeepEqual(expected.Locations, actual.Locations) { t.Errorf("wrong DirectiveDefinition locations: want %v, got %v", expected.Locations, actual.Locations) } compareLoc(t, "DirectiveDefinition", expected.Loc, actual.Loc) } func compareInputObjectTypeDefinition(t *testing.T, expected, actual *ast.InputObject) { t.Helper() if expected.Name != actual.Name { t.Fatalf("wrong InputObject name: want %q, got %q", expected.Name, actual.Name) } compareLoc(t, "InputObjectTypeDefinition", expected.Loc, actual.Loc) } func compareEnumTypeDefs(t *testing.T, expected, actual *ast.EnumTypeDefinition) { t.Helper() if expected.Name != actual.Name { t.Fatalf("wrong EnumTypeDefinition name: want %q, got %q", expected.Name, actual.Name) } compareLoc(t, "EnumValueDefinition", expected.Loc, actual.Loc) for i, definition := range expected.EnumValuesDefinition { expectedValue, expectedLoc := definition.EnumValue, definition.Loc actualDef := actual.EnumValuesDefinition[i] if expectedValue != actualDef.EnumValue { t.Fatalf("wrong EnumValue: want %q, got %q", expectedValue, actualDef.EnumValue) } compareLoc(t, "EnumValue "+expectedValue, expectedLoc, actualDef.Loc) } } func compareLoc(t *testing.T, typeName string, expected, actual errors.Location) { t.Helper() if expected != actual { t.Errorf("wrong location on %s: want %v, got %v", typeName, expected, actual) } } func compareErrors(t *testing.T, expected, actual *errors.QueryError) { t.Helper() switch { case expected != nil && actual != nil: if expected.Message != actual.Message { t.Fatalf("wanted error message %q, got %q", expected.Message, actual.Message) } // TODO: Check error locations are as expected. case expected != nil && actual == nil: t.Fatalf("missing expected error: %q", expected) case expected == nil && actual != nil: t.Fatalf("got unexpected error: %q", actual) } } func compareInterfaces(t *testing.T, expected, actual *ast.InterfaceTypeDefinition) { t.Helper() if expected.Name != actual.Name { t.Errorf("wrong interface name: want %q, got %q", expected.Name, actual.Name) } compareLoc(t, "InterfaceTypeDefinition", expected.Loc, actual.Loc) if len(expected.Fields) != len(actual.Fields) { t.Fatalf("wanted %d field definitions, got %d", len(expected.Fields), len(actual.Fields)) } for i, f := range expected.Fields { if f.Name != actual.Fields[i].Name { t.Errorf("fields[%d]: wrong field name: want %q, got %q", i, f.Name, actual.Fields[i].Name) } } } func compareUnions(t *testing.T, expected, actual *ast.Union) { t.Helper() if expected.Name != actual.Name { t.Errorf("wrong object name: want %q, got %q", expected.Name, actual.Name) } if !reflect.DeepEqual(expected, actual) { t.Errorf("wrong type names: want %v, got %v", expected.TypeNames, actual.TypeNames) } } func compareObjects(t *testing.T, expected, actual *ast.ObjectTypeDefinition) { t.Helper() if expected.Name != actual.Name { t.Errorf("wrong object name: want %q, got %q", expected.Name, actual.Name) } if len(expected.InterfaceNames) != len(actual.InterfaceNames) { t.Fatalf( "wrong number of interface names: want %s, got %s", expected.InterfaceNames, actual.InterfaceNames, ) } for i, expectedName := range expected.InterfaceNames { actualName := actual.InterfaceNames[i] if expectedName != actualName { t.Errorf("wrong interface name: want %q, got %q", expectedName, actualName) } } } func compareSchemaDefinitions(t *testing.T, expected, actual *ast.SchemaDefinition) { t.Helper() if expected.Present != actual.Present { t.Errorf("wrong boolean Present: want %v, got %v", expected.Present, actual.Present) } if expected.Desc != actual.Desc { t.Errorf("wrong schema Desc: want %q, got %q", expected.Desc, actual.Desc) } if len(expected.RootOperationTypes) != len(actual.RootOperationTypes) { t.Fatalf( "wrong number of root operations: want %d, got %d", len(expected.RootOperationTypes), len(actual.RootOperationTypes), ) } for name, expectedOp := range expected.RootOperationTypes { actualOp := actual.RootOperationTypes[name] if actualOp != expectedOp { t.Errorf("wrong root operation name: want %q, got %q", actualOp, expectedOp) } } compareDirectiveList(t, "SchemaDef", expected.Directives, actual.Directives) compareLoc(t, "SchemaDef ", expected.Loc, actual.Loc) } func compareDirectiveList(t *testing.T, target string, expectedList, actualList ast.DirectiveList) { if len(expectedList) != len(actualList) { t.Fatalf( "wrong number of schema directives on %s: want %d, got %d", target, len(expectedList), len(actualList), ) } for i, expected := range expectedList { actual := actualList[i] if !reflect.DeepEqual(expectedList, actualList) { if expected.Name.Name != actual.Name.Name { t.Errorf("wrong directive name: want %q, got %q", expected.Name.Name, actual.Name.Name) } target := "directive " + expected.Name.Name + " on SchemaDefinition" compareLoc(t, target, expected.Name.Loc, actual.Name.Loc) compareArgumentList(t, target, expected.Arguments, actual.Arguments) } } } func compareArgumentList(t *testing.T, target string, expectedList, actualList ast.ArgumentList) { if len(expectedList) != len(actualList) { t.Fatalf( "wrong number of arguments on %s: want %d, got %d", target, len(expectedList), len(actualList), ) } for i, expected := range expectedList { actual := actualList[i] if !reflect.DeepEqual(expectedList, actualList) { if expected.Name.Name != actual.Name.Name { t.Errorf("wrong argument name on %s: want %q, got %q", target, expected.Name.Name, actual.Name.Name) } if expected.Value.String() != actual.Value.String() { t.Errorf("wrong argument value on %s: want %q, got %q", target, expected.Value, actual.Value) } compareDirectiveList(t, "argument "+expected.Name.Name+" on "+target, expected.Directives, actual.Directives) compareLoc(t, "argument "+expected.Name.Name+" on "+target, expected.Name.Loc, actual.Name.Loc) compareLoc(t, "value on argument "+expected.Name.Name+" on "+target, expected.Value.Location(), actual.Value.Location()) } } } func setup(t *testing.T, def string) *common.Lexer { t.Helper() lex := common.NewLexer(def, false) lex.ConsumeWhitespace() return lex } graphql-go-1.6.0/internal/schema/schema_test.go000066400000000000000000001005711475633407000214760ustar00rootroot00000000000000package schema_test import ( "fmt" "strings" "testing" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/internal/schema" ) func TestParse(t *testing.T) { for _, test := range []struct { name string sdl string useStringDescriptions bool validateError func(err error) error validateSchema func(s *ast.Schema) error }{ { name: "Parses interface definition", sdl: "interface Greeting { message: String! }", validateSchema: func(s *ast.Schema) error { const typeName = "Greeting" typ, ok := s.Types[typeName].(*ast.InterfaceTypeDefinition) if !ok { return fmt.Errorf("interface %q not found", typeName) } if want, have := 1, len(typ.Fields); want != have { return fmt.Errorf("invalid number of fields: want %d, have %d", want, have) } const fieldName = "message" if typ.Fields[0].Name != fieldName { return fmt.Errorf("field %q not found", fieldName) } return nil }, }, { name: "Parses implementing type without providing required fields", sdl: ` interface Greeting { message: String! } type Welcome implements Greeting { }`, validateError: func(err error) error { if err == nil { return fmt.Errorf("want error, have ") } if want, have := `graphql: interface "Greeting" expects field "message" but "Welcome" does not provide it`, err.Error(); want != have { return fmt.Errorf("unexpected error: want %q, have %q", want, have) } return nil }, }, { name: "Parses type with description string", sdl: ` "Single line description." type Type { field: String }`, useStringDescriptions: true, validateSchema: func(s *ast.Schema) error { const typeName = "Type" typ, ok := s.Types[typeName].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", typeName) } if want, have := "Single line description.", typ.Description(); want != have { return fmt.Errorf("invalid description: want %q, have %q", want, have) } return nil }, }, { name: "Parses type with simple multi-line 'BlockString' description", sdl: ` """ Multi-line description. """ type Type { field: String }`, useStringDescriptions: true, validateSchema: func(s *ast.Schema) error { const typeName = "Type" typ, ok := s.Types[typeName].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", typeName) } if want, have := "Multi-line description.", typ.Description(); want != have { return fmt.Errorf("invalid description: want %q, have %q", want, have) } return nil }, }, { name: "Parses type with empty multi-line 'BlockString' description", sdl: ` """ """ type Type { field: String }`, useStringDescriptions: true, validateSchema: func(s *ast.Schema) error { const typeName = "Type" typ, ok := s.Types[typeName].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", typeName) } if want, have := "", typ.Description(); want != have { return fmt.Errorf("invalid description: want %q, have %q", want, have) } return nil }, }, { name: "Parses type with multi-line 'BlockString' description", sdl: ` """ First line of the description. Second line of the description. query { code { example } } Notes: * First note * Second note """ type Type { field: String }`, useStringDescriptions: true, validateSchema: func(s *ast.Schema) error { const typeName = "Type" typ, ok := s.Types[typeName].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", typeName) } want := "First line of the description.\n\nSecond line of the description.\n\n\tquery {\n\t\tcode {\n\t\t\texample\n\t\t}\n\t}\n\nNotes:\n\n * First note\n * Second note" if have := typ.Description(); want != have { return fmt.Errorf("invalid description: want %q, have %q", want, have) } return nil }, }, { name: "Parses type with un-indented multi-line 'BlockString' description", sdl: ` """ First line of the description. Second line of the description. """ type Type { field: String }`, useStringDescriptions: true, validateSchema: func(s *ast.Schema) error { const typeName = "Type" typ, ok := s.Types[typeName].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", typeName) } want := "First line of the description.\n\nSecond line of the description." if have := typ.Description(); want != have { return fmt.Errorf("invalid description: want %q, have %q", want, have) } return nil }, }, { name: "Parses type with space-indented multi-line 'BlockString' description", sdl: ` """ First line of the description. Second line of the description. query { code { example } } """ type Type { field: String }`, useStringDescriptions: true, validateSchema: func(s *ast.Schema) error { const typeName = "Type" typ, ok := s.Types[typeName].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", typeName) } want := "First line of the description.\n\nSecond line of the description.\n\n query {\n code {\n example\n }\n }" if have := typ.Description(); want != have { return fmt.Errorf("invalid description: want %q, have %q", want, have) } return nil }, }, { name: "Parses type with multi-line 'BlockString' description and ignores comments", sdl: ` """ Multi-line description with ignored comments. """ # This comment should be ignored. type Type { field: String }`, useStringDescriptions: true, validateSchema: func(s *ast.Schema) error { const typeName = "Type" typ, ok := s.Types[typeName].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", typeName) } if want, have := "Multi-line description with ignored comments.", typ.Description(); want != have { return fmt.Errorf("invalid description: want %q, have %q", want, have) } return nil }, }, { name: "Parses type invalid syntax", sdl: ` type U = T `, validateError: func(err error) error { msg := `graphql: syntax error: unexpected "=", expecting "{" (line 2, column 11)` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Description is correctly parsed for non-described types", sdl: ` "Some description." scalar MyInt type Type { field: String }`, useStringDescriptions: true, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["Type"] if !ok { return fmt.Errorf("type %q not found", "Type") } if want, have := "", typ.Description(); want != have { return fmt.Errorf("description does not match: want %q, have %q ", want, have) } return nil }, }, { name: "Multi-line comment is correctly parsed", sdl: ` # Multi-line # comment. " This description should be ignored. " scalar MyInt type Type { field: String }`, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["MyInt"] if !ok { return fmt.Errorf("scalar %q not found", "MyInt") } if want, have := "Multi-line\ncomment.", typ.Description(); want != have { return fmt.Errorf("description does not match: want %q, have %q ", want, have) } typ, ok = s.Types["Type"] if !ok { return fmt.Errorf("type %q not found", "Type") } if want, have := "", typ.Description(); want != have { return fmt.Errorf("description does not match: want %q, have %q ", want, have) } return nil }, }, { name: "Default Root schema", sdl: ` type Query { hello: String! } type Mutation { concat(a: String!, b: String!): String! } `, validateSchema: func(s *ast.Schema) error { typq, ok := s.Types["Query"].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", "Query") } helloField := typq.Fields.Get("hello") if helloField == nil { return fmt.Errorf("field %q not found", "hello") } if helloField.Type.String() != "String!" { return fmt.Errorf("field %q has an invalid type: %q", "hello", helloField.Type.String()) } typm, ok := s.Types["Mutation"].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", "Mutation") } concatField := typm.Fields.Get("concat") if concatField == nil { return fmt.Errorf("field %q not found", "concat") } if concatField.Type.String() != "String!" { return fmt.Errorf("field %q has an invalid type: %q", "concat", concatField.Type.String()) } if len(concatField.Arguments) != 2 || concatField.Arguments[0] == nil || concatField.Arguments[1] == nil || concatField.Arguments[0].Type.String() != "String!" || concatField.Arguments[1].Type.String() != "String!" { return fmt.Errorf("field %q has an invalid args: %+v", "concat", concatField.Arguments) } return nil }, }, { name: "Extend type", sdl: ` type Query { hello: String! } extend type Query { world: String! }`, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["Query"].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", "Query") } helloField := typ.Fields.Get("hello") if helloField == nil { return fmt.Errorf("field %q not found", "hello") } if helloField.Type.String() != "String!" { return fmt.Errorf("field %q has an invalid type: %q", "hello", helloField.Type.String()) } worldField := typ.Fields.Get("world") if worldField == nil { return fmt.Errorf("field %q not found", "world") } if worldField.Type.String() != "String!" { return fmt.Errorf("field %q has an invalid type: %q", "world", worldField.Type.String()) } return nil }, }, { name: "Extend schema", sdl: ` schema { query: Query } type Query { hello: String! } extend schema { mutation: Mutation } type Mutation { concat(a: String!, b: String!): String! } `, validateSchema: func(s *ast.Schema) error { typq, ok := s.Types["Query"].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", "Query") } helloField := typq.Fields.Get("hello") if helloField == nil { return fmt.Errorf("field %q not found", "hello") } if helloField.Type.String() != "String!" { return fmt.Errorf("field %q has an invalid type: %q", "hello", helloField.Type.String()) } typm, ok := s.Types["Mutation"].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", "Mutation") } concatField := typm.Fields.Get("concat") if concatField == nil { return fmt.Errorf("field %q not found", "concat") } if concatField.Type.String() != "String!" { return fmt.Errorf("field %q has an invalid type: %q", "concat", concatField.Type.String()) } if len(concatField.Arguments) != 2 || concatField.Arguments[0] == nil || concatField.Arguments[1] == nil || concatField.Arguments[0].Type.String() != "String!" || concatField.Arguments[1].Type.String() != "String!" { return fmt.Errorf("field %q has an invalid args: %+v", "concat", concatField.Arguments) } return nil }, }, { name: "Extend type with interface implementation", sdl: ` interface Named { name: String! } type Product { id: ID! } extend type Product implements Named { name: String! }`, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["Product"].(*ast.ObjectTypeDefinition) if !ok { return fmt.Errorf("type %q not found", "Product") } idField := typ.Fields.Get("id") if idField == nil { return fmt.Errorf("field %q not found", "id") } if idField.Type.String() != "ID!" { return fmt.Errorf("field %q has an invalid type: %q", "id", idField.Type.String()) } nameField := typ.Fields.Get("name") if nameField == nil { return fmt.Errorf("field %q not found", "name") } if nameField.Type.String() != "String!" { return fmt.Errorf("field %q has an invalid type: %q", "name", nameField.Type.String()) } ifc, ok := s.Types["Named"].(*ast.InterfaceTypeDefinition) if !ok { return fmt.Errorf("type %q not found", "Named") } nameField = ifc.Fields.Get("name") if nameField == nil { return fmt.Errorf("field %q not found", "name") } if nameField.Type.String() != "String!" { return fmt.Errorf("field %q has an invalid type: %q", "name", nameField.Type.String()) } return nil }, }, { name: "Extend union type", sdl: ` type Named { name: String! } type Numbered { num: Int! } union Item = Named | Numbered type Coloured { Colour: String! } extend union Item = Coloured `, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["Item"].(*ast.Union) if !ok { return fmt.Errorf("type %q not found", "Item") } if len(typ.UnionMemberTypes) != 3 { return fmt.Errorf("Expected 3 possible types, but instead got %d types", len(typ.UnionMemberTypes)) } posible := map[string]struct{}{ "Coloured": {}, "Named": {}, "Numbered": {}, } for _, pt := range typ.UnionMemberTypes { if _, ok := posible[pt.Name]; !ok { return fmt.Errorf("Unexpected possible type %q", pt.Name) } } return nil }, }, { name: "Extend enum type", sdl: ` enum Currencies{ AUD USD EUR } extend enum Currencies { BGN GBP } `, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["Currencies"].(*ast.EnumTypeDefinition) if !ok { return fmt.Errorf("enum %q not found", "Currencies") } if len(typ.EnumValuesDefinition) != 5 { return fmt.Errorf("Expected 5 enum values, but instead got %d types", len(typ.EnumValuesDefinition)) } posible := map[string]struct{}{ "AUD": {}, "USD": {}, "EUR": {}, "BGN": {}, "GBP": {}, } for _, v := range typ.EnumValuesDefinition { if _, ok := posible[v.EnumValue]; !ok { return fmt.Errorf("Unexpected enum value %q", v.EnumValue) } } return nil }, }, { name: "Extend incompatible type", sdl: ` type Query { hello: String! } extend interface Query { name: String! }`, validateError: func(err error) error { msg := `trying to extend type "OBJECT" with type "INTERFACE"` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Extend type already implements an interface", sdl: ` interface Named { name: String! } type Product implements Named { id: ID! name: String! } extend type Product implements Named { }`, validateError: func(err error) error { msg := `interface "Named" implemented in the extension is already implemented in "Product"` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Extend union already contains type", sdl: ` type Named { name: String! } type Numbered { num: Int! } union Item = Named | Numbered type Coloured { Colour: String! } extend union Item = Coloured | Named `, validateError: func(err error) error { msg := `union type "Named" already declared in "Item"` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Extend union contains type", sdl: ` type Named { name: String! } type Numbered { num: Int! } union Item = Named | Numbered type Coloured { Colour: String! } extend union Item = Coloured `, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["Item"].(*ast.Union) if !ok { return fmt.Errorf("type %q not found", "Item") } if len(typ.UnionMemberTypes) != 3 { return fmt.Errorf("Expected 3 possible types, but instead got %d types", len(typ.UnionMemberTypes)) } posible := map[string]struct{}{ "Coloured": {}, "Named": {}, "Numbered": {}, } for _, pt := range typ.UnionMemberTypes { if _, ok := posible[pt.Name]; !ok { return fmt.Errorf("Unexpected possible type %q", pt.Name) } } return nil }, }, { name: "Extend input", sdl: ` input Product { id: ID! name: String! } extend input Product { category: Category! tags: [String!]! = ["sale", "shoes"] } input Category { id: ID! name: String! } `, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["Product"].(*ast.InputObject) if !ok { return fmt.Errorf("type %q not found", "Product") } if len(typ.Values) != 4 { return fmt.Errorf("Expected 4 fields, but instead got %d types", len(typ.Values)) } posible := map[string]struct{}{ "id": {}, "name": {}, "category": {}, "tags": {}, } for _, pt := range typ.Values { if _, ok := posible[pt.Name.Name]; !ok { return fmt.Errorf("Unexpected possible type %q", pt.Name) } } categoryField := typ.Values.Get("category") if categoryField == nil { return fmt.Errorf("field %q not found", "category") } if categoryField.Type.String() != "Category!" { return fmt.Errorf("expected type %q, but got %q", "Category!", categoryField.Type.String()) } if categoryField.Type.Kind() != "NON_NULL" { return fmt.Errorf("expected kind %q, but got %q", "NON_NULL", categoryField.Type.Kind()) } return nil }, }, { name: "Extend enum value already exists", sdl: ` enum Currencies{ AUD USD EUR } extend enum Currencies { AUD }`, validateError: func(err error) error { msg := `enum value "AUD" already declared in "Currencies"` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Extend input field already exists", sdl: ` input Product{ name: String! } extend input Product { name: String! }`, validateError: func(err error) error { msg := `extended field {"name" {'\x06' '\x05'}} already exists` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Extend field already exists", sdl: ` interface Named { name: String! } type Product implements Named { id: ID! name: String! } extend type Product { name: String! }`, validateError: func(err error) error { msg := `extended field "name" already exists` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Extend interface type", sdl: ` interface Product { id: ID! name: String! } extend interface Product { category: String! } `, validateSchema: func(s *ast.Schema) error { typ, ok := s.Types["Product"].(*ast.InterfaceTypeDefinition) if !ok { return fmt.Errorf("type %q not found", "Product") } if len(typ.Fields) != 3 { return fmt.Errorf("Expected 3 fields, but instead got %d types", len(typ.Fields)) } fields := map[string]struct{}{ "id": {}, "name": {}, "category": {}, } for _, f := range typ.Fields { if _, ok := fields[f.Name]; !ok { return fmt.Errorf("Unexpected field %q", f.Name) } } return nil }, }, { name: "Extend unknown type", sdl: ` extend type User { name: String! } `, validateError: func(err error) error { msg := `trying to extend unknown type "User"` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Extend invalid syntax", sdl: ` extend invalid Node { id: ID! } `, validateError: func(err error) error { msg := `graphql: syntax error: unexpected "invalid", expecting "schema", "type", "enum", "interface", "union" or "input" (line 2, column 19)` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Parses directives", sdl: ` directive @objectdirective on OBJECT directive @fielddirective on FIELD_DEFINITION directive @enumdirective on ENUM directive @uniondirective on UNION directive @directive on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @repeatabledirective repeatable on SCALAR interface NamedEntity @directive { name: String } scalar Time @directive type Photo @objectdirective { id: ID! @deprecated @fielddirective } type Person implements NamedEntity @objectdirective { name: String } enum Direction @enumdirective { NORTH @deprecated EAST SOUTH WEST } union Union @uniondirective = Photo | Person scalar Mass @repeatabledirective @repeatabledirective `, validateSchema: func(s *ast.Schema) error { namedEntityDirectives := s.Types["NamedEntity"].(*ast.InterfaceTypeDefinition).Directives if len(namedEntityDirectives) != 1 || namedEntityDirectives[0].Name.Name != "directive" { return fmt.Errorf("missing directive on NamedEntity interface, expected @directive but got %v", namedEntityDirectives) } timeDirectives := s.Types["Time"].(*ast.ScalarTypeDefinition).Directives if len(timeDirectives) != 1 || timeDirectives[0].Name.Name != "directive" { return fmt.Errorf("missing directive on Time scalar, expected @directive but got %v", timeDirectives) } photo := s.Types["Photo"].(*ast.ObjectTypeDefinition) photoDirectives := photo.Directives if len(photoDirectives) != 1 || photoDirectives[0].Name.Name != "objectdirective" { return fmt.Errorf("missing directive on Time scalar, expected @objectdirective but got %v", photoDirectives) } if len(photo.Fields.Get("id").Directives) != 2 { return fmt.Errorf("expected Photo.id to have 2 directives but got %v", photoDirectives) } directionDirectives := s.Types["Direction"].(*ast.EnumTypeDefinition).Directives if len(directionDirectives) != 1 || directionDirectives[0].Name.Name != "enumdirective" { return fmt.Errorf("missing directive on Direction enum, expected @enumdirective but got %v", directionDirectives) } unionDirectives := s.Types["Union"].(*ast.Union).Directives if len(unionDirectives) != 1 || unionDirectives[0].Name.Name != "uniondirective" { return fmt.Errorf("missing directive on Union union, expected @uniondirective but got %v", unionDirectives) } massDirectives := s.Types["Mass"].(*ast.ScalarTypeDefinition).Directives if len(massDirectives) != 2 || massDirectives[0].Name.Name != "repeatabledirective" || massDirectives[1].Name.Name != "repeatabledirective" { return fmt.Errorf("missing directive on Repeatable scalar, expected @repeatabledirective @repeatabledirective but got %v", massDirectives) } return nil }, }, { name: "Sets Directive.Repeatable if `repeatable` keyword is given", sdl: ` directive @nonrepeatabledirective on SCALAR directive @repeatabledirective repeatable on SCALAR `, validateSchema: func(s *ast.Schema) error { if dir := s.Directives["nonrepeatabledirective"]; dir.Repeatable { return fmt.Errorf("did not expect directive to be repeatable: %v", dir) } if dir := s.Directives["repeatabledirective"]; !dir.Repeatable { return fmt.Errorf("expected directive to be repeatable: %v", dir) } return nil }, }, { name: "Directive definition does not allow double-`repeatable`", sdl: ` directive @mydirective repeatable repeatable SCALAR scalar MyScalar @mydirective `, validateError: func(err error) error { msg := `graphql: syntax error: unexpected "repeatable", expecting "on" (line 2, column 38)` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Directive definition does not allow double-`on` instead of `repeatable on`", sdl: ` directive @mydirective on on SCALAR scalar MyScalar @mydirective `, validateError: func(err error) error { prefix := `graphql: syntax error: "on" is not a legal directive location` if err == nil || !strings.HasPrefix(err.Error(), prefix) { return fmt.Errorf("expected error starting with %q, but got %q", prefix, err) } return nil }, }, { name: "Disallow repeat of a directive if it is not `repeatable`", sdl: ` directive @nonrepeatabledirective on FIELD_DEFINITION type Foo { bar: String @nonrepeatabledirective @nonrepeatabledirective } `, validateError: func(err error) error { prefix := `graphql: non repeatable directive "nonrepeatabledirective" can not be repeated. Consider adding "repeatable"` if err == nil || !strings.HasPrefix(err.Error(), prefix) { return fmt.Errorf("expected error starting with %q, but got %q", prefix, err) } return nil }, }, { name: "Decorating scalar with an undeclared directive should return an error", sdl: ` scalar S @undeclareddirective `, validateError: func(err error) error { prefix := `graphql: directive "undeclareddirective" not found` if err == nil || !strings.HasPrefix(err.Error(), prefix) { return fmt.Errorf("expected error starting with %q, but got %q", prefix, err) } return nil }, }, { name: "Decorating argument with an undeclared directive should return an error", sdl: ` type Query { hello(name: String! @undeclareddirective): String! } `, validateError: func(err error) error { prefix := `graphql: directive "undeclareddirective" not found` if err == nil || !strings.HasPrefix(err.Error(), prefix) { return fmt.Errorf("expected error starting with %q, but got %q", prefix, err) } return nil }, }, { name: "Decorating input object with an undeclared directive should return an error", sdl: ` input InputObject @undeclareddirective{} `, validateError: func(err error) error { prefix := `graphql: directive "undeclareddirective" not found` if err == nil || !strings.HasPrefix(err.Error(), prefix) { return fmt.Errorf("expected error starting with %q, but got %q", prefix, err) } return nil }, }, { name: "Decorating interface with an undeclared directive should return an error", sdl: ` interface I @undeclareddirective {} `, validateError: func(err error) error { prefix := `graphql: directive "undeclareddirective" not found` if err == nil || !strings.HasPrefix(err.Error(), prefix) { return fmt.Errorf("expected error starting with %q, but got %q", prefix, err) } return nil }, }, } { t.Run(test.name, func(t *testing.T) { s, err := schema.ParseSchema(test.sdl, test.useStringDescriptions) if err != nil { if test.validateError == nil { t.Fatal(err) } if err2 := test.validateError(err); err2 != nil { t.Fatal(err2) } } if test.validateSchema != nil { if err := test.validateSchema(s); err != nil { t.Fatal(err) } } }) } } func TestInterfaceImplementsInterface(t *testing.T) { for _, tt := range []struct { name string sdl string useStringDescriptions bool validateError func(err error) error validateSchema func(s *ast.Schema) error }{ { name: "Parses interface implementing other interface", sdl: ` interface Foo { field: String! } interface Bar implements Foo { field: String! } `, validateSchema: func(s *ast.Schema) error { const implementedInterfaceName = "Bar" typ, ok := s.Types[implementedInterfaceName].(*ast.InterfaceTypeDefinition) if !ok { return fmt.Errorf("interface %q not found", implementedInterfaceName) } if len(typ.Fields) != 1 { return fmt.Errorf("invalid number of fields: want %d, have %d", 1, len(typ.Fields)) } const fieldName = "field" if typ.Fields[0].Name != fieldName { return fmt.Errorf("field %q not found", fieldName) } if len(typ.Interfaces) != 1 { return fmt.Errorf("invalid number of implementing interfaces found on %q: want %d, have %d", implementedInterfaceName, 1, len(typ.Interfaces)) } const implementingInterfaceName = "Foo" if typ.Interfaces[0].Name != implementingInterfaceName { return fmt.Errorf("interface %q not found", implementingInterfaceName) } return nil }, }, { name: "Parses interface transitively implementing an interface that implements an interface", sdl: ` interface Foo { field: String! } interface Bar implements Foo { field: String! } interface Baz implements Bar & Foo { field: String! } `, validateSchema: func(s *ast.Schema) error { const implementedInterfaceName = "Baz" typ, ok := s.Types[implementedInterfaceName].(*ast.InterfaceTypeDefinition) if !ok { return fmt.Errorf("interface %q not found", implementedInterfaceName) } if len(typ.Fields) != 1 { return fmt.Errorf("invalid number of fields: want %d, have %d", 1, len(typ.Fields)) } const fieldName = "field" if typ.Fields[0].Name != fieldName { return fmt.Errorf("field %q not found", fieldName) } if len(typ.Interfaces) != 2 { return fmt.Errorf("invalid number of implementing interfaces found on %q: want %d, have %d", implementedInterfaceName, 2, len(typ.Interfaces)) } const firstImplementingInterfaceName = "Bar" if typ.Interfaces[0].Name != firstImplementingInterfaceName { return fmt.Errorf("first interface %q not found", firstImplementingInterfaceName) } const secondImplementingInterfaceName = "Foo" if typ.Interfaces[1].Name != secondImplementingInterfaceName { return fmt.Errorf("second interface %q not found", secondImplementingInterfaceName) } return nil }, }, { name: "Transitively implemented interfaces must also be defined on an implementing type or interface", sdl: ` interface A { message: String! } interface B implements A { message: String! name: String! } interface C implements B { message: String! name: String! hug: Boolean! } `, validateError: func(err error) error { msg := `graphql: interface "C" must explicitly implement transitive interface "A"` if err == nil || err.Error() != msg { return fmt.Errorf("expected error %q, but got %q", msg, err) } return nil }, }, { name: "Unions can be defined with a leading pipe", sdl: ` type Named { name: String! } type Numbered { num: Int! } union Item1 = | Named | Numbered union Item2 = | Named | Numbered `, validateSchema: func(s *ast.Schema) error { for _, itemName := range []string{"Item1", "Item2"} { typ, ok := s.Types[itemName].(*ast.Union) if !ok { return fmt.Errorf("type %q not found", "Item") } if len(typ.UnionMemberTypes) != 2 { return fmt.Errorf("Expected 2 possible types, but instead got %d types", len(typ.UnionMemberTypes)) } posible := map[string]struct{}{ "Named": {}, "Numbered": {}, } for _, pt := range typ.UnionMemberTypes { if _, ok := posible[pt.Name]; !ok { return fmt.Errorf("Unexpected possible type %q", pt.Name) } } } return nil }, }, } { t.Run(tt.name, func(t *testing.T) { s, err := schema.ParseSchema(tt.sdl, tt.useStringDescriptions) if err != nil { if tt.validateError == nil { t.Fatal(err) } if err2 := tt.validateError(err); err2 != nil { t.Fatal(err2) } } if tt.validateSchema != nil { if err2 := tt.validateSchema(s); err2 != nil { t.Fatal(err2) } } }) } } graphql-go-1.6.0/internal/validation/000077500000000000000000000000001475633407000175365ustar00rootroot00000000000000graphql-go-1.6.0/internal/validation/suggestion.go000066400000000000000000000025131475633407000222550ustar00rootroot00000000000000package validation import ( "fmt" "sort" "strconv" "strings" ) func makeSuggestion(prefix string, options []string, input string) string { var selected []string distances := make(map[string]int) for _, opt := range options { distance := levenshteinDistance(input, opt) threshold := max(len(input)/2, max(len(opt)/2, 2)) if distance < threshold { selected = append(selected, opt) distances[opt] = distance } } if len(selected) == 0 { return "" } sort.Slice(selected, func(i, j int) bool { return distances[selected[i]] < distances[selected[j]] }) parts := make([]string, len(selected)) for i, opt := range selected { parts[i] = strconv.Quote(opt) } if len(parts) > 1 { parts[len(parts)-1] = "or " + parts[len(parts)-1] } return fmt.Sprintf(" %s %s?", prefix, strings.Join(parts, ", ")) } func levenshteinDistance(s1, s2 string) int { column := make([]int, len(s1)+1) for y := range s1 { column[y+1] = y + 1 } for x, rx := range s2 { column[0] = x + 1 lastdiag := x for y, ry := range s1 { olddiag := column[y+1] if rx != ry { lastdiag++ } column[y+1] = min(column[y+1]+1, min(column[y]+1, lastdiag)) lastdiag = olddiag } } return column[len(s1)] } func min(a, b int) int { if a < b { return a } return b } func max(a, b int) int { if a > b { return a } return b } graphql-go-1.6.0/internal/validation/testdata/000077500000000000000000000000001475633407000213475ustar00rootroot00000000000000graphql-go-1.6.0/internal/validation/testdata/LICENSE000066400000000000000000000031441475633407000223560ustar00rootroot00000000000000The files in this testdata directory are derived from the graphql-js project: https://github.com/graphql/graphql-js BSD License For GraphQL software Copyright (c) 2015, Facebook, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Facebook nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.graphql-go-1.6.0/internal/validation/testdata/README.md000066400000000000000000000016331475633407000226310ustar00rootroot00000000000000# graphql-js testdata Test cases are generated here by extracting them from [graphql-js] into JSON that we can use to drive Go tests. ## Usage To update the testdata, run the following command within the `testdata` directory: ```sh go generate . ``` ## How it works A Node.js project is used to pull in graphql-js as a dependency, and automatically patch that via `patch-package`. These patches replace the `mocha` test functions `describe`, `it`, assertions and the test `harness`. This allows the expectations to be captured, and written to a JSON file. These test cases in the JSON file are then used to drive the Go tests. ## Updating patches With changes to [graphql-js], the patches may need to be updated. To do this, update the `graphql` dependency under `node_modules`, and sync the patches with the following command: ```sh npm run create-patches ``` [graphql-js]: https://github.com/graphql/graphql-js graphql-go-1.6.0/internal/validation/testdata/export.ts000066400000000000000000000067321475633407000232500ustar00rootroot00000000000000import * as fs from 'node:fs'; import { createHash } from 'crypto'; import { printSchema } from 'graphql/src/utilities/printSchema.js'; import { schemas, testCases } from 'graphql/src/validation/__tests__/harness.js'; // TODO: Fix test failures. // require('graphql/src/validation/__tests__/ExecutableDefinitions-test'); import 'graphql/src/validation/__tests__/FieldsOnCorrectTypeRule-test.js'; import 'graphql/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.js'; import 'graphql/src/validation/__tests__/KnownArgumentNamesRule-test.js'; import 'graphql/src/validation/__tests__/KnownDirectivesRule-test.js'; import 'graphql/src/validation/__tests__/KnownFragmentNamesRule-test.js'; import 'graphql/src/validation/__tests__/KnownTypeNamesRule-test.js'; import 'graphql/src/validation/__tests__/LoneAnonymousOperationRule-test.js'; import 'graphql/src/validation/__tests__/NoFragmentCyclesRule-test.js'; import 'graphql/src/validation/__tests__/NoUndefinedVariablesRule-test.js'; import 'graphql/src/validation/__tests__/NoUnusedFragmentsRule-test.js'; import 'graphql/src/validation/__tests__/NoUnusedVariablesRule-test.js'; import 'graphql/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.js'; import 'graphql/src/validation/__tests__/PossibleFragmentSpreadsRule-test.js'; import 'graphql/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.js'; import 'graphql/src/validation/__tests__/ScalarLeafsRule-test.js'; // TODO: Add support for subscriptions. // require('graphql/src/validation/__tests__/SingleFieldSubscriptions-test.js'); import 'graphql/src/validation/__tests__/UniqueArgumentNamesRule-test.js'; import 'graphql/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.js'; import 'graphql/src/validation/__tests__/UniqueFragmentNamesRule-test.js'; import 'graphql/src/validation/__tests__/UniqueInputFieldNamesRule-test.js'; import 'graphql/src/validation/__tests__/UniqueOperationNamesRule-test.js'; import 'graphql/src/validation/__tests__/UniqueVariableNamesRule-test.js'; // TODO: Fix test failures. // require('graphql/src/validation/__tests__/ValuesofCorrectType-test'); import 'graphql/src/validation/__tests__/VariablesAreInputTypesRule-test.js'; // TODO: Fix test failures. // require('graphql/src/validation/__tests__/VariablesDefaultValueAllowed-test'); import 'graphql/src/validation/__tests__/VariablesInAllowedPositionRule-test.js'; // Schema index in the source array can be unstable, as its dependent on the order they are used in the registered test // files. The SHA256 of the schema will change if there are any changes to the content, but is a better reference than // the schema indexes all changing when a new schema is inserted. let s = schemas().map(s => { const sdl = printSchema(s) const id = createHash('sha256').update(sdl).digest('base64'); const v: { id: string; sdl: string; } = {id: id, sdl: sdl}; return v; }); const tests = testCases().map(c => { const schema = s[c.schema]; return { name: c.name, rule: c.rule, schema: schema.id, query: c.query, errors: c.errors, } }); // Order based on the schema string to provide semi-stable ordering s = s.sort((a, b) => a.sdl.localeCompare(b.sdl)); let output = JSON.stringify({schemas: s, tests: tests}, null, 2) output = output.replace(/ Did you mean to use an inline fragment on [^?]*\?/g, ''); // Ignore suggested types in errors, which graphql-go does not currently produce. output = output.replace(/ Did you mean \\"[A-Z].*\"\?/g, ''); fs.writeFileSync("tests.json", output); graphql-go-1.6.0/internal/validation/testdata/gen.go000066400000000000000000000004461475633407000224530ustar00rootroot00000000000000// Package testdata copies validation test cases from the reference implementation at // github.com/graphql/graphql-js. // // Pre-requisites: // - nodejs // - npm >= 5.2.0 (for use of npx) // // Usage: // $ go generate . package testdata //go:generate npm install //go:generate npm run export graphql-go-1.6.0/internal/validation/testdata/package-lock.json000066400000000000000000001147251475633407000245750ustar00rootroot00000000000000{ "name": "testdata", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "testdata", "version": "0.0.0", "hasInstallScript": true, "license": "BSD-3-Clause", "devDependencies": { "@types/chai": "4.3.4", "chai": "^4.3.7", "graphql": "github:graphql/graphql-js", "patch-package": "^8.0.0", "postinstall-postinstall": "^2.1.0", "ts-node": "10.9.1", "typescript": "5.0.4" } }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, "engines": { "node": ">=12" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "node_modules/@tsconfig/node10": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.10.tgz", "integrity": "sha512-PiaIWIoPvO6qm6t114ropMCagj6YAF24j9OkCA2mJDXFnlionEwhsBCJ8yek4aib575BI3OkART/90WsgHgLWw==", "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "node_modules/@types/chai": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, "node_modules/@types/node": { "version": "20.11.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", "dev": true, "peer": true, "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/acorn-walk": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, "engines": { "node": ">=0.4.0" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, "engines": { "node": "*" } }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, "engines": { "node": ">= 4.0.0" } }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { "fill-range": "^7.0.1" }, "engines": { "node": ">=8" } }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/chai": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", "deep-eql": "^4.1.3", "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", "type-detect": "^4.0.8" }, "engines": { "node": ">=4" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, "dependencies": { "get-func-name": "^2.0.2" }, "engines": { "node": "*" } }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], "engines": { "node": ">=8" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, "dependencies": { "type-detect": "^4.0.0" }, "engines": { "node": ">=6" } }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, "engines": { "node": ">=0.3.1" } }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, "dependencies": { "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "engines": { "node": ">= 0.4" } }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/find-yarn-workspace-root": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", "dev": true, "dependencies": { "micromatch": "^4.0.2" } }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { "node": ">=10" } }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-func-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { "node": "*" } }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/graphql": { "version": "17.0.0-alpha.3", "resolved": "git+ssh://git@github.com/graphql/graphql-js.git#a81e6238fa3f3dd317cb33572dcc97020376c329", "dev": true, "license": "MIT", "engines": { "node": "^16.19.0 || ^18.14.0 || >=19.7.0" } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, "bin": { "is-docker": "cli.js" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "engines": { "node": ">=0.12.0" } }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, "dependencies": { "is-docker": "^2.0.0" }, "engines": { "node": ">=8" } }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/json-stable-stringify": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", "dev": true, "dependencies": { "call-bind": "^1.0.5", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/jsonify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/klaw-sync": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", "dev": true, "dependencies": { "graceful-fs": "^4.1.11" } }, "node_modules/loupe": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "dependencies": { "get-func-name": "^2.0.1" } }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "engines": { "node": ">= 0.4" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" } }, "node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "dev": true, "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/patch-package": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", "dev": true, "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^9.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "rimraf": "^2.6.3", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.0.33", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" }, "engines": { "node": ">=14", "npm": ">5" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, "engines": { "node": "*" } }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/postinstall-postinstall": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz", "integrity": "sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==", "dev": true, "hasInstallScript": true }, "node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "dependencies": { "os-tmpdir": "~1.0.2" }, "engines": { "node": ">=0.6.0" } }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", "ts-node-cwd": "dist/bin-cwd.js", "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "peerDependenciesMeta": { "@swc/core": { "optional": true }, "@swc/wasm": { "optional": true } } }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/typescript": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=12.20" } }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true, "peer": true }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "node_modules/yaml": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", "dev": true, "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14" } }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "engines": { "node": ">=6" } } } } graphql-go-1.6.0/internal/validation/testdata/package.json000066400000000000000000000016271475633407000236430ustar00rootroot00000000000000{ "name": "testdata", "type": "module", "version": "0.0.0", "description": "Extracts test data from github.com/graphql/graphql-js", "main": "", "devDependencies": { "@types/chai": "4.3.4", "chai": "^4.3.7", "graphql": "github:graphql/graphql-js", "patch-package": "^8.0.0", "postinstall-postinstall": "^2.1.0", "ts-node": "10.9.1", "typescript": "5.0.4" }, "scripts": { "create-patches": "npx patch-package graphql", "export": "node --loader ts-node/esm export.ts", "postinstall": "patch-package", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/graph-gophers/graphql-go.git" }, "author": "", "license": "BSD-3-Clause", "bugs": { "url": "https://github.com/graph-gophers/graphql-go/issues" }, "homepage": "https://github.com/graph-gophers/graphql-go#readme" } graphql-go-1.6.0/internal/validation/testdata/patches/000077500000000000000000000000001475633407000227765ustar00rootroot00000000000000graphql-go-1.6.0/internal/validation/testdata/patches/graphql+17.0.0-alpha.3.patch000066400000000000000000000505111475633407000274220ustar00rootroot00000000000000diff --git a/node_modules/graphql/src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts b/node_modules/graphql/src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts index 1c7fbc0..1771fff 100644 --- a/node_modules/graphql/src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts @@ -1,5 +1,4 @@ import { expect } from 'chai'; -import { describe, it } from 'mocha'; import { parse } from '../../language/parser.js'; @@ -10,7 +9,7 @@ import { buildSchema } from '../../utilities/buildASTSchema.js'; import { FieldsOnCorrectTypeRule } from '../rules/FieldsOnCorrectTypeRule.js'; import { validate } from '../validate.js'; -import { expectValidationErrorsWithSchema } from './harness.js'; +import { describe, it, expectValidationErrorsWithSchema } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrorsWithSchema( diff --git a/node_modules/graphql/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.ts b/node_modules/graphql/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.ts index 849b662..1e37004 100644 --- a/node_modules/graphql/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { FragmentsOnCompositeTypesRule } from '../rules/FragmentsOnCompositeTypesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(FragmentsOnCompositeTypesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/KnownArgumentNamesRule-test.ts b/node_modules/graphql/src/validation/__tests__/KnownArgumentNamesRule-test.ts index 0fcffec..8aa40d3 100644 --- a/node_modules/graphql/src/validation/__tests__/KnownArgumentNamesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/KnownArgumentNamesRule-test.ts @@ -1,5 +1,3 @@ -import { describe, it } from 'mocha'; - import type { GraphQLSchema } from '../../type/schema.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; @@ -10,6 +8,8 @@ import { } from '../rules/KnownArgumentNamesRule.js'; import { + describe, + it, expectSDLValidationErrors, expectValidationErrors, } from './harness.js'; diff --git a/node_modules/graphql/src/validation/__tests__/KnownDirectivesRule-test.ts b/node_modules/graphql/src/validation/__tests__/KnownDirectivesRule-test.ts index a3bbc19..7b04bba 100644 --- a/node_modules/graphql/src/validation/__tests__/KnownDirectivesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/KnownDirectivesRule-test.ts @@ -1,5 +1,3 @@ -import { describe, it } from 'mocha'; - import type { GraphQLSchema } from '../../type/schema.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; @@ -7,6 +5,8 @@ import { buildSchema } from '../../utilities/buildASTSchema.js'; import { KnownDirectivesRule } from '../rules/KnownDirectivesRule.js'; import { + describe, + it, expectSDLValidationErrors, expectValidationErrorsWithSchema, } from './harness.js'; diff --git a/node_modules/graphql/src/validation/__tests__/KnownFragmentNamesRule-test.ts b/node_modules/graphql/src/validation/__tests__/KnownFragmentNamesRule-test.ts index ad0158b..bc5a356 100644 --- a/node_modules/graphql/src/validation/__tests__/KnownFragmentNamesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/KnownFragmentNamesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { KnownFragmentNamesRule } from '../rules/KnownFragmentNamesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(KnownFragmentNamesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/KnownTypeNamesRule-test.ts b/node_modules/graphql/src/validation/__tests__/KnownTypeNamesRule-test.ts index 0440c09..69d950a 100644 --- a/node_modules/graphql/src/validation/__tests__/KnownTypeNamesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/KnownTypeNamesRule-test.ts @@ -1,5 +1,3 @@ -import { describe, it } from 'mocha'; - import type { GraphQLSchema } from '../../type/schema.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; @@ -7,6 +5,8 @@ import { buildSchema } from '../../utilities/buildASTSchema.js'; import { KnownTypeNamesRule } from '../rules/KnownTypeNamesRule.js'; import { + describe, + it, expectSDLValidationErrors, expectValidationErrors, expectValidationErrorsWithSchema, diff --git a/node_modules/graphql/src/validation/__tests__/LoneAnonymousOperationRule-test.ts b/node_modules/graphql/src/validation/__tests__/LoneAnonymousOperationRule-test.ts index f60750b..df1d288 100644 --- a/node_modules/graphql/src/validation/__tests__/LoneAnonymousOperationRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/LoneAnonymousOperationRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { LoneAnonymousOperationRule } from '../rules/LoneAnonymousOperationRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(LoneAnonymousOperationRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/NoFragmentCyclesRule-test.ts b/node_modules/graphql/src/validation/__tests__/NoFragmentCyclesRule-test.ts index 69f951c..f1da380 100644 --- a/node_modules/graphql/src/validation/__tests__/NoFragmentCyclesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/NoFragmentCyclesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { NoFragmentCyclesRule } from '../rules/NoFragmentCyclesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(NoFragmentCyclesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/NoUndefinedVariablesRule-test.ts b/node_modules/graphql/src/validation/__tests__/NoUndefinedVariablesRule-test.ts index c6ed758..920df75 100644 --- a/node_modules/graphql/src/validation/__tests__/NoUndefinedVariablesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/NoUndefinedVariablesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { NoUndefinedVariablesRule } from '../rules/NoUndefinedVariablesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(NoUndefinedVariablesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/NoUnusedFragmentsRule-test.ts b/node_modules/graphql/src/validation/__tests__/NoUnusedFragmentsRule-test.ts index d20f99e..75cda8b 100644 --- a/node_modules/graphql/src/validation/__tests__/NoUnusedFragmentsRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/NoUnusedFragmentsRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { NoUnusedFragmentsRule } from '../rules/NoUnusedFragmentsRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(NoUnusedFragmentsRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/NoUnusedVariablesRule-test.ts b/node_modules/graphql/src/validation/__tests__/NoUnusedVariablesRule-test.ts index 47dac39..96b0e3e 100644 --- a/node_modules/graphql/src/validation/__tests__/NoUnusedVariablesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/NoUnusedVariablesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { NoUnusedVariablesRule } from '../rules/NoUnusedVariablesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(NoUnusedVariablesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts b/node_modules/graphql/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts index ecb56a1..cde5f38 100644 --- a/node_modules/graphql/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts @@ -1,5 +1,3 @@ -import { describe, it } from 'mocha'; - import type { GraphQLSchema } from '../../type/schema.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; @@ -7,6 +5,8 @@ import { buildSchema } from '../../utilities/buildASTSchema.js'; import { OverlappingFieldsCanBeMergedRule } from '../rules/OverlappingFieldsCanBeMergedRule.js'; import { + describe, + it, expectValidationErrors, expectValidationErrorsWithSchema, } from './harness.js'; diff --git a/node_modules/graphql/src/validation/__tests__/PossibleFragmentSpreadsRule-test.ts b/node_modules/graphql/src/validation/__tests__/PossibleFragmentSpreadsRule-test.ts index bd3bb63..2633a2f 100644 --- a/node_modules/graphql/src/validation/__tests__/PossibleFragmentSpreadsRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/PossibleFragmentSpreadsRule-test.ts @@ -1,10 +1,8 @@ -import { describe, it } from 'mocha'; - import { buildSchema } from '../../utilities/buildASTSchema.js'; import { PossibleFragmentSpreadsRule } from '../rules/PossibleFragmentSpreadsRule.js'; -import { expectValidationErrorsWithSchema } from './harness.js'; +import { describe, it, expectValidationErrorsWithSchema } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrorsWithSchema( diff --git a/node_modules/graphql/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.ts b/node_modules/graphql/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.ts index 6f0d223..fb7101e 100644 --- a/node_modules/graphql/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.ts @@ -1,5 +1,3 @@ -import { describe, it } from 'mocha'; - import type { GraphQLSchema } from '../../type/schema.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; @@ -10,6 +8,8 @@ import { } from '../rules/ProvidedRequiredArgumentsRule.js'; import { + describe, + it, expectSDLValidationErrors, expectValidationErrors, } from './harness.js'; diff --git a/node_modules/graphql/src/validation/__tests__/ScalarLeafsRule-test.ts b/node_modules/graphql/src/validation/__tests__/ScalarLeafsRule-test.ts index fd000b9..279bbe5 100644 --- a/node_modules/graphql/src/validation/__tests__/ScalarLeafsRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/ScalarLeafsRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { ScalarLeafsRule } from '../rules/ScalarLeafsRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(ScalarLeafsRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/UniqueArgumentNamesRule-test.ts b/node_modules/graphql/src/validation/__tests__/UniqueArgumentNamesRule-test.ts index 8a08f98..bf8c4a7 100644 --- a/node_modules/graphql/src/validation/__tests__/UniqueArgumentNamesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/UniqueArgumentNamesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { UniqueArgumentNamesRule } from '../rules/UniqueArgumentNamesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(UniqueArgumentNamesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.ts b/node_modules/graphql/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.ts index fd67ff8..ff54aa3 100644 --- a/node_modules/graphql/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.ts @@ -1,5 +1,3 @@ -import { describe, it } from 'mocha'; - import { parse } from '../../language/parser.js'; import type { GraphQLSchema } from '../../type/schema.js'; @@ -9,6 +7,8 @@ import { extendSchema } from '../../utilities/extendSchema.js'; import { UniqueDirectivesPerLocationRule } from '../rules/UniqueDirectivesPerLocationRule.js'; import { + describe, + it, expectSDLValidationErrors, expectValidationErrorsWithSchema, testSchema, diff --git a/node_modules/graphql/src/validation/__tests__/UniqueFragmentNamesRule-test.ts b/node_modules/graphql/src/validation/__tests__/UniqueFragmentNamesRule-test.ts index 30b0f5f..515c220 100644 --- a/node_modules/graphql/src/validation/__tests__/UniqueFragmentNamesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/UniqueFragmentNamesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { UniqueFragmentNamesRule } from '../rules/UniqueFragmentNamesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(UniqueFragmentNamesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts b/node_modules/graphql/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts index 3dc56ff..1e2cc24 100644 --- a/node_modules/graphql/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { UniqueInputFieldNamesRule } from '../rules/UniqueInputFieldNamesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(UniqueInputFieldNamesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/UniqueOperationNamesRule-test.ts b/node_modules/graphql/src/validation/__tests__/UniqueOperationNamesRule-test.ts index ef24487..17f8e56 100644 --- a/node_modules/graphql/src/validation/__tests__/UniqueOperationNamesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/UniqueOperationNamesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { UniqueOperationNamesRule } from '../rules/UniqueOperationNamesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(UniqueOperationNamesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/UniqueVariableNamesRule-test.ts b/node_modules/graphql/src/validation/__tests__/UniqueVariableNamesRule-test.ts index f23c778..3b74faf 100644 --- a/node_modules/graphql/src/validation/__tests__/UniqueVariableNamesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/UniqueVariableNamesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { UniqueVariableNamesRule } from '../rules/UniqueVariableNamesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(UniqueVariableNamesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/VariablesAreInputTypesRule-test.ts b/node_modules/graphql/src/validation/__tests__/VariablesAreInputTypesRule-test.ts index 8027a35..1305c6d 100644 --- a/node_modules/graphql/src/validation/__tests__/VariablesAreInputTypesRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/VariablesAreInputTypesRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { VariablesAreInputTypesRule } from '../rules/VariablesAreInputTypesRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(VariablesAreInputTypesRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts b/node_modules/graphql/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts index 1646774..a4358db 100644 --- a/node_modules/graphql/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts +++ b/node_modules/graphql/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts @@ -1,8 +1,6 @@ -import { describe, it } from 'mocha'; - import { VariablesInAllowedPositionRule } from '../rules/VariablesInAllowedPositionRule.js'; -import { expectValidationErrors } from './harness.js'; +import { describe, it, expectValidationErrors } from './harness.js'; function expectErrors(queryStr: string) { return expectValidationErrors(VariablesInAllowedPositionRule, queryStr); diff --git a/node_modules/graphql/src/validation/__tests__/harness.ts b/node_modules/graphql/src/validation/__tests__/harness.ts index b7710ff..8287c61 100644 --- a/node_modules/graphql/src/validation/__tests__/harness.ts +++ b/node_modules/graphql/src/validation/__tests__/harness.ts @@ -8,7 +8,7 @@ import type { GraphQLSchema } from '../../type/schema.js'; import { buildSchema } from '../../utilities/buildASTSchema.js'; -import { validate, validateSDL } from '../validate.js'; +import { validateSDL } from '../validate.js'; import type { SDLValidationRule, ValidationRule, @@ -124,29 +124,89 @@ export const testSchema: GraphQLSchema = buildSchema(` directive @onField on FIELD `); +let _schemas: GraphQLSchema[] = []; +function registerSchema(schema: GraphQLSchema) { + for (let i = 0; i < _schemas.length; i++) { + if (_schemas[i] == schema) { + return i; + } + } + _schemas.push(schema); + return _schemas.length - 1; +} + +type Test = { + name: string, + rule: string, + schema: number, + query: string, + errors: any[], +}; + +let tests: Test[] = []; +let names: string[] = []; + export function expectValidationErrorsWithSchema( - schema: GraphQLSchema, - rule: ValidationRule, - queryStr: string, + schema: GraphQLSchema, + rule: ValidationRule, + queryStr: string, ): any { - const doc = parse(queryStr); - const errors = validate(schema, doc, [rule]); - return expectJSON(errors); + return { + toDeepEqual: (errors: any[]) => { + return tests.push({ + name: names.join('/'), + rule: rule.name, + schema: registerSchema(schema), + query: queryStr, + errors: errors, + }); + } + } } export function expectValidationErrors( - rule: ValidationRule, - queryStr: string, + rule: ValidationRule, + queryStr: string, ): any { return expectValidationErrorsWithSchema(testSchema, rule, queryStr); } export function expectSDLValidationErrors( - schema: Maybe, - rule: SDLValidationRule, - sdlStr: string, + schema: Maybe, + rule: SDLValidationRule, + sdlStr: string, ): any { const doc = parse(sdlStr); const errors = validateSDL(doc, schema, [rule]); return expectJSON(errors); } + +export function describe(name: string, f: Function) { + switch (name) { + case 'within schema language': + return; + } + names.push(name); + f(); + names.pop(); +} + +export function it(name: string, f: Function) { + switch (name) { + case 'ignores type definitions': + case 'reports correctly when a non-exclusive follows an exclusive': + case 'disallows differing subfields': + return; + } + names.push(name); + f(); + names.pop(); +} + +export function schemas(): GraphQLSchema[] { + return _schemas; +} + +export function testCases(): Test[] { + return tests; +} graphql-go-1.6.0/internal/validation/testdata/tests.json000066400000000000000000005425101475633407000234130ustar00rootroot00000000000000{ "schemas": [ { "id": "7vlHtoKB6yn7Yw66xpvtAb0M4Wjj5LmzeSTymz2zE5w=", "sdl": "directive @onQuery on QUERY\n\ndirective @onMutation on MUTATION\n\ndirective @onSubscription on SUBSCRIPTION\n\ndirective @onField on FIELD\n\ndirective @onFragmentDefinition on FRAGMENT_DEFINITION\n\ndirective @onFragmentSpread on FRAGMENT_SPREAD\n\ndirective @onInlineFragment on INLINE_FRAGMENT\n\ndirective @onVariableDefinition on VARIABLE_DEFINITION\n\ntype Query {\n dummy: String\n}" }, { "id": "FPFhio7mA1zxXhXd/8NCpbdwBrysWfBkn36GZ1uvDAA=", "sdl": "input SomeInput {\n a: String\n b: String\n}\n\ntype Query {\n someField(arg: SomeInput): String\n}" }, { "id": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "sdl": "interface Being {\n name: String\n}\n\ninterface Pet implements Being {\n name: String\n}\n\ntype Dog implements Being & Pet {\n name: String\n barkVolume: Int\n}\n\ntype Cat implements Being & Pet {\n name: String\n meowVolume: Int\n}\n\nunion CatOrDog = Cat | Dog\n\ninterface Intelligent {\n iq: Int\n}\n\ntype Human implements Being & Intelligent {\n name: String\n pets: [Pet]\n iq: Int\n}\n\ntype Alien implements Being & Intelligent {\n name: String\n iq: Int\n}\n\nunion DogOrHuman = Dog | Human\n\nunion HumanOrAlien = Human | Alien\n\ntype Query {\n catOrDog: CatOrDog\n dogOrHuman: DogOrHuman\n humanOrAlien: HumanOrAlien\n}" }, { "id": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "sdl": "interface Pet {\n name: String\n}\n\ntype Dog implements Pet {\n name: String\n nickname: String\n barkVolume: Int\n}\n\ntype Cat implements Pet {\n name: String\n nickname: String\n meowVolume: Int\n}\n\nunion CatOrDog = Cat | Dog\n\ntype Human {\n name: String\n pets: [Pet]\n}\n\ntype Query {\n human: Human\n}" }, { "id": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "sdl": "interface SomeBox {\n deepBox: SomeBox\n unrelatedField: String\n}\n\ntype StringBox implements SomeBox {\n scalar: String\n deepBox: StringBox\n unrelatedField: String\n listStringBox: [StringBox]\n stringBox: StringBox\n intBox: IntBox\n}\n\ntype IntBox implements SomeBox {\n scalar: Int\n deepBox: IntBox\n unrelatedField: String\n listStringBox: [StringBox]\n stringBox: StringBox\n intBox: IntBox\n}\n\ninterface NonNullStringBox1 {\n scalar: String!\n}\n\ntype NonNullStringBox1Impl implements SomeBox & NonNullStringBox1 {\n scalar: String!\n unrelatedField: String\n deepBox: SomeBox\n}\n\ninterface NonNullStringBox2 {\n scalar: String!\n}\n\ntype NonNullStringBox2Impl implements SomeBox & NonNullStringBox2 {\n scalar: String!\n unrelatedField: String\n deepBox: SomeBox\n}\n\ntype Connection {\n edges: [Edge]\n}\n\ntype Edge {\n node: Node\n}\n\ntype Node {\n id: ID\n name: String\n}\n\ntype Query {\n someBox: SomeBox\n connection: Connection\n}" }, { "id": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "sdl": "schema {\n query: QueryRoot\n}\n\ndirective @onField on FIELD\n\ndirective @directive on FIELD | FRAGMENT_DEFINITION\n\ndirective @directiveA on FIELD | FRAGMENT_DEFINITION\n\ndirective @directiveB on FIELD | FRAGMENT_DEFINITION\n\ndirective @repeatable repeatable on FIELD | FRAGMENT_DEFINITION\n\ninterface Mammal {\n mother: Mammal\n father: Mammal\n}\n\ninterface Pet {\n name(surname: Boolean): String\n}\n\ninterface Canine implements Mammal {\n name(surname: Boolean): String\n mother: Canine\n father: Canine\n}\n\nenum DogCommand {\n SIT\n HEEL\n DOWN\n}\n\ntype Dog implements Pet & Mammal & Canine {\n name(surname: Boolean): String\n nickname: String\n barkVolume: Int\n barks: Boolean\n doesKnowCommand(dogCommand: DogCommand): Boolean\n isHouseTrained(atOtherHomes: Boolean = true): Boolean\n isAtLocation(x: Int, y: Int): Boolean\n mother: Dog\n father: Dog\n}\n\ntype Cat implements Pet {\n name(surname: Boolean): String\n nickname: String\n meows: Boolean\n meowsVolume: Int\n furColor: FurColor\n}\n\nunion CatOrDog = Cat | Dog\n\ntype Human {\n name(surname: Boolean): String\n pets: [Pet]\n relatives: [Human]!\n}\n\nenum FurColor {\n BROWN\n BLACK\n TAN\n SPOTTED\n NO_FUR\n UNKNOWN\n}\n\ninput ComplexInput {\n requiredField: Boolean!\n nonNullField: Boolean! = false\n intField: Int\n stringField: String\n booleanField: Boolean\n stringListField: [String]\n}\n\ninput OneOfInput @oneOf {\n stringField: String\n intField: Int\n}\n\ntype ComplicatedArgs {\n intArgField(intArg: Int): String\n nonNullIntArgField(nonNullIntArg: Int!): String\n stringArgField(stringArg: String): String\n booleanArgField(booleanArg: Boolean): String\n enumArgField(enumArg: FurColor): String\n floatArgField(floatArg: Float): String\n idArgField(idArg: ID): String\n stringListArgField(stringListArg: [String]): String\n stringListNonNullArgField(stringListNonNullArg: [String!]): String\n complexArgField(complexArg: ComplexInput): String\n oneOfArgField(oneOfArg: OneOfInput): String\n multipleReqs(req1: Int!, req2: Int!): String\n nonNullFieldWithDefault(arg: Int! = 0): String\n multipleOpts(opt1: Int = 0, opt2: Int = 0): String\n multipleOptAndReq(req1: Int!, req2: Int!, opt1: Int = 0, opt2: Int = 0): String\n}\n\ntype QueryRoot {\n human(id: ID): Human\n dog: Dog\n cat: Cat\n pet: Pet\n catOrDog: CatOrDog\n complicatedArgs: ComplicatedArgs\n}" }, { "id": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "sdl": "schema {\n query: QueryRoot\n}\n\ndirective @onField on FIELD\n\ninterface Mammal {\n mother: Mammal\n father: Mammal\n}\n\ninterface Pet {\n name(surname: Boolean): String\n}\n\ninterface Canine implements Mammal {\n name(surname: Boolean): String\n mother: Canine\n father: Canine\n}\n\nenum DogCommand {\n SIT\n HEEL\n DOWN\n}\n\ntype Dog implements Pet & Mammal & Canine {\n name(surname: Boolean): String\n nickname: String\n barkVolume: Int\n barks: Boolean\n doesKnowCommand(dogCommand: DogCommand): Boolean\n isHouseTrained(atOtherHomes: Boolean = true): Boolean\n isAtLocation(x: Int, y: Int): Boolean\n mother: Dog\n father: Dog\n}\n\ntype Cat implements Pet {\n name(surname: Boolean): String\n nickname: String\n meows: Boolean\n meowsVolume: Int\n furColor: FurColor\n}\n\nunion CatOrDog = Cat | Dog\n\ntype Human {\n name(surname: Boolean): String\n pets: [Pet]\n relatives: [Human]!\n}\n\nenum FurColor {\n BROWN\n BLACK\n TAN\n SPOTTED\n NO_FUR\n UNKNOWN\n}\n\ninput ComplexInput {\n requiredField: Boolean!\n nonNullField: Boolean! = false\n intField: Int\n stringField: String\n booleanField: Boolean\n stringListField: [String]\n}\n\ninput OneOfInput @oneOf {\n stringField: String\n intField: Int\n}\n\ntype ComplicatedArgs {\n intArgField(intArg: Int): String\n nonNullIntArgField(nonNullIntArg: Int!): String\n stringArgField(stringArg: String): String\n booleanArgField(booleanArg: Boolean): String\n enumArgField(enumArg: FurColor): String\n floatArgField(floatArg: Float): String\n idArgField(idArg: ID): String\n stringListArgField(stringListArg: [String]): String\n stringListNonNullArgField(stringListNonNullArg: [String!]): String\n complexArgField(complexArg: ComplexInput): String\n oneOfArgField(oneOfArg: OneOfInput): String\n multipleReqs(req1: Int!, req2: Int!): String\n nonNullFieldWithDefault(arg: Int! = 0): String\n multipleOpts(opt1: Int = 0, opt2: Int = 0): String\n multipleOptAndReq(req1: Int!, req2: Int!, opt1: Int = 0, opt2: Int = 0): String\n}\n\ntype QueryRoot {\n human(id: ID): Human\n dog: Dog\n cat: Cat\n pet: Pet\n catOrDog: CatOrDog\n complicatedArgs: ComplicatedArgs\n}" }, { "id": "kxqeANW6SwgMzv7OfoER7Pehb0UyllcRPcBPGckVxRc=", "sdl": "type Foo {\n constructor: String\n}\n\ntype Query {\n foo: Foo\n}" }, { "id": "oECbw4BIP7gX1R+fCGRDCcqbO2FV/Uf0MhhE0EDAWIw=", "sdl": "type Query {\n foo: String\n}" }, { "id": "6+inK6Z824FQA4uBaUhxFXmMhc79+vKbamUi32/NtdQ=", "sdl": "type Query {\n someField(a: String, b: String): String\n}" } ], "tests": [ { "name": "Validate: Fields on correct type/Object field selection", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment objectFieldSelection on Dog {\n __typename\n name\n }\n ", "errors": [] }, { "name": "Validate: Fields on correct type/Aliased object field selection", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment aliasedObjectFieldSelection on Dog {\n tn : __typename\n otherName : name\n }\n ", "errors": [] }, { "name": "Validate: Fields on correct type/Interface field selection", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment interfaceFieldSelection on Pet {\n __typename\n name\n }\n ", "errors": [] }, { "name": "Validate: Fields on correct type/Aliased interface field selection", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment interfaceFieldSelection on Pet {\n otherName : name\n }\n ", "errors": [] }, { "name": "Validate: Fields on correct type/Lying alias selection", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment lyingAliasSelection on Dog {\n name : nickname\n }\n ", "errors": [] }, { "name": "Validate: Fields on correct type/Ignores fields on unknown type", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment unknownSelection on UnknownType {\n unknownField\n }\n ", "errors": [] }, { "name": "Validate: Fields on correct type/reports errors when type is known again", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment typeKnownAgain on Pet {\n unknown_pet_field {\n ... on Cat {\n unknown_cat_field\n }\n }\n }\n ", "errors": [ { "message": "Cannot query field \"unknown_pet_field\" on type \"Pet\".", "locations": [ { "line": 3, "column": 9 } ] }, { "message": "Cannot query field \"unknown_cat_field\" on type \"Cat\".", "locations": [ { "line": 5, "column": 13 } ] } ] }, { "name": "Validate: Fields on correct type/Field not defined on fragment", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment fieldNotDefined on Dog {\n meowVolume\n }\n ", "errors": [ { "message": "Cannot query field \"meowVolume\" on type \"Dog\". Did you mean \"barkVolume\"?", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Fields on correct type/Ignores deeply unknown field", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment deepFieldNotDefined on Dog {\n unknown_field {\n deeper_unknown_field\n }\n }\n ", "errors": [ { "message": "Cannot query field \"unknown_field\" on type \"Dog\".", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Fields on correct type/Sub-field not defined", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment subFieldNotDefined on Human {\n pets {\n unknown_field\n }\n }\n ", "errors": [ { "message": "Cannot query field \"unknown_field\" on type \"Pet\".", "locations": [ { "line": 4, "column": 11 } ] } ] }, { "name": "Validate: Fields on correct type/Field not defined on inline fragment", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment fieldNotDefined on Pet {\n ... on Dog {\n meowVolume\n }\n }\n ", "errors": [ { "message": "Cannot query field \"meowVolume\" on type \"Dog\". Did you mean \"barkVolume\"?", "locations": [ { "line": 4, "column": 11 } ] } ] }, { "name": "Validate: Fields on correct type/Aliased field target not defined", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment aliasedFieldTargetNotDefined on Dog {\n volume : mooVolume\n }\n ", "errors": [ { "message": "Cannot query field \"mooVolume\" on type \"Dog\". Did you mean \"barkVolume\"?", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Fields on correct type/Aliased lying field target not defined", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment aliasedLyingFieldTargetNotDefined on Dog {\n barkVolume : kawVolume\n }\n ", "errors": [ { "message": "Cannot query field \"kawVolume\" on type \"Dog\". Did you mean \"barkVolume\"?", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Fields on correct type/Not defined on interface", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment notDefinedOnInterface on Pet {\n tailLength\n }\n ", "errors": [ { "message": "Cannot query field \"tailLength\" on type \"Pet\".", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Fields on correct type/Defined on implementors but not on interface", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment definedOnImplementorsButNotInterface on Pet {\n nickname\n }\n ", "errors": [ { "message": "Cannot query field \"nickname\" on type \"Pet\".", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Fields on correct type/Meta field selection on union", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment directFieldSelectionOnUnion on CatOrDog {\n __typename\n }\n ", "errors": [] }, { "name": "Validate: Fields on correct type/Direct field selection on union", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment directFieldSelectionOnUnion on CatOrDog {\n directField\n }\n ", "errors": [ { "message": "Cannot query field \"directField\" on type \"CatOrDog\".", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Fields on correct type/Defined on implementors queried on union", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment definedOnImplementorsQueriedOnUnion on CatOrDog {\n name\n }\n ", "errors": [ { "message": "Cannot query field \"name\" on type \"CatOrDog\".", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Fields on correct type/valid field in inline fragment", "rule": "FieldsOnCorrectTypeRule", "schema": "QCY6hMOsxyATcac05rKqjKSVL1T9s4WCW+M3bkNLnbk=", "query": "\n fragment objectFieldSelection on Pet {\n ... on Dog {\n name\n }\n ... {\n name\n }\n }\n ", "errors": [] }, { "name": "Validate: Fragments on composite types/object is valid fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment validFragment on Dog {\n barks\n }\n ", "errors": [] }, { "name": "Validate: Fragments on composite types/interface is valid fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment validFragment on Pet {\n name\n }\n ", "errors": [] }, { "name": "Validate: Fragments on composite types/object is valid inline fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment validFragment on Pet {\n ... on Dog {\n barks\n }\n }\n ", "errors": [] }, { "name": "Validate: Fragments on composite types/interface is valid inline fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment validFragment on Mammal {\n ... on Canine {\n name\n }\n }\n ", "errors": [] }, { "name": "Validate: Fragments on composite types/inline fragment without type is valid", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment validFragment on Pet {\n ... {\n name\n }\n }\n ", "errors": [] }, { "name": "Validate: Fragments on composite types/union is valid fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment validFragment on CatOrDog {\n __typename\n }\n ", "errors": [] }, { "name": "Validate: Fragments on composite types/scalar is invalid fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarFragment on Boolean {\n bad\n }\n ", "errors": [ { "message": "Fragment \"scalarFragment\" cannot condition on non composite type \"Boolean\".", "locations": [ { "line": 2, "column": 34 } ] } ] }, { "name": "Validate: Fragments on composite types/enum is invalid fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarFragment on FurColor {\n bad\n }\n ", "errors": [ { "message": "Fragment \"scalarFragment\" cannot condition on non composite type \"FurColor\".", "locations": [ { "line": 2, "column": 34 } ] } ] }, { "name": "Validate: Fragments on composite types/input object is invalid fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment inputFragment on ComplexInput {\n stringField\n }\n ", "errors": [ { "message": "Fragment \"inputFragment\" cannot condition on non composite type \"ComplexInput\".", "locations": [ { "line": 2, "column": 33 } ] } ] }, { "name": "Validate: Fragments on composite types/scalar is invalid inline fragment type", "rule": "FragmentsOnCompositeTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment invalidFragment on Pet {\n ... on String {\n barks\n }\n }\n ", "errors": [ { "message": "Fragment cannot condition on non composite type \"String\".", "locations": [ { "line": 3, "column": 16 } ] } ] }, { "name": "Validate: Known argument names/single arg is known", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment argOnRequiredArg on Dog {\n doesKnowCommand(dogCommand: SIT)\n }\n ", "errors": [] }, { "name": "Validate: Known argument names/multiple args are known", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment multipleArgs on ComplicatedArgs {\n multipleReqs(req1: 1, req2: 2)\n }\n ", "errors": [] }, { "name": "Validate: Known argument names/ignores args of unknown fields", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment argOnUnknownField on Dog {\n unknownField(unknownArg: SIT)\n }\n ", "errors": [] }, { "name": "Validate: Known argument names/multiple args in reverse order are known", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment multipleArgsReverseOrder on ComplicatedArgs {\n multipleReqs(req2: 2, req1: 1)\n }\n ", "errors": [] }, { "name": "Validate: Known argument names/no args on optional arg", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment noArgOnOptionalArg on Dog {\n isHouseTrained\n }\n ", "errors": [] }, { "name": "Validate: Known argument names/args are known deeply", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog {\n doesKnowCommand(dogCommand: SIT)\n }\n human {\n pet {\n ... on Dog {\n doesKnowCommand(dogCommand: SIT)\n }\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: Known argument names/directive args are known", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog @skip(if: true)\n }\n ", "errors": [] }, { "name": "Validate: Known argument names/field args are invalid", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog @skip(unless: true)\n }\n ", "errors": [ { "message": "Unknown argument \"unless\" on directive \"@skip\".", "locations": [ { "line": 3, "column": 19 } ] } ] }, { "name": "Validate: Known argument names/directive without args is valid", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog @onField\n }\n ", "errors": [] }, { "name": "Validate: Known argument names/arg passed to directive without arg is reported", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog @onField(if: true)\n }\n ", "errors": [ { "message": "Unknown argument \"if\" on directive \"@onField\".", "locations": [ { "line": 3, "column": 22 } ] } ] }, { "name": "Validate: Known argument names/misspelled directive args are reported", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog @skip(iff: true)\n }\n ", "errors": [ { "message": "Unknown argument \"iff\" on directive \"@skip\". Did you mean \"if\"?", "locations": [ { "line": 3, "column": 19 } ] } ] }, { "name": "Validate: Known argument names/invalid arg name", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment invalidArgName on Dog {\n doesKnowCommand(unknown: true)\n }\n ", "errors": [ { "message": "Unknown argument \"unknown\" on field \"Dog.doesKnowCommand\".", "locations": [ { "line": 3, "column": 25 } ] } ] }, { "name": "Validate: Known argument names/misspelled arg name is reported", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment invalidArgName on Dog {\n doesKnowCommand(DogCommand: true)\n }\n ", "errors": [ { "message": "Unknown argument \"DogCommand\" on field \"Dog.doesKnowCommand\". Did you mean \"dogCommand\"?", "locations": [ { "line": 3, "column": 25 } ] } ] }, { "name": "Validate: Known argument names/unknown args amongst known args", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment oneGoodArgOneInvalidArg on Dog {\n doesKnowCommand(whoKnows: 1, dogCommand: SIT, unknown: true)\n }\n ", "errors": [ { "message": "Unknown argument \"whoKnows\" on field \"Dog.doesKnowCommand\".", "locations": [ { "line": 3, "column": 25 } ] }, { "message": "Unknown argument \"unknown\" on field \"Dog.doesKnowCommand\".", "locations": [ { "line": 3, "column": 55 } ] } ] }, { "name": "Validate: Known argument names/unknown args deeply", "rule": "KnownArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog {\n doesKnowCommand(unknown: true)\n }\n human {\n pet {\n ... on Dog {\n doesKnowCommand(unknown: true)\n }\n }\n }\n }\n ", "errors": [ { "message": "Unknown argument \"unknown\" on field \"Dog.doesKnowCommand\".", "locations": [ { "line": 4, "column": 27 } ] }, { "message": "Unknown argument \"unknown\" on field \"Dog.doesKnowCommand\".", "locations": [ { "line": 9, "column": 31 } ] } ] }, { "name": "Validate: Known directives/with no directives", "rule": "KnownDirectivesRule", "schema": "7vlHtoKB6yn7Yw66xpvtAb0M4Wjj5LmzeSTymz2zE5w=", "query": "\n query Foo {\n name\n ...Frag\n }\n\n fragment Frag on Dog {\n name\n }\n ", "errors": [] }, { "name": "Validate: Known directives/with standard directives", "rule": "KnownDirectivesRule", "schema": "7vlHtoKB6yn7Yw66xpvtAb0M4Wjj5LmzeSTymz2zE5w=", "query": "\n {\n human @skip(if: false) {\n name\n pets {\n ... on Dog @include(if: true) {\n name\n }\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: Known directives/with unknown directive", "rule": "KnownDirectivesRule", "schema": "7vlHtoKB6yn7Yw66xpvtAb0M4Wjj5LmzeSTymz2zE5w=", "query": "\n {\n human @unknown(directive: \"value\") {\n name\n }\n }\n ", "errors": [ { "message": "Unknown directive \"@unknown\".", "locations": [ { "line": 3, "column": 15 } ] } ] }, { "name": "Validate: Known directives/with many unknown directives", "rule": "KnownDirectivesRule", "schema": "7vlHtoKB6yn7Yw66xpvtAb0M4Wjj5LmzeSTymz2zE5w=", "query": "\n {\n __typename @unknown\n human @unknown {\n name\n pets @unknown {\n name\n }\n }\n }\n ", "errors": [ { "message": "Unknown directive \"@unknown\".", "locations": [ { "line": 3, "column": 20 } ] }, { "message": "Unknown directive \"@unknown\".", "locations": [ { "line": 4, "column": 15 } ] }, { "message": "Unknown directive \"@unknown\".", "locations": [ { "line": 6, "column": 16 } ] } ] }, { "name": "Validate: Known directives/with well placed directives", "rule": "KnownDirectivesRule", "schema": "7vlHtoKB6yn7Yw66xpvtAb0M4Wjj5LmzeSTymz2zE5w=", "query": "\n query ($var: Boolean @onVariableDefinition) @onQuery {\n human @onField {\n ...Frag @onFragmentSpread\n ... @onInlineFragment {\n name @onField\n }\n }\n }\n\n mutation @onMutation {\n someField @onField\n }\n\n subscription @onSubscription {\n someField @onField\n }\n\n fragment Frag on Human @onFragmentDefinition {\n name @onField\n }\n ", "errors": [] }, { "name": "Validate: Known directives/with misplaced directives", "rule": "KnownDirectivesRule", "schema": "7vlHtoKB6yn7Yw66xpvtAb0M4Wjj5LmzeSTymz2zE5w=", "query": "\n query ($var: Boolean @onQuery) @onMutation {\n human @onQuery {\n ...Frag @onQuery\n ... @onQuery {\n name @onQuery\n }\n }\n }\n\n mutation @onQuery {\n someField @onQuery\n }\n\n subscription @onQuery {\n someField @onQuery\n }\n\n fragment Frag on Human @onQuery {\n name @onQuery\n }\n ", "errors": [ { "message": "Directive \"@onQuery\" may not be used on VARIABLE_DEFINITION.", "locations": [ { "line": 2, "column": 28 } ] }, { "message": "Directive \"@onMutation\" may not be used on QUERY.", "locations": [ { "line": 2, "column": 38 } ] }, { "message": "Directive \"@onQuery\" may not be used on FIELD.", "locations": [ { "line": 3, "column": 15 } ] }, { "message": "Directive \"@onQuery\" may not be used on FRAGMENT_SPREAD.", "locations": [ { "line": 4, "column": 19 } ] }, { "message": "Directive \"@onQuery\" may not be used on INLINE_FRAGMENT.", "locations": [ { "line": 5, "column": 15 } ] }, { "message": "Directive \"@onQuery\" may not be used on FIELD.", "locations": [ { "line": 6, "column": 18 } ] }, { "message": "Directive \"@onQuery\" may not be used on MUTATION.", "locations": [ { "line": 11, "column": 16 } ] }, { "message": "Directive \"@onQuery\" may not be used on FIELD.", "locations": [ { "column": 19, "line": 12 } ] }, { "message": "Directive \"@onQuery\" may not be used on SUBSCRIPTION.", "locations": [ { "column": 20, "line": 15 } ] }, { "message": "Directive \"@onQuery\" may not be used on FIELD.", "locations": [ { "column": 19, "line": 16 } ] }, { "message": "Directive \"@onQuery\" may not be used on FRAGMENT_DEFINITION.", "locations": [ { "column": 30, "line": 19 } ] }, { "message": "Directive \"@onQuery\" may not be used on FIELD.", "locations": [ { "column": 14, "line": 20 } ] } ] }, { "name": "Validate: Known fragment names/known fragment names are valid", "rule": "KnownFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n human(id: 4) {\n ...HumanFields1\n ... on Human {\n ...HumanFields2\n }\n ... {\n name\n }\n }\n }\n fragment HumanFields1 on Human {\n name\n ...HumanFields3\n }\n fragment HumanFields2 on Human {\n name\n }\n fragment HumanFields3 on Human {\n name\n }\n ", "errors": [] }, { "name": "Validate: Known fragment names/unknown fragment names are invalid", "rule": "KnownFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n human(id: 4) {\n ...UnknownFragment1\n ... on Human {\n ...UnknownFragment2\n }\n }\n }\n fragment HumanFields on Human {\n name\n ...UnknownFragment3\n }\n ", "errors": [ { "message": "Unknown fragment \"UnknownFragment1\".", "locations": [ { "line": 4, "column": 14 } ] }, { "message": "Unknown fragment \"UnknownFragment2\".", "locations": [ { "line": 6, "column": 16 } ] }, { "message": "Unknown fragment \"UnknownFragment3\".", "locations": [ { "line": 12, "column": 12 } ] } ] }, { "name": "Validate: Known type names/known type names are valid", "rule": "KnownTypeNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo(\n $var: String\n $required: [Int!]!\n $introspectionType: __EnumValue\n ) {\n user(id: 4) {\n pets { ... on Pet { name }, ...PetFields, ... { name } }\n }\n }\n\n fragment PetFields on Pet {\n name\n }\n ", "errors": [] }, { "name": "Validate: Known type names/unknown type names are invalid", "rule": "KnownTypeNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($var: [JumbledUpLetters!]!) {\n user(id: 4) {\n name\n pets { ... on Badger { name }, ...PetFields }\n }\n }\n fragment PetFields on Peat {\n name\n }\n ", "errors": [ { "message": "Unknown type \"JumbledUpLetters\".", "locations": [ { "line": 2, "column": 24 } ] }, { "message": "Unknown type \"Badger\".", "locations": [ { "line": 5, "column": 25 } ] }, { "message": "Unknown type \"Peat\".", "locations": [ { "line": 8, "column": 29 } ] } ] }, { "name": "Validate: Known type names/references to standard scalars that are missing in schema", "rule": "KnownTypeNamesRule", "schema": "oECbw4BIP7gX1R+fCGRDCcqbO2FV/Uf0MhhE0EDAWIw=", "query": "\n query ($id: ID, $float: Float, $int: Int) {\n __typename\n }\n ", "errors": [ { "message": "Unknown type \"ID\".", "locations": [ { "line": 2, "column": 19 } ] }, { "message": "Unknown type \"Float\".", "locations": [ { "line": 2, "column": 31 } ] }, { "message": "Unknown type \"Int\".", "locations": [ { "line": 2, "column": 44 } ] } ] }, { "name": "Validate: Anonymous operation must be alone/no operations", "rule": "LoneAnonymousOperationRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Type {\n field\n }\n ", "errors": [] }, { "name": "Validate: Anonymous operation must be alone/one anon operation", "rule": "LoneAnonymousOperationRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field\n }\n ", "errors": [] }, { "name": "Validate: Anonymous operation must be alone/multiple named operations", "rule": "LoneAnonymousOperationRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n field\n }\n\n query Bar {\n field\n }\n ", "errors": [] }, { "name": "Validate: Anonymous operation must be alone/anon operation with fragment", "rule": "LoneAnonymousOperationRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...Foo\n }\n fragment Foo on Type {\n field\n }\n ", "errors": [] }, { "name": "Validate: Anonymous operation must be alone/multiple anon operations", "rule": "LoneAnonymousOperationRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n fieldA\n }\n {\n fieldB\n }\n ", "errors": [ { "message": "This anonymous operation must be the only defined operation.", "locations": [ { "line": 2, "column": 7 } ] }, { "message": "This anonymous operation must be the only defined operation.", "locations": [ { "line": 5, "column": 7 } ] } ] }, { "name": "Validate: Anonymous operation must be alone/anon operation with a mutation", "rule": "LoneAnonymousOperationRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n fieldA\n }\n mutation Foo {\n fieldB\n }\n ", "errors": [ { "message": "This anonymous operation must be the only defined operation.", "locations": [ { "line": 2, "column": 7 } ] } ] }, { "name": "Validate: Anonymous operation must be alone/anon operation with a subscription", "rule": "LoneAnonymousOperationRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n fieldA\n }\n subscription Foo {\n fieldB\n }\n ", "errors": [ { "message": "This anonymous operation must be the only defined operation.", "locations": [ { "line": 2, "column": 7 } ] } ] }, { "name": "Validate: No circular fragment spreads/single reference is valid", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragB }\n fragment fragB on Dog { name }\n ", "errors": [] }, { "name": "Validate: No circular fragment spreads/spreading twice is not circular", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragB, ...fragB }\n fragment fragB on Dog { name }\n ", "errors": [] }, { "name": "Validate: No circular fragment spreads/spreading twice indirectly is not circular", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragB, ...fragC }\n fragment fragB on Dog { ...fragC }\n fragment fragC on Dog { name }\n ", "errors": [] }, { "name": "Validate: No circular fragment spreads/double spread within abstract types", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment nameFragment on Pet {\n ... on Dog { name }\n ... on Cat { name }\n }\n\n fragment spreadsInAnon on Pet {\n ... on Dog { ...nameFragment }\n ... on Cat { ...nameFragment }\n }\n ", "errors": [] }, { "name": "Validate: No circular fragment spreads/does not false positive on unknown fragment", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment nameFragment on Pet {\n ...UnknownFragment\n }\n ", "errors": [] }, { "name": "Validate: No circular fragment spreads/spreading recursively within field fails", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Human { relatives { ...fragA } },\n ", "errors": [ { "message": "Cannot spread fragment \"fragA\" within itself.", "locations": [ { "line": 2, "column": 45 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself directly", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragA }\n ", "errors": [ { "message": "Cannot spread fragment \"fragA\" within itself.", "locations": [ { "line": 2, "column": 31 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself directly within inline fragment", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Pet {\n ... on Dog {\n ...fragA\n }\n }\n ", "errors": [ { "message": "Cannot spread fragment \"fragA\" within itself.", "locations": [ { "line": 4, "column": 11 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself indirectly", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragB }\n fragment fragB on Dog { ...fragA }\n ", "errors": [ { "message": "Cannot spread fragment \"fragA\" within itself via \"fragB\".", "locations": [ { "line": 2, "column": 31 }, { "line": 3, "column": 31 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself indirectly reports opposite order", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragB on Dog { ...fragA }\n fragment fragA on Dog { ...fragB }\n ", "errors": [ { "message": "Cannot spread fragment \"fragB\" within itself via \"fragA\".", "locations": [ { "line": 2, "column": 31 }, { "line": 3, "column": 31 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself indirectly within inline fragment", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Pet {\n ... on Dog {\n ...fragB\n }\n }\n fragment fragB on Pet {\n ... on Dog {\n ...fragA\n }\n }\n ", "errors": [ { "message": "Cannot spread fragment \"fragA\" within itself via \"fragB\".", "locations": [ { "line": 4, "column": 11 }, { "line": 9, "column": 11 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself deeply", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragB }\n fragment fragB on Dog { ...fragC }\n fragment fragC on Dog { ...fragO }\n fragment fragX on Dog { ...fragY }\n fragment fragY on Dog { ...fragZ }\n fragment fragZ on Dog { ...fragO }\n fragment fragO on Dog { ...fragP }\n fragment fragP on Dog { ...fragA, ...fragX }\n ", "errors": [ { "message": "Cannot spread fragment \"fragA\" within itself via \"fragB\", \"fragC\", \"fragO\", \"fragP\".", "locations": [ { "line": 2, "column": 31 }, { "line": 3, "column": 31 }, { "line": 4, "column": 31 }, { "line": 8, "column": 31 }, { "line": 9, "column": 31 } ] }, { "message": "Cannot spread fragment \"fragO\" within itself via \"fragP\", \"fragX\", \"fragY\", \"fragZ\".", "locations": [ { "line": 8, "column": 31 }, { "line": 9, "column": 41 }, { "line": 5, "column": 31 }, { "line": 6, "column": 31 }, { "line": 7, "column": 31 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself deeply two paths", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragB, ...fragC }\n fragment fragB on Dog { ...fragA }\n fragment fragC on Dog { ...fragA }\n ", "errors": [ { "message": "Cannot spread fragment \"fragA\" within itself via \"fragB\".", "locations": [ { "line": 2, "column": 31 }, { "line": 3, "column": 31 } ] }, { "message": "Cannot spread fragment \"fragA\" within itself via \"fragC\".", "locations": [ { "line": 2, "column": 41 }, { "line": 4, "column": 31 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself deeply two paths -- alt traverse order", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragC }\n fragment fragB on Dog { ...fragC }\n fragment fragC on Dog { ...fragA, ...fragB }\n ", "errors": [ { "message": "Cannot spread fragment \"fragA\" within itself via \"fragC\".", "locations": [ { "line": 2, "column": 31 }, { "line": 4, "column": 31 } ] }, { "message": "Cannot spread fragment \"fragC\" within itself via \"fragB\".", "locations": [ { "line": 4, "column": 41 }, { "line": 3, "column": 31 } ] } ] }, { "name": "Validate: No circular fragment spreads/no spreading itself deeply and immediately", "rule": "NoFragmentCyclesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Dog { ...fragB }\n fragment fragB on Dog { ...fragB, ...fragC }\n fragment fragC on Dog { ...fragA, ...fragB }\n ", "errors": [ { "message": "Cannot spread fragment \"fragB\" within itself.", "locations": [ { "line": 3, "column": 31 } ] }, { "message": "Cannot spread fragment \"fragA\" within itself via \"fragB\", \"fragC\".", "locations": [ { "line": 2, "column": 31 }, { "line": 3, "column": 41 }, { "line": 4, "column": 31 } ] }, { "message": "Cannot spread fragment \"fragB\" within itself via \"fragC\".", "locations": [ { "line": 3, "column": 41 }, { "line": 4, "column": 41 } ] } ] }, { "name": "Validate: No undefined variables/all variables defined", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n field(a: $a, b: $b, c: $c)\n }\n ", "errors": [] }, { "name": "Validate: No undefined variables/all variables deeply defined", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n field(a: $a) {\n field(b: $b) {\n field(c: $c)\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: No undefined variables/all variables deeply in inline fragments defined", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n ... on Type {\n field(a: $a) {\n field(b: $b) {\n ... on Type {\n field(c: $c)\n }\n }\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: No undefined variables/all variables in fragments deeply defined", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a) {\n ...FragB\n }\n }\n fragment FragB on Type {\n field(b: $b) {\n ...FragC\n }\n }\n fragment FragC on Type {\n field(c: $c)\n }\n ", "errors": [] }, { "name": "Validate: No undefined variables/variable within single fragment defined in multiple operations", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String) {\n ...FragA\n }\n query Bar($a: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a)\n }\n ", "errors": [] }, { "name": "Validate: No undefined variables/variable within fragments defined in operations", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String) {\n ...FragA\n }\n query Bar($b: String) {\n ...FragB\n }\n fragment FragA on Type {\n field(a: $a)\n }\n fragment FragB on Type {\n field(b: $b)\n }\n ", "errors": [] }, { "name": "Validate: No undefined variables/variable within recursive fragment defined", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a) {\n ...FragA\n }\n }\n ", "errors": [] }, { "name": "Validate: No undefined variables/variable not defined", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n field(a: $a, b: $b, c: $c, d: $d)\n }\n ", "errors": [ { "message": "Variable \"$d\" is not defined by operation \"Foo\".", "locations": [ { "line": 3, "column": 39 }, { "line": 2, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/variable not defined by un-named query", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(a: $a)\n }\n ", "errors": [ { "message": "Variable \"$a\" is not defined.", "locations": [ { "line": 3, "column": 18 }, { "line": 2, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/multiple variables not defined", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($b: String) {\n field(a: $a, b: $b, c: $c)\n }\n ", "errors": [ { "message": "Variable \"$a\" is not defined by operation \"Foo\".", "locations": [ { "line": 3, "column": 18 }, { "line": 2, "column": 7 } ] }, { "message": "Variable \"$c\" is not defined by operation \"Foo\".", "locations": [ { "line": 3, "column": 32 }, { "line": 2, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/variable in fragment not defined by un-named query", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a)\n }\n ", "errors": [ { "message": "Variable \"$a\" is not defined.", "locations": [ { "line": 6, "column": 18 }, { "line": 2, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/variable in fragment not defined by operation", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a) {\n ...FragB\n }\n }\n fragment FragB on Type {\n field(b: $b) {\n ...FragC\n }\n }\n fragment FragC on Type {\n field(c: $c)\n }\n ", "errors": [ { "message": "Variable \"$c\" is not defined by operation \"Foo\".", "locations": [ { "line": 16, "column": 18 }, { "line": 2, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/multiple variables in fragments not defined", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($b: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a) {\n ...FragB\n }\n }\n fragment FragB on Type {\n field(b: $b) {\n ...FragC\n }\n }\n fragment FragC on Type {\n field(c: $c)\n }\n ", "errors": [ { "message": "Variable \"$a\" is not defined by operation \"Foo\".", "locations": [ { "line": 6, "column": 18 }, { "line": 2, "column": 7 } ] }, { "message": "Variable \"$c\" is not defined by operation \"Foo\".", "locations": [ { "line": 16, "column": 18 }, { "line": 2, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/single variable in fragment not defined by multiple operations", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String) {\n ...FragAB\n }\n query Bar($a: String) {\n ...FragAB\n }\n fragment FragAB on Type {\n field(a: $a, b: $b)\n }\n ", "errors": [ { "message": "Variable \"$b\" is not defined by operation \"Foo\".", "locations": [ { "line": 9, "column": 25 }, { "line": 2, "column": 7 } ] }, { "message": "Variable \"$b\" is not defined by operation \"Bar\".", "locations": [ { "line": 9, "column": 25 }, { "line": 5, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/variables in fragment not defined by multiple operations", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($b: String) {\n ...FragAB\n }\n query Bar($a: String) {\n ...FragAB\n }\n fragment FragAB on Type {\n field(a: $a, b: $b)\n }\n ", "errors": [ { "message": "Variable \"$a\" is not defined by operation \"Foo\".", "locations": [ { "line": 9, "column": 18 }, { "line": 2, "column": 7 } ] }, { "message": "Variable \"$b\" is not defined by operation \"Bar\".", "locations": [ { "line": 9, "column": 25 }, { "line": 5, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/variable in fragment used by other operation", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($b: String) {\n ...FragA\n }\n query Bar($a: String) {\n ...FragB\n }\n fragment FragA on Type {\n field(a: $a)\n }\n fragment FragB on Type {\n field(b: $b)\n }\n ", "errors": [ { "message": "Variable \"$a\" is not defined by operation \"Foo\".", "locations": [ { "line": 9, "column": 18 }, { "line": 2, "column": 7 } ] }, { "message": "Variable \"$b\" is not defined by operation \"Bar\".", "locations": [ { "line": 12, "column": 18 }, { "line": 5, "column": 7 } ] } ] }, { "name": "Validate: No undefined variables/multiple undefined variables produce multiple errors", "rule": "NoUndefinedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($b: String) {\n ...FragAB\n }\n query Bar($a: String) {\n ...FragAB\n }\n fragment FragAB on Type {\n field1(a: $a, b: $b)\n ...FragC\n field3(a: $a, b: $b)\n }\n fragment FragC on Type {\n field2(c: $c)\n }\n ", "errors": [ { "message": "Variable \"$a\" is not defined by operation \"Foo\".", "locations": [ { "line": 9, "column": 19 }, { "line": 2, "column": 7 } ] }, { "message": "Variable \"$a\" is not defined by operation \"Foo\".", "locations": [ { "line": 11, "column": 19 }, { "line": 2, "column": 7 } ] }, { "message": "Variable \"$c\" is not defined by operation \"Foo\".", "locations": [ { "line": 14, "column": 19 }, { "line": 2, "column": 7 } ] }, { "message": "Variable \"$b\" is not defined by operation \"Bar\".", "locations": [ { "line": 9, "column": 26 }, { "line": 5, "column": 7 } ] }, { "message": "Variable \"$b\" is not defined by operation \"Bar\".", "locations": [ { "line": 11, "column": 26 }, { "line": 5, "column": 7 } ] }, { "message": "Variable \"$c\" is not defined by operation \"Bar\".", "locations": [ { "line": 14, "column": 19 }, { "line": 5, "column": 7 } ] } ] }, { "name": "Validate: No unused fragments/all fragment names are used", "rule": "NoUnusedFragmentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n human(id: 4) {\n ...HumanFields1\n ... on Human {\n ...HumanFields2\n }\n }\n }\n fragment HumanFields1 on Human {\n name\n ...HumanFields3\n }\n fragment HumanFields2 on Human {\n name\n }\n fragment HumanFields3 on Human {\n name\n }\n ", "errors": [] }, { "name": "Validate: No unused fragments/all fragment names are used by multiple operations", "rule": "NoUnusedFragmentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n human(id: 4) {\n ...HumanFields1\n }\n }\n query Bar {\n human(id: 4) {\n ...HumanFields2\n }\n }\n fragment HumanFields1 on Human {\n name\n ...HumanFields3\n }\n fragment HumanFields2 on Human {\n name\n }\n fragment HumanFields3 on Human {\n name\n }\n ", "errors": [] }, { "name": "Validate: No unused fragments/contains unknown fragments", "rule": "NoUnusedFragmentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n human(id: 4) {\n ...HumanFields1\n }\n }\n query Bar {\n human(id: 4) {\n ...HumanFields2\n }\n }\n fragment HumanFields1 on Human {\n name\n ...HumanFields3\n }\n fragment HumanFields2 on Human {\n name\n }\n fragment HumanFields3 on Human {\n name\n }\n fragment Unused1 on Human {\n name\n }\n fragment Unused2 on Human {\n name\n }\n ", "errors": [ { "message": "Fragment \"Unused1\" is never used.", "locations": [ { "line": 22, "column": 7 } ] }, { "message": "Fragment \"Unused2\" is never used.", "locations": [ { "line": 25, "column": 7 } ] } ] }, { "name": "Validate: No unused fragments/contains unknown fragments with ref cycle", "rule": "NoUnusedFragmentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n human(id: 4) {\n ...HumanFields1\n }\n }\n query Bar {\n human(id: 4) {\n ...HumanFields2\n }\n }\n fragment HumanFields1 on Human {\n name\n ...HumanFields3\n }\n fragment HumanFields2 on Human {\n name\n }\n fragment HumanFields3 on Human {\n name\n }\n fragment Unused1 on Human {\n name\n ...Unused2\n }\n fragment Unused2 on Human {\n name\n ...Unused1\n }\n ", "errors": [ { "message": "Fragment \"Unused1\" is never used.", "locations": [ { "line": 22, "column": 7 } ] }, { "message": "Fragment \"Unused2\" is never used.", "locations": [ { "line": 26, "column": 7 } ] } ] }, { "name": "Validate: No unused fragments/contains unknown and undef fragments", "rule": "NoUnusedFragmentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n human(id: 4) {\n ...bar\n }\n }\n fragment foo on Human {\n name\n }\n ", "errors": [ { "message": "Fragment \"foo\" is never used.", "locations": [ { "line": 7, "column": 7 } ] } ] }, { "name": "Validate: No unused variables/uses all variables", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query ($a: String, $b: String, $c: String) {\n field(a: $a, b: $b, c: $c)\n }\n ", "errors": [] }, { "name": "Validate: No unused variables/uses all variables deeply", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n field(a: $a) {\n field(b: $b) {\n field(c: $c)\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: No unused variables/uses all variables deeply in inline fragments", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n ... on Type {\n field(a: $a) {\n field(b: $b) {\n ... on Type {\n field(c: $c)\n }\n }\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: No unused variables/uses all variables in fragments", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a) {\n ...FragB\n }\n }\n fragment FragB on Type {\n field(b: $b) {\n ...FragC\n }\n }\n fragment FragC on Type {\n field(c: $c)\n }\n ", "errors": [] }, { "name": "Validate: No unused variables/variable used by fragment in multiple operations", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String) {\n ...FragA\n }\n query Bar($b: String) {\n ...FragB\n }\n fragment FragA on Type {\n field(a: $a)\n }\n fragment FragB on Type {\n field(b: $b)\n }\n ", "errors": [] }, { "name": "Validate: No unused variables/variable used by recursive fragment", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a) {\n ...FragA\n }\n }\n ", "errors": [] }, { "name": "Validate: No unused variables/variable not used", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query ($a: String, $b: String, $c: String) {\n field(a: $a, b: $b)\n }\n ", "errors": [ { "message": "Variable \"$c\" is never used.", "locations": [ { "line": 2, "column": 38 } ] } ] }, { "name": "Validate: No unused variables/multiple variables not used", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n field(b: $b)\n }\n ", "errors": [ { "message": "Variable \"$a\" is never used in operation \"Foo\".", "locations": [ { "line": 2, "column": 17 } ] }, { "message": "Variable \"$c\" is never used in operation \"Foo\".", "locations": [ { "line": 2, "column": 41 } ] } ] }, { "name": "Validate: No unused variables/variable not used in fragments", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a) {\n ...FragB\n }\n }\n fragment FragB on Type {\n field(b: $b) {\n ...FragC\n }\n }\n fragment FragC on Type {\n field\n }\n ", "errors": [ { "message": "Variable \"$c\" is never used in operation \"Foo\".", "locations": [ { "line": 2, "column": 41 } ] } ] }, { "name": "Validate: No unused variables/multiple variables not used in fragments", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: String, $c: String) {\n ...FragA\n }\n fragment FragA on Type {\n field {\n ...FragB\n }\n }\n fragment FragB on Type {\n field(b: $b) {\n ...FragC\n }\n }\n fragment FragC on Type {\n field\n }\n ", "errors": [ { "message": "Variable \"$a\" is never used in operation \"Foo\".", "locations": [ { "line": 2, "column": 17 } ] }, { "message": "Variable \"$c\" is never used in operation \"Foo\".", "locations": [ { "line": 2, "column": 41 } ] } ] }, { "name": "Validate: No unused variables/variable not used by unreferenced fragment", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($b: String) {\n ...FragA\n }\n fragment FragA on Type {\n field(a: $a)\n }\n fragment FragB on Type {\n field(b: $b)\n }\n ", "errors": [ { "message": "Variable \"$b\" is never used in operation \"Foo\".", "locations": [ { "line": 2, "column": 17 } ] } ] }, { "name": "Validate: No unused variables/variable not used by fragment used by other operation", "rule": "NoUnusedVariablesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($b: String) {\n ...FragA\n }\n query Bar($a: String) {\n ...FragB\n }\n fragment FragA on Type {\n field(a: $a)\n }\n fragment FragB on Type {\n field(b: $b)\n }\n ", "errors": [ { "message": "Variable \"$b\" is never used in operation \"Foo\".", "locations": [ { "line": 2, "column": 17 } ] }, { "message": "Variable \"$a\" is never used in operation \"Bar\".", "locations": [ { "line": 5, "column": 17 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/unique fields", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment uniqueFields on Dog {\n name\n nickname\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/identical fields", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment mergeIdenticalFields on Dog {\n name\n name\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/identical fields with identical args", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {\n doesKnowCommand(dogCommand: SIT)\n doesKnowCommand(dogCommand: SIT)\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/identical fields with identical directives", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment mergeSameFieldsWithSameDirectives on Dog {\n name @include(if: true)\n name @include(if: true)\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/different args with different aliases", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment differentArgsWithDifferentAliases on Dog {\n knowsSit: doesKnowCommand(dogCommand: SIT)\n knowsDown: doesKnowCommand(dogCommand: DOWN)\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/different directives with different aliases", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment differentDirectivesWithDifferentAliases on Dog {\n nameIfTrue: name @include(if: true)\n nameIfFalse: name @include(if: false)\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/different skip/include directives accepted", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment differentDirectivesWithDifferentAliases on Dog {\n name @include(if: true)\n name @include(if: false)\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/Same stream directives supported", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment differentDirectivesWithDifferentAliases on Dog {\n name @stream(label: \"streamLabel\", initialCount: 1)\n name @stream(label: \"streamLabel\", initialCount: 1)\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/different stream directive label", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n name @stream(label: \"streamLabel\", initialCount: 1)\n name @stream(label: \"anotherLabel\", initialCount: 1)\n }\n ", "errors": [ { "message": "Fields \"name\" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/different stream directive initialCount", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n name @stream(label: \"streamLabel\", initialCount: 1)\n name @stream(label: \"streamLabel\", initialCount: 2)\n }\n ", "errors": [ { "message": "Fields \"name\" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/different stream directive first missing args", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n name @stream\n name @stream(label: \"streamLabel\", initialCount: 1)\n }\n ", "errors": [ { "message": "Fields \"name\" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/different stream directive second missing args", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n name @stream(label: \"streamLabel\", initialCount: 1)\n name @stream\n }\n ", "errors": [ { "message": "Fields \"name\" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/different stream directive extra argument", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n name @stream(label: \"streamLabel\", initialCount: 1)\n name @stream(label: \"streamLabel\", initialCount: 1, extraArg: true)\n }\n ", "errors": [ { "message": "Fields \"name\" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/mix of stream and no stream", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n name @stream\n name\n }\n ", "errors": [ { "message": "Fields \"name\" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/different stream directive both missing args", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n name @stream\n name @stream\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/Same aliases with different field targets", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment sameAliasesWithDifferentFieldTargets on Dog {\n fido: name\n fido: nickname\n }\n ", "errors": [ { "message": "Fields \"fido\" conflict because \"name\" and \"nickname\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/Same aliases allowed on non-overlapping fields", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment sameAliasesWithDifferentFieldTargets on Pet {\n ... on Dog {\n name\n }\n ... on Cat {\n name: nickname\n }\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/Alias masking direct field access", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment aliasMaskingDirectFieldAccess on Dog {\n name: nickname\n name\n }\n ", "errors": [ { "message": "Fields \"name\" conflict because \"nickname\" and \"name\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/different args, second adds an argument", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n doesKnowCommand\n doesKnowCommand(dogCommand: HEEL)\n }\n ", "errors": [ { "message": "Fields \"doesKnowCommand\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/different args, second missing an argument", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n doesKnowCommand(dogCommand: SIT)\n doesKnowCommand\n }\n ", "errors": [ { "message": "Fields \"doesKnowCommand\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/conflicting arg values", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n doesKnowCommand(dogCommand: SIT)\n doesKnowCommand(dogCommand: HEEL)\n }\n ", "errors": [ { "message": "Fields \"doesKnowCommand\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/conflicting arg names", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Dog {\n isAtLocation(x: 0)\n isAtLocation(y: 0)\n }\n ", "errors": [ { "message": "Fields \"isAtLocation\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/allows different args where no conflict is possible", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment conflictingArgs on Pet {\n ... on Dog {\n name(surname: true)\n }\n ... on Cat {\n name\n }\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/allows different order of args", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "6+inK6Z824FQA4uBaUhxFXmMhc79+vKbamUi32/NtdQ=", "query": "\n {\n someField(a: null, b: null)\n someField(b: null, a: null)\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/allows different order of input object fields in arg values", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "FPFhio7mA1zxXhXd/8NCpbdwBrysWfBkn36GZ1uvDAA=", "query": "\n {\n someField(arg: { a: null, b: null })\n someField(arg: { b: null, a: null })\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/encounters conflict in fragments", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...A\n ...B\n }\n fragment A on Type {\n x: a\n }\n fragment B on Type {\n x: b\n }\n ", "errors": [ { "message": "Fields \"x\" conflict because \"a\" and \"b\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 7, "column": 9 }, { "line": 10, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/reports each conflict once", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n f1 {\n ...A\n ...B\n }\n f2 {\n ...B\n ...A\n }\n f3 {\n ...A\n ...B\n x: c\n }\n }\n fragment A on Type {\n x: a\n }\n fragment B on Type {\n x: b\n }\n ", "errors": [ { "message": "Fields \"x\" conflict because \"a\" and \"b\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 18, "column": 9 }, { "line": 21, "column": 9 } ] }, { "message": "Fields \"x\" conflict because \"c\" and \"a\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 14, "column": 11 }, { "line": 18, "column": 9 } ] }, { "message": "Fields \"x\" conflict because \"c\" and \"b\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 14, "column": 11 }, { "line": 21, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/deep conflict", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field {\n x: a\n },\n field {\n x: b\n }\n }\n ", "errors": [ { "message": "Fields \"field\" conflict because subfields \"x\" conflict because \"a\" and \"b\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 11 }, { "line": 6, "column": 9 }, { "line": 7, "column": 11 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/deep conflict with multiple issues", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field {\n x: a\n y: c\n },\n field {\n x: b\n y: d\n }\n }\n ", "errors": [ { "message": "Fields \"field\" conflict because subfields \"x\" conflict because \"a\" and \"b\" are different fields and subfields \"y\" conflict because \"c\" and \"d\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 11 }, { "line": 5, "column": 11 }, { "line": 7, "column": 9 }, { "line": 8, "column": 11 }, { "line": 9, "column": 11 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/very deep conflict", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field {\n deepField {\n x: a\n }\n },\n field {\n deepField {\n x: b\n }\n }\n }\n ", "errors": [ { "message": "Fields \"field\" conflict because subfields \"deepField\" conflict because subfields \"x\" conflict because \"a\" and \"b\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 4, "column": 11 }, { "line": 5, "column": 13 }, { "line": 8, "column": 9 }, { "line": 9, "column": 11 }, { "line": 10, "column": 13 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/reports deep conflict to nearest common ancestor", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field {\n deepField {\n x: a\n }\n deepField {\n x: b\n }\n },\n field {\n deepField {\n y\n }\n }\n }\n ", "errors": [ { "message": "Fields \"deepField\" conflict because subfields \"x\" conflict because \"a\" and \"b\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 4, "column": 11 }, { "line": 5, "column": 13 }, { "line": 7, "column": 11 }, { "line": 8, "column": 13 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/reports deep conflict to nearest common ancestor in fragments", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field {\n ...F\n }\n field {\n ...F\n }\n }\n fragment F on T {\n deepField {\n deeperField {\n x: a\n }\n deeperField {\n x: b\n }\n },\n deepField {\n deeperField {\n y\n }\n }\n }\n ", "errors": [ { "message": "Fields \"deeperField\" conflict because subfields \"x\" conflict because \"a\" and \"b\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 12, "column": 11 }, { "line": 13, "column": 13 }, { "line": 15, "column": 11 }, { "line": 16, "column": 13 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/reports deep conflict in nested fragments", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field {\n ...F\n }\n field {\n ...I\n }\n }\n fragment F on T {\n x: a\n ...G\n }\n fragment G on T {\n y: c\n }\n fragment I on T {\n y: d\n ...J\n }\n fragment J on T {\n x: b\n }\n ", "errors": [ { "message": "Fields \"field\" conflict because subfields \"x\" conflict because \"a\" and \"b\" are different fields and subfields \"y\" conflict because \"c\" and \"d\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 11, "column": 9 }, { "line": 15, "column": 9 }, { "line": 6, "column": 9 }, { "line": 22, "column": 9 }, { "line": 18, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/ignores unknown fragments", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field\n ...Unknown\n ...Known\n }\n\n fragment Known on T {\n field\n ...OtherUnknown\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/conflicting return types which potentially overlap", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ...on IntBox {\n scalar\n }\n ...on NonNullStringBox1 {\n scalar\n }\n }\n }\n ", "errors": [ { "message": "Fields \"scalar\" conflict because they return conflicting types \"Int\" and \"String!\". Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 5, "column": 17 }, { "line": 8, "column": 17 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/compatible return shapes on different return types", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ... on SomeBox {\n deepBox {\n unrelatedField\n }\n }\n ... on StringBox {\n deepBox {\n unrelatedField\n }\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/disallows differing return types despite no overlap", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ... on IntBox {\n scalar\n }\n ... on StringBox {\n scalar\n }\n }\n }\n ", "errors": [ { "message": "Fields \"scalar\" conflict because they return conflicting types \"Int\" and \"String\". Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 5, "column": 17 }, { "line": 8, "column": 17 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/disallows differing return type nullability despite no overlap", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ... on NonNullStringBox1 {\n scalar\n }\n ... on StringBox {\n scalar\n }\n }\n }\n ", "errors": [ { "message": "Fields \"scalar\" conflict because they return conflicting types \"String!\" and \"String\". Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 5, "column": 17 }, { "line": 8, "column": 17 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/disallows differing return type list despite no overlap", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ... on IntBox {\n box: listStringBox {\n scalar\n }\n }\n ... on StringBox {\n box: stringBox {\n scalar\n }\n }\n }\n }\n ", "errors": [ { "message": "Fields \"box\" conflict because they return conflicting types \"[StringBox]\" and \"StringBox\". Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 5, "column": 17 }, { "line": 10, "column": 17 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/disallows differing return type list despite no overlap", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ... on IntBox {\n box: stringBox {\n scalar\n }\n }\n ... on StringBox {\n box: listStringBox {\n scalar\n }\n }\n }\n }\n ", "errors": [ { "message": "Fields \"box\" conflict because they return conflicting types \"StringBox\" and \"[StringBox]\". Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 5, "column": 17 }, { "line": 10, "column": 17 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/disallows differing deep return types despite no overlap", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ... on IntBox {\n box: stringBox {\n scalar\n }\n }\n ... on StringBox {\n box: intBox {\n scalar\n }\n }\n }\n }\n ", "errors": [ { "message": "Fields \"box\" conflict because subfields \"scalar\" conflict because they return conflicting types \"String\" and \"Int\". Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 5, "column": 17 }, { "line": 6, "column": 19 }, { "line": 10, "column": 17 }, { "line": 11, "column": 19 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/allows non-conflicting overlapping types", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ... on IntBox {\n scalar: unrelatedField\n }\n ... on StringBox {\n scalar\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/same wrapped scalar return types", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ...on NonNullStringBox1 {\n scalar\n }\n ...on NonNullStringBox2 {\n scalar\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/allows inline fragments without type condition", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n a\n ... {\n a\n }\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/compares deep types including list", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n connection {\n ...edgeID\n edges {\n node {\n id: name\n }\n }\n }\n }\n\n fragment edgeID on Connection {\n edges {\n node {\n id\n }\n }\n }\n ", "errors": [ { "message": "Fields \"edges\" conflict because subfields \"node\" conflict because subfields \"id\" conflict because \"name\" and \"id\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 5, "column": 15 }, { "line": 6, "column": 17 }, { "line": 7, "column": 19 }, { "line": 14, "column": 13 }, { "line": 15, "column": 15 }, { "line": 16, "column": 17 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/ignores unknown types", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "lI0vTN4faZpDsH78EAqnHxgWvSXnGiiQrLUtNc+zVJw=", "query": "\n {\n someBox {\n ...on UnknownType {\n scalar\n }\n ...on NonNullStringBox2 {\n scalar\n }\n }\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/return types must be unambiguous/works for field names that are JS keywords", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "kxqeANW6SwgMzv7OfoER7Pehb0UyllcRPcBPGckVxRc=", "query": "\n {\n foo {\n constructor\n }\n }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/does not infinite loop on recursive fragment", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...fragA\n }\n\n fragment fragA on Human { name, relatives { name, ...fragA } }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/does not infinite loop on immediately recursive fragment", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...fragA\n }\n\n fragment fragA on Human { name, ...fragA }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/does not infinite loop on recursive fragment with a field named after fragment", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...fragA\n fragA\n }\n\n fragment fragA on Query { ...fragA }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/finds invalid cases even with field named after fragment", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n fragA\n ...fragA\n }\n\n fragment fragA on Type {\n fragA: b\n }\n ", "errors": [ { "message": "Fields \"fragA\" conflict because \"fragA\" and \"b\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 3, "column": 9 }, { "line": 8, "column": 9 } ] } ] }, { "name": "Validate: Overlapping fields can be merged/does not infinite loop on transitively recursive fragment", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...fragA\n fragB\n }\n\n fragment fragA on Human { name, ...fragB }\n fragment fragB on Human { name, ...fragC }\n fragment fragC on Human { name, ...fragA }\n ", "errors": [] }, { "name": "Validate: Overlapping fields can be merged/finds invalid case even with immediately recursive fragment", "rule": "OverlappingFieldsCanBeMergedRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment sameAliasesWithDifferentFieldTargets on Dog {\n ...sameAliasesWithDifferentFieldTargets\n fido: name\n fido: nickname\n }\n ", "errors": [ { "message": "Fields \"fido\" conflict because \"name\" and \"nickname\" are different fields. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 4, "column": 9 }, { "line": 5, "column": 9 } ] } ] }, { "name": "Validate: Possible fragment spreads/of the same object", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment objectWithinObject on Dog { ...dogFragment }\n fragment dogFragment on Dog { barkVolume }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/of the same object with inline fragment", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/object into an implemented interface", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment objectWithinInterface on Pet { ...dogFragment }\n fragment dogFragment on Dog { barkVolume }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/object into containing union", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment objectWithinUnion on CatOrDog { ...dogFragment }\n fragment dogFragment on Dog { barkVolume }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/union into contained object", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment unionWithinObject on Dog { ...catOrDogFragment }\n fragment catOrDogFragment on CatOrDog { __typename }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/union into overlapping interface", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment unionWithinInterface on Pet { ...catOrDogFragment }\n fragment catOrDogFragment on CatOrDog { __typename }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/union into overlapping union", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment }\n fragment catOrDogFragment on CatOrDog { __typename }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/interface into implemented object", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment interfaceWithinObject on Dog { ...petFragment }\n fragment petFragment on Pet { name }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/interface into overlapping interface", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment interfaceWithinInterface on Pet { ...beingFragment }\n fragment beingFragment on Being { name }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/interface into overlapping interface in inline fragment", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment interfaceWithinInterface on Pet { ... on Being { name } }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/interface into overlapping union", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment interfaceWithinUnion on CatOrDog { ...petFragment }\n fragment petFragment on Pet { name }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/ignores incorrect type (caught by FragmentsOnCompositeTypesRule)", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment petFragment on Pet { ...badInADifferentWay }\n fragment badInADifferentWay on String { name }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/ignores unknown fragments (caught by KnownFragmentNamesRule)", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment petFragment on Pet { ...UnknownFragment }\n ", "errors": [] }, { "name": "Validate: Possible fragment spreads/different object into object", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidObjectWithinObject on Cat { ...dogFragment }\n fragment dogFragment on Dog { barkVolume }\n ", "errors": [ { "message": "Fragment \"dogFragment\" cannot be spread here as objects of type \"Cat\" can never be of type \"Dog\".", "locations": [ { "line": 2, "column": 51 } ] } ] }, { "name": "Validate: Possible fragment spreads/different object into object in inline fragment", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidObjectWithinObjectAnon on Cat {\n ... on Dog { barkVolume }\n }\n ", "errors": [ { "message": "Fragment cannot be spread here as objects of type \"Cat\" can never be of type \"Dog\".", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Possible fragment spreads/object into not implementing interface", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidObjectWithinInterface on Pet { ...humanFragment }\n fragment humanFragment on Human { pets { name } }\n ", "errors": [ { "message": "Fragment \"humanFragment\" cannot be spread here as objects of type \"Pet\" can never be of type \"Human\".", "locations": [ { "line": 2, "column": 54 } ] } ] }, { "name": "Validate: Possible fragment spreads/object into not containing union", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment }\n fragment humanFragment on Human { pets { name } }\n ", "errors": [ { "message": "Fragment \"humanFragment\" cannot be spread here as objects of type \"CatOrDog\" can never be of type \"Human\".", "locations": [ { "line": 2, "column": 55 } ] } ] }, { "name": "Validate: Possible fragment spreads/union into not contained object", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidUnionWithinObject on Human { ...catOrDogFragment }\n fragment catOrDogFragment on CatOrDog { __typename }\n ", "errors": [ { "message": "Fragment \"catOrDogFragment\" cannot be spread here as objects of type \"Human\" can never be of type \"CatOrDog\".", "locations": [ { "line": 2, "column": 52 } ] } ] }, { "name": "Validate: Possible fragment spreads/union into non overlapping interface", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment }\n fragment humanOrAlienFragment on HumanOrAlien { __typename }\n ", "errors": [ { "message": "Fragment \"humanOrAlienFragment\" cannot be spread here as objects of type \"Pet\" can never be of type \"HumanOrAlien\".", "locations": [ { "line": 2, "column": 53 } ] } ] }, { "name": "Validate: Possible fragment spreads/union into non overlapping union", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment }\n fragment humanOrAlienFragment on HumanOrAlien { __typename }\n ", "errors": [ { "message": "Fragment \"humanOrAlienFragment\" cannot be spread here as objects of type \"CatOrDog\" can never be of type \"HumanOrAlien\".", "locations": [ { "line": 2, "column": 54 } ] } ] }, { "name": "Validate: Possible fragment spreads/interface into non implementing object", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment }\n fragment intelligentFragment on Intelligent { iq }\n ", "errors": [ { "message": "Fragment \"intelligentFragment\" cannot be spread here as objects of type \"Cat\" can never be of type \"Intelligent\".", "locations": [ { "line": 2, "column": 54 } ] } ] }, { "name": "Validate: Possible fragment spreads/interface into non overlapping interface", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidInterfaceWithinInterface on Pet {\n ...intelligentFragment\n }\n fragment intelligentFragment on Intelligent { iq }\n ", "errors": [ { "message": "Fragment \"intelligentFragment\" cannot be spread here as objects of type \"Pet\" can never be of type \"Intelligent\".", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Possible fragment spreads/interface into non overlapping interface in inline fragment", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidInterfaceWithinInterfaceAnon on Pet {\n ...on Intelligent { iq }\n }\n ", "errors": [ { "message": "Fragment cannot be spread here as objects of type \"Pet\" can never be of type \"Intelligent\".", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Possible fragment spreads/interface into non overlapping union", "rule": "PossibleFragmentSpreadsRule", "schema": "j60+7rXUIN3Vm5LJHNUyKRATi1ksX6cSMmX+z0nvQMs=", "query": "\n fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment }\n fragment petFragment on Pet { name }\n ", "errors": [ { "message": "Fragment \"petFragment\" cannot be spread here as objects of type \"HumanOrAlien\" can never be of type \"Pet\".", "locations": [ { "line": 2, "column": 62 } ] } ] }, { "name": "Validate: Provided required arguments/ignores unknown arguments", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog {\n isHouseTrained(unknownArgument: true)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/Arg on optional arg", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog {\n isHouseTrained(atOtherHomes: true)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/No Arg on optional arg", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog {\n isHouseTrained\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/No arg on non-null field with default", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n nonNullFieldWithDefault\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/Multiple args", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleReqs(req1: 1, req2: 2)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/Multiple args reverse order", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleReqs(req2: 2, req1: 1)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/No args on multiple optional", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleOpts\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/One arg on multiple optional", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleOpts(opt1: 1)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/Second arg on multiple optional", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleOpts(opt2: 1)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/Multiple required args on mixedList", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleOptAndReq(req1: 3, req2: 4)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/Multiple required and one optional arg on mixedList", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleOptAndReq(req1: 3, req2: 4, opt1: 5)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Valid non-nullable value/All required and optional args on mixedList", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Invalid non-nullable value/Missing one non-nullable argument", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleReqs(req2: 2)\n }\n }\n ", "errors": [ { "message": "Field \"multipleReqs\" argument \"req1\" of type \"Int!\" is required, but it was not provided.", "locations": [ { "line": 4, "column": 13 } ] } ] }, { "name": "Validate: Provided required arguments/Invalid non-nullable value/Missing multiple non-nullable arguments", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleReqs\n }\n }\n ", "errors": [ { "message": "Field \"multipleReqs\" argument \"req1\" of type \"Int!\" is required, but it was not provided.", "locations": [ { "line": 4, "column": 13 } ] }, { "message": "Field \"multipleReqs\" argument \"req2\" of type \"Int!\" is required, but it was not provided.", "locations": [ { "line": 4, "column": 13 } ] } ] }, { "name": "Validate: Provided required arguments/Invalid non-nullable value/Incorrect value and missing argument", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n complicatedArgs {\n multipleReqs(req1: \"one\")\n }\n }\n ", "errors": [ { "message": "Field \"multipleReqs\" argument \"req2\" of type \"Int!\" is required, but it was not provided.", "locations": [ { "line": 4, "column": 13 } ] } ] }, { "name": "Validate: Provided required arguments/Directive arguments/ignores unknown directives", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog @unknown\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Directive arguments/with directives of valid types", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog @include(if: true) {\n name\n }\n human @skip(if: false) {\n name\n }\n }\n ", "errors": [] }, { "name": "Validate: Provided required arguments/Directive arguments/with directive with missing types", "rule": "ProvidedRequiredArgumentsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n dog @include {\n name @skip\n }\n }\n ", "errors": [ { "message": "Directive \"@include\" argument \"if\" of type \"Boolean!\" is required, but it was not provided.", "locations": [ { "line": 3, "column": 15 } ] }, { "message": "Directive \"@skip\" argument \"if\" of type \"Boolean!\" is required, but it was not provided.", "locations": [ { "line": 4, "column": 18 } ] } ] }, { "name": "Validate: Scalar leafs/valid scalar selection", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarSelection on Dog {\n barks\n }\n ", "errors": [] }, { "name": "Validate: Scalar leafs/object type missing selection", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query directQueryOnObjectWithoutSubFields {\n human\n }\n ", "errors": [ { "message": "Field \"human\" of type \"Human\" must have a selection of subfields. Did you mean \"human { ... }\"?", "locations": [ { "line": 3, "column": 9 } ] } ] }, { "name": "Validate: Scalar leafs/interface type missing selection", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n human { pets }\n }\n ", "errors": [ { "message": "Field \"pets\" of type \"[Pet]\" must have a selection of subfields. Did you mean \"pets { ... }\"?", "locations": [ { "line": 3, "column": 17 } ] } ] }, { "name": "Validate: Scalar leafs/valid scalar selection with args", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarSelectionWithArgs on Dog {\n doesKnowCommand(dogCommand: SIT)\n }\n ", "errors": [] }, { "name": "Validate: Scalar leafs/scalar selection not allowed on Boolean", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarSelectionsNotAllowedOnBoolean on Dog {\n barks { sinceWhen }\n }\n ", "errors": [ { "message": "Field \"barks\" must not have a selection since type \"Boolean\" has no subfields.", "locations": [ { "line": 3, "column": 15 } ] } ] }, { "name": "Validate: Scalar leafs/scalar selection not allowed on Enum", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarSelectionsNotAllowedOnEnum on Cat {\n furColor { inHexDec }\n }\n ", "errors": [ { "message": "Field \"furColor\" must not have a selection since type \"FurColor\" has no subfields.", "locations": [ { "line": 3, "column": 18 } ] } ] }, { "name": "Validate: Scalar leafs/scalar selection not allowed with args", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarSelectionsNotAllowedWithArgs on Dog {\n doesKnowCommand(dogCommand: SIT) { sinceWhen }\n }\n ", "errors": [ { "message": "Field \"doesKnowCommand\" must not have a selection since type \"Boolean\" has no subfields.", "locations": [ { "line": 3, "column": 42 } ] } ] }, { "name": "Validate: Scalar leafs/Scalar selection not allowed with directives", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarSelectionsNotAllowedWithDirectives on Dog {\n name @include(if: true) { isAlsoHumanName }\n }\n ", "errors": [ { "message": "Field \"name\" must not have a selection since type \"String\" has no subfields.", "locations": [ { "line": 3, "column": 33 } ] } ] }, { "name": "Validate: Scalar leafs/Scalar selection not allowed with directives and args", "rule": "ScalarLeafsRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog {\n doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen }\n }\n ", "errors": [ { "message": "Field \"doesKnowCommand\" must not have a selection since type \"Boolean\" has no subfields.", "locations": [ { "line": 3, "column": 61 } ] } ] }, { "name": "Validate: Unique argument names/no arguments on field", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/no arguments on directive", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field @directive\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/argument on field", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg: \"value\")\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/argument on directive", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field @directive(arg: \"value\")\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/same argument on two fields", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n one: field(arg: \"value\")\n two: field(arg: \"value\")\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/same argument on field and directive", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg: \"value\") @directive(arg: \"value\")\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/same argument on two directives", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field @directive1(arg: \"value\") @directive2(arg: \"value\")\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/multiple field arguments", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg1: \"value\", arg2: \"value\", arg3: \"value\")\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/multiple directive arguments", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field @directive(arg1: \"value\", arg2: \"value\", arg3: \"value\")\n }\n ", "errors": [] }, { "name": "Validate: Unique argument names/duplicate field arguments", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg1: \"value\", arg1: \"value\")\n }\n ", "errors": [ { "message": "There can be only one argument named \"arg1\".", "locations": [ { "line": 3, "column": 15 }, { "line": 3, "column": 30 } ] } ] }, { "name": "Validate: Unique argument names/many duplicate field arguments", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg1: \"value\", arg1: \"value\", arg1: \"value\")\n }\n ", "errors": [ { "message": "There can be only one argument named \"arg1\".", "locations": [ { "line": 3, "column": 15 }, { "line": 3, "column": 30 }, { "line": 3, "column": 45 } ] } ] }, { "name": "Validate: Unique argument names/duplicate directive arguments", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field @directive(arg1: \"value\", arg1: \"value\")\n }\n ", "errors": [ { "message": "There can be only one argument named \"arg1\".", "locations": [ { "line": 3, "column": 26 }, { "line": 3, "column": 41 } ] } ] }, { "name": "Validate: Unique argument names/many duplicate directive arguments", "rule": "UniqueArgumentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field @directive(arg1: \"value\", arg1: \"value\", arg1: \"value\")\n }\n ", "errors": [ { "message": "There can be only one argument named \"arg1\".", "locations": [ { "line": 3, "column": 26 }, { "line": 3, "column": 41 }, { "line": 3, "column": 56 } ] } ] }, { "name": "Validate: Directives Are Unique Per Location/no directives", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type {\n field\n }\n ", "errors": [] }, { "name": "Validate: Directives Are Unique Per Location/unique directives in different locations", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type @directiveA {\n field @directiveB\n }\n ", "errors": [] }, { "name": "Validate: Directives Are Unique Per Location/unique directives in same locations", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type @directiveA @directiveB {\n field @directiveA @directiveB\n }\n ", "errors": [] }, { "name": "Validate: Directives Are Unique Per Location/same directives in different locations", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type @directiveA {\n field @directiveA\n }\n ", "errors": [] }, { "name": "Validate: Directives Are Unique Per Location/same directives in similar locations", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type {\n field @directive\n field @directive\n }\n ", "errors": [] }, { "name": "Validate: Directives Are Unique Per Location/repeatable directives in same location", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type @repeatable @repeatable {\n field @repeatable @repeatable\n }\n ", "errors": [] }, { "name": "Validate: Directives Are Unique Per Location/unknown directives must be ignored", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n type Test @unknown @unknown {\n field: String! @unknown @unknown\n }\n\n extend type Test @unknown {\n anotherField: String!\n }\n ", "errors": [] }, { "name": "Validate: Directives Are Unique Per Location/duplicate directives in one location", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type {\n field @directive @directive\n }\n ", "errors": [ { "message": "The directive \"@directive\" can only be used once at this location.", "locations": [ { "line": 3, "column": 15 }, { "line": 3, "column": 26 } ] } ] }, { "name": "Validate: Directives Are Unique Per Location/many duplicate directives in one location", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type {\n field @directive @directive @directive\n }\n ", "errors": [ { "message": "The directive \"@directive\" can only be used once at this location.", "locations": [ { "line": 3, "column": 15 }, { "line": 3, "column": 26 } ] }, { "message": "The directive \"@directive\" can only be used once at this location.", "locations": [ { "line": 3, "column": 15 }, { "line": 3, "column": 37 } ] } ] }, { "name": "Validate: Directives Are Unique Per Location/different duplicate directives in one location", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type {\n field @directiveA @directiveB @directiveA @directiveB\n }\n ", "errors": [ { "message": "The directive \"@directiveA\" can only be used once at this location.", "locations": [ { "line": 3, "column": 15 }, { "line": 3, "column": 39 } ] }, { "message": "The directive \"@directiveB\" can only be used once at this location.", "locations": [ { "line": 3, "column": 27 }, { "line": 3, "column": 51 } ] } ] }, { "name": "Validate: Directives Are Unique Per Location/duplicate directives in many locations", "rule": "UniqueDirectivesPerLocationRule", "schema": "JcQm34rC4nzCQ4Cn4QDjItvRRlqLNNgWJlR58cdBrQc=", "query": "\n fragment Test on Type @directive @directive {\n field @directive @directive\n }\n ", "errors": [ { "message": "The directive \"@directive\" can only be used once at this location.", "locations": [ { "line": 2, "column": 29 }, { "line": 2, "column": 40 } ] }, { "message": "The directive \"@directive\" can only be used once at this location.", "locations": [ { "line": 3, "column": 15 }, { "line": 3, "column": 26 } ] } ] }, { "name": "Validate: Unique fragment names/no fragments", "rule": "UniqueFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique fragment names/one fragment", "rule": "UniqueFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...fragA\n }\n\n fragment fragA on Type {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique fragment names/many fragments", "rule": "UniqueFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...fragA\n ...fragB\n ...fragC\n }\n fragment fragA on Type {\n fieldA\n }\n fragment fragB on Type {\n fieldB\n }\n fragment fragC on Type {\n fieldC\n }\n ", "errors": [] }, { "name": "Validate: Unique fragment names/inline fragments are always unique", "rule": "UniqueFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...on Type {\n fieldA\n }\n ...on Type {\n fieldB\n }\n }\n ", "errors": [] }, { "name": "Validate: Unique fragment names/fragment and operation named the same", "rule": "UniqueFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n ...Foo\n }\n fragment Foo on Type {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique fragment names/fragments named the same", "rule": "UniqueFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n ...fragA\n }\n fragment fragA on Type {\n fieldA\n }\n fragment fragA on Type {\n fieldB\n }\n ", "errors": [ { "message": "There can be only one fragment named \"fragA\".", "locations": [ { "line": 5, "column": 16 }, { "line": 8, "column": 16 } ] } ] }, { "name": "Validate: Unique fragment names/fragments named the same without being referenced", "rule": "UniqueFragmentNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Type {\n fieldA\n }\n fragment fragA on Type {\n fieldB\n }\n ", "errors": [ { "message": "There can be only one fragment named \"fragA\".", "locations": [ { "line": 2, "column": 16 }, { "line": 5, "column": 16 } ] } ] }, { "name": "Validate: Unique input field names/input object with fields", "rule": "UniqueInputFieldNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg: { f: true })\n }\n ", "errors": [] }, { "name": "Validate: Unique input field names/same input object within two args", "rule": "UniqueInputFieldNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg1: { f: true }, arg2: { f: true })\n }\n ", "errors": [] }, { "name": "Validate: Unique input field names/multiple input object fields", "rule": "UniqueInputFieldNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg: { f1: \"value\", f2: \"value\", f3: \"value\" })\n }\n ", "errors": [] }, { "name": "Validate: Unique input field names/allows for nested input objects with similar fields", "rule": "UniqueInputFieldNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg: {\n deep: {\n deep: {\n id: 1\n }\n id: 1\n }\n id: 1\n })\n }\n ", "errors": [] }, { "name": "Validate: Unique input field names/duplicate input object fields", "rule": "UniqueInputFieldNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg: { f1: \"value\", f1: \"value\" })\n }\n ", "errors": [ { "message": "There can be only one input field named \"f1\".", "locations": [ { "line": 3, "column": 22 }, { "line": 3, "column": 35 } ] } ] }, { "name": "Validate: Unique input field names/many duplicate input object fields", "rule": "UniqueInputFieldNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg: { f1: \"value\", f1: \"value\", f1: \"value\" })\n }\n ", "errors": [ { "message": "There can be only one input field named \"f1\".", "locations": [ { "line": 3, "column": 22 }, { "line": 3, "column": 35 } ] }, { "message": "There can be only one input field named \"f1\".", "locations": [ { "line": 3, "column": 22 }, { "line": 3, "column": 48 } ] } ] }, { "name": "Validate: Unique input field names/nested duplicate input object fields", "rule": "UniqueInputFieldNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field(arg: { f1: {f2: \"value\", f2: \"value\" }})\n }\n ", "errors": [ { "message": "There can be only one input field named \"f2\".", "locations": [ { "line": 3, "column": 27 }, { "line": 3, "column": 40 } ] } ] }, { "name": "Validate: Unique operation names/no operations", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment fragA on Type {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique operation names/one anon operation", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique operation names/one named operation", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique operation names/multiple operations", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n field\n }\n\n query Bar {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique operation names/multiple operations of different types", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n field\n }\n\n mutation Bar {\n field\n }\n\n subscription Baz {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique operation names/fragment and operation named the same", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n ...Foo\n }\n fragment Foo on Type {\n field\n }\n ", "errors": [] }, { "name": "Validate: Unique operation names/multiple operations of same name", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n fieldA\n }\n query Foo {\n fieldB\n }\n ", "errors": [ { "message": "There can be only one operation named \"Foo\".", "locations": [ { "line": 2, "column": 13 }, { "line": 5, "column": 13 } ] } ] }, { "name": "Validate: Unique operation names/multiple ops of same name of different types (mutation)", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n fieldA\n }\n mutation Foo {\n fieldB\n }\n ", "errors": [ { "message": "There can be only one operation named \"Foo\".", "locations": [ { "line": 2, "column": 13 }, { "line": 5, "column": 16 } ] } ] }, { "name": "Validate: Unique operation names/multiple ops of same name of different types (subscription)", "rule": "UniqueOperationNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo {\n fieldA\n }\n subscription Foo {\n fieldB\n }\n ", "errors": [ { "message": "There can be only one operation named \"Foo\".", "locations": [ { "line": 2, "column": 13 }, { "line": 5, "column": 20 } ] } ] }, { "name": "Validate: Unique variable names/unique variable names", "rule": "UniqueVariableNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query A($x: Int, $y: String) { __typename }\n query B($x: String, $y: Int) { __typename }\n ", "errors": [] }, { "name": "Validate: Unique variable names/duplicate variable names", "rule": "UniqueVariableNamesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query A($x: Int, $x: Int, $x: String) { __typename }\n query B($x: String, $x: Int) { __typename }\n query C($x: Int, $x: Int) { __typename }\n ", "errors": [ { "message": "There can be only one variable named \"$x\".", "locations": [ { "line": 2, "column": 16 }, { "line": 2, "column": 25 }, { "line": 2, "column": 34 } ] }, { "message": "There can be only one variable named \"$x\".", "locations": [ { "line": 3, "column": 16 }, { "line": 3, "column": 28 } ] }, { "message": "There can be only one variable named \"$x\".", "locations": [ { "line": 4, "column": 16 }, { "line": 4, "column": 25 } ] } ] }, { "name": "Validate: Variables are input types/unknown types are ignored", "rule": "VariablesAreInputTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: Unknown, $b: [[Unknown!]]!) {\n field(a: $a, b: $b)\n }\n ", "errors": [] }, { "name": "Validate: Variables are input types/input types are valid", "rule": "VariablesAreInputTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) {\n field(a: $a, b: $b, c: $c)\n }\n ", "errors": [] }, { "name": "Validate: Variables are input types/output types are invalid", "rule": "VariablesAreInputTypesRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) {\n field(a: $a, b: $b, c: $c)\n }\n ", "errors": [ { "locations": [ { "line": 2, "column": 21 } ], "message": "Variable \"$a\" cannot be non-input type \"Dog\"." }, { "locations": [ { "line": 2, "column": 30 } ], "message": "Variable \"$b\" cannot be non-input type \"[[CatOrDog!]]!\"." }, { "locations": [ { "line": 2, "column": 50 } ], "message": "Variable \"$c\" cannot be non-input type \"Pet\"." } ] }, { "name": "Validate: Variables are in allowed positions/Boolean => Boolean", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($booleanArg: Boolean)\n {\n complicatedArgs {\n booleanArgField(booleanArg: $booleanArg)\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/Boolean => Boolean within fragment", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment booleanArgFrag on ComplicatedArgs {\n booleanArgField(booleanArg: $booleanArg)\n }\n query Query($booleanArg: Boolean)\n {\n complicatedArgs {\n ...booleanArgFrag\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/Boolean => Boolean within fragment", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($booleanArg: Boolean)\n {\n complicatedArgs {\n ...booleanArgFrag\n }\n }\n fragment booleanArgFrag on ComplicatedArgs {\n booleanArgField(booleanArg: $booleanArg)\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/Boolean! => Boolean", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($nonNullBooleanArg: Boolean!)\n {\n complicatedArgs {\n booleanArgField(booleanArg: $nonNullBooleanArg)\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/Boolean! => Boolean within fragment", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment booleanArgFrag on ComplicatedArgs {\n booleanArgField(booleanArg: $nonNullBooleanArg)\n }\n\n query Query($nonNullBooleanArg: Boolean!)\n {\n complicatedArgs {\n ...booleanArgFrag\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/[String] => [String]", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($stringListVar: [String])\n {\n complicatedArgs {\n stringListArgField(stringListArg: $stringListVar)\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/[String!] => [String]", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($stringListVar: [String!])\n {\n complicatedArgs {\n stringListArgField(stringListArg: $stringListVar)\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/String => [String] in item position", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($stringVar: String)\n {\n complicatedArgs {\n stringListArgField(stringListArg: [$stringVar])\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/String! => [String] in item position", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($stringVar: String!)\n {\n complicatedArgs {\n stringListArgField(stringListArg: [$stringVar])\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/ComplexInput => ComplexInput", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($complexVar: ComplexInput)\n {\n complicatedArgs {\n complexArgField(complexArg: $complexVar)\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/ComplexInput => ComplexInput in field position", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($boolVar: Boolean = false)\n {\n complicatedArgs {\n complexArgField(complexArg: {requiredArg: $boolVar})\n }\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/Boolean! => Boolean! in directive", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($boolVar: Boolean!)\n {\n dog @include(if: $boolVar)\n }\n ", "errors": [] }, { "name": "Validate: Variables are in allowed positions/Int => Int!", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($intArg: Int) {\n complicatedArgs {\n nonNullIntArgField(nonNullIntArg: $intArg)\n }\n }\n ", "errors": [ { "message": "Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\".", "locations": [ { "line": 2, "column": 19 }, { "line": 4, "column": 45 } ] } ] }, { "name": "Validate: Variables are in allowed positions/Int => Int! within fragment", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment nonNullIntArgFieldFrag on ComplicatedArgs {\n nonNullIntArgField(nonNullIntArg: $intArg)\n }\n\n query Query($intArg: Int) {\n complicatedArgs {\n ...nonNullIntArgFieldFrag\n }\n }\n ", "errors": [ { "message": "Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\".", "locations": [ { "line": 6, "column": 19 }, { "line": 3, "column": 43 } ] } ] }, { "name": "Validate: Variables are in allowed positions/Int => Int! within nested fragment", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n fragment outerFrag on ComplicatedArgs {\n ...nonNullIntArgFieldFrag\n }\n\n fragment nonNullIntArgFieldFrag on ComplicatedArgs {\n nonNullIntArgField(nonNullIntArg: $intArg)\n }\n\n query Query($intArg: Int) {\n complicatedArgs {\n ...outerFrag\n }\n }\n ", "errors": [ { "message": "Variable \"$intArg\" of type \"Int\" used in position expecting type \"Int!\".", "locations": [ { "line": 10, "column": 19 }, { "line": 7, "column": 43 } ] } ] }, { "name": "Validate: Variables are in allowed positions/String over Boolean", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($stringVar: String) {\n complicatedArgs {\n booleanArgField(booleanArg: $stringVar)\n }\n }\n ", "errors": [ { "message": "Variable \"$stringVar\" of type \"String\" used in position expecting type \"Boolean\".", "locations": [ { "line": 2, "column": 19 }, { "line": 4, "column": 39 } ] } ] }, { "name": "Validate: Variables are in allowed positions/String => [String]", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($stringVar: String) {\n complicatedArgs {\n stringListArgField(stringListArg: $stringVar)\n }\n }\n ", "errors": [ { "message": "Variable \"$stringVar\" of type \"String\" used in position expecting type \"[String]\".", "locations": [ { "line": 2, "column": 19 }, { "line": 4, "column": 45 } ] } ] }, { "name": "Validate: Variables are in allowed positions/Boolean => Boolean! in directive", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($boolVar: Boolean) {\n dog @include(if: $boolVar)\n }\n ", "errors": [ { "message": "Variable \"$boolVar\" of type \"Boolean\" used in position expecting type \"Boolean!\".", "locations": [ { "line": 2, "column": 19 }, { "line": 3, "column": 26 } ] } ] }, { "name": "Validate: Variables are in allowed positions/String => Boolean! in directive", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($stringVar: String) {\n dog @include(if: $stringVar)\n }\n ", "errors": [ { "message": "Variable \"$stringVar\" of type \"String\" used in position expecting type \"Boolean!\".", "locations": [ { "line": 2, "column": 19 }, { "line": 3, "column": 26 } ] } ] }, { "name": "Validate: Variables are in allowed positions/[String] => [String!]", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($stringListVar: [String])\n {\n complicatedArgs {\n stringListNonNullArgField(stringListNonNullArg: $stringListVar)\n }\n }\n ", "errors": [ { "message": "Variable \"$stringListVar\" of type \"[String]\" used in position expecting type \"[String!]\".", "locations": [ { "line": 2, "column": 19 }, { "line": 5, "column": 59 } ] } ] }, { "name": "Validate: Variables are in allowed positions/Allows optional (nullable) variables with default values/Int => Int! fails when variable provides null default value", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($intVar: Int = null) {\n complicatedArgs {\n nonNullIntArgField(nonNullIntArg: $intVar)\n }\n }\n ", "errors": [ { "message": "Variable \"$intVar\" of type \"Int\" used in position expecting type \"Int!\".", "locations": [ { "line": 2, "column": 21 }, { "line": 4, "column": 47 } ] } ] }, { "name": "Validate: Variables are in allowed positions/Allows optional (nullable) variables with default values/Int => Int! when variable provides non-null default value", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($intVar: Int = 1) {\n complicatedArgs {\n nonNullIntArgField(nonNullIntArg: $intVar)\n }\n }", "errors": [] }, { "name": "Validate: Variables are in allowed positions/Allows optional (nullable) variables with default values/Int => Int! when optional argument provides default value", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($intVar: Int) {\n complicatedArgs {\n nonNullFieldWithDefault(nonNullIntArg: $intVar)\n }\n }", "errors": [] }, { "name": "Validate: Variables are in allowed positions/Allows optional (nullable) variables with default values/Boolean => Boolean! in directive with default value with option", "rule": "VariablesInAllowedPositionRule", "schema": "3vm8aJ4UHhzj01V2PFAYRVZj2R0SPm+mxfn/edqH9aU=", "query": "\n query Query($boolVar: Boolean = false) {\n dog @include(if: $boolVar)\n }", "errors": [] } ] }graphql-go-1.6.0/internal/validation/testdata/tsconfig.json000066400000000000000000000024251475633407000240610ustar00rootroot00000000000000{ "include": [ "run.ts", "node_modules/graphql/**/*.ts" ], "compilerOptions": { "lib": [ "es2022", "dom" // Workaround for missing web-compatible globals in `@types/node` ], "target": "es2021", "module": "es2022", "moduleResolution": "node", "noEmit": true, "isolatedModules": true, "verbatimModuleSyntax": true, "forceConsistentCasingInFileNames": true, // Type Checking // https://www.typescriptlang.org/tsconfig#Type_Checking_6248 "strict": true, "useUnknownInCatchVariables": false, // FIXME part of 'strict' but is temporary disabled // All checks that are not part of "strict" "allowUnreachableCode": false, "allowUnusedLabels": false, "exactOptionalPropertyTypes": true, "noFallthroughCasesInSwitch": false, // TODO consider "noImplicitOverride": true, "noImplicitReturns": false, // TODO consider "noPropertyAccessFromIndexSignature": false, // TODO consider "noUncheckedIndexedAccess": false, // FIXME "noUnusedLocals": true, "noUnusedParameters": true, "allowSyntheticDefaultImports": true }, "ts-node": { "esm": true, // Dependencies would normally be skipped, however we've got to checkout graphql-js as a source package "skipIgnore": true } } graphql-go-1.6.0/internal/validation/validate_max_depth_test.go000066400000000000000000000225151475633407000247530ustar00rootroot00000000000000package validation import ( "testing" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/internal/query" "github.com/graph-gophers/graphql-go/internal/schema" ) const ( simpleSchema = `schema { query: Query } type Query { characters: [Character]! } type Character { id: ID! name: String! friends: [Character]! }` interfaceSimple = `schema { query: Query } type Query { characters: [Character] } interface Character { id: ID! name: String! friends: [Character] enemies: [Character] appearsIn: [Episode]! } enum Episode { NEWHOPE EMPIRE JEDI } type Starship { id: ID! } type Human implements Character { id: ID! name: String! friends: [Character] enemies: [Character] appearsIn: [Episode]! starships: [Starship] totalCredits: Int } type Droid implements Character { id: ID! name: String! friends: [Character] enemies: [Character] appearsIn: [Episode]! primaryFunction: String }` ) type maxDepthTestCase struct { name string query string depth int failure bool expectedErrors []string } func (tc maxDepthTestCase) Run(t *testing.T, s *ast.Schema) { t.Run(tc.name, func(t *testing.T) { doc, qErr := query.Parse(tc.query) if qErr != nil { t.Fatal(qErr) } errs := Validate(s, doc, nil, tc.depth) if len(tc.expectedErrors) > 0 { if len(errs) > 0 { for _, expected := range tc.expectedErrors { found := false for _, err := range errs { if err.Rule == expected { found = true break } } if !found { t.Errorf("expected error %v is missing", expected) } } } else { t.Errorf("expected errors [%v] are missing", tc.expectedErrors) } } if (len(errs) > 0) != tc.failure { t.Errorf("expected failure: %t, actual errors (%d): %v", tc.failure, len(errs), errs) } }) } func TestMaxDepth(t *testing.T) { s, err := schema.ParseSchema(simpleSchema, false) if err != nil { t.Fatal(err) } for _, tc := range []maxDepthTestCase{ { name: "off", query: `query Okay { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 friends { # depth 3 friends { # depth 4 id # depth 5 name # depth 5 } } } } }`, depth: 0, }, { name: "maxDepth-1", query: `query Fine { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 id # depth 3 name # depth 3 } } }`, depth: 4, }, { name: "maxDepth", query: `query Deep { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 id # depth 3 name # depth 3 } } }`, depth: 3, }, { name: "maxDepth+1", query: `query TooDeep { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 friends { # depth 3 friends { # depth 4 id # depth 5 name # depth 5 } } } } }`, depth: 4, failure: true, }, } { tc.Run(t, s) } } func TestMaxDepthInlineFragments(t *testing.T) { s, err := schema.ParseSchema(interfaceSimple, false) if err != nil { t.Fatal(err) } for _, tc := range []maxDepthTestCase{ { name: "maxDepth-1", query: `query { # depth 0 characters { # depth 1 name # depth 2 ... on Human { # depth 2 totalCredits # depth 2 } } }`, depth: 3, }, { name: "maxDepth", query: `query { # depth 0 characters { # depth 1 ... on Droid { # depth 2 primaryFunction # depth 2 } } }`, depth: 2, }, { name: "maxDepth+1", query: `query { # depth 0 characters { # depth 1 ... on Droid { # depth 2 primaryFunction # depth 2 } } }`, depth: 1, failure: true, }, } { tc.Run(t, s) } } func TestMaxDepthFragmentSpreads(t *testing.T) { s, err := schema.ParseSchema(interfaceSimple, false) if err != nil { t.Fatal(err) } for _, tc := range []maxDepthTestCase{ { name: "maxDepth-1", query: `fragment friend on Character { id # depth 5 name friends { name # depth 6 } } query { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 friends { # depth 3 friends { # depth 4 ...friend # depth 5 } } } } }`, depth: 7, }, { name: "maxDepth", query: `fragment friend on Character { id # depth 5 name } query { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 friends { # depth 3 friends { # depth 4 ...friend # depth 5 } } } } }`, depth: 5, }, { name: "maxDepth+1", query: `fragment friend on Character { id # depth 6 name friends { name # depth 7 } } query { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 friends { # depth 3 friends { # depth 4 friends { # depth 5 ...friend # depth 6 } } } } } }`, depth: 6, failure: true, }, { name: "spreadAtDifferentDepths", query: ` fragment character on Character { name # depth + 0 friends { # depth + 0 name # depth + 1 } } query laterDepthValidated { ...character # depth 1 (+1) enemies { # depth 1 friends { # depth 2 ...character # depth 2 (+1), should error! } } } `, depth: 2, failure: true, }, { name: "spreadAtSameDepth", query: ` fragment character on Character { name # depth + 0 friends { # depth + 0 name # depth + 1 } } query { characters { # depth 1 friends { # depth 2 ...character # depth 3 (+1) } enemies { # depth 2 ...character # depth 3 (+1) } } } `, depth: 4, }, { name: "fragmentCycle", query: ` fragment X on Query { ...Y } fragment Y on Query { ...Z } fragment Z on Query { ...X } query { ...X } `, depth: 10, failure: true, }, } { tc.Run(t, s) } } func TestMaxDepthUnknownFragmentSpreads(t *testing.T) { s, err := schema.ParseSchema(interfaceSimple, false) if err != nil { t.Fatal(err) } for _, tc := range []maxDepthTestCase{ { name: "maxDepthUnknownFragment", query: `query { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 friends { # depth 3 friends { # depth 4 friends { # depth 5 ...unknownFragment # depth 6 } } } } } }`, depth: 6, failure: true, expectedErrors: []string{"MaxDepthEvaluationError"}, }, } { tc.Run(t, s) } } func TestMaxDepthValidation(t *testing.T) { s, err := schema.ParseSchema(interfaceSimple, false) if err != nil { t.Fatal(err) } for _, tc := range []struct { name string query string maxDepth int expected bool }{ { name: "off", query: `query Fine { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 id # depth 3 name # depth 3 } } }`, maxDepth: 0, }, { name: "fields", query: `query Fine { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 id # depth 3 name # depth 3 } } }`, maxDepth: 2, expected: true, }, { name: "fragment", query: `fragment friend on Character { id # depth 6 name friends { name # depth 7 } } query { # depth 0 characters { # depth 1 id # depth 2 name # depth 2 friends { # depth 2 friends { # depth 3 friends { # depth 4 friends { # depth 5 ...friend # depth 6 } } } } } }`, maxDepth: 5, expected: true, }, { name: "inlinefragment", query: `query { # depth 0 characters { # depth 1 ... on Droid { # depth 2 primaryFunction # depth 2 } } }`, maxDepth: 1, expected: true, }, } { t.Run(tc.name, func(t *testing.T) { doc, err := query.Parse(tc.query) if err != nil { t.Fatal(err) } context := newContext(s, doc, tc.maxDepth) op := doc.Operations[0] opc := &opContext{context: context, ops: doc.Operations} actual := validateMaxDepth(opc, op.Selections, nil, 1) if actual != tc.expected { t.Errorf("expected %t, actual %t", tc.expected, actual) } }) } } graphql-go-1.6.0/internal/validation/validation.go000066400000000000000000000731361475633407000222310ustar00rootroot00000000000000package validation import ( "fmt" "math" "reflect" "strconv" "strings" "text/scanner" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/common" "github.com/graph-gophers/graphql-go/internal/query" ) type varSet map[*ast.InputValueDefinition]struct{} type selectionPair struct{ a, b ast.Selection } type nameSet map[string][]errors.Location type fieldInfo struct { sf *ast.FieldDefinition parent ast.NamedType } type context struct { schema *ast.Schema doc *ast.ExecutableDefinition errs []*errors.QueryError opErrs map[*ast.OperationDefinition][]*errors.QueryError usedVars map[*ast.OperationDefinition]varSet fieldMap map[*ast.Field]fieldInfo overlapValidated map[selectionPair]struct{} maxDepth int } func (c *context) addErr(loc errors.Location, rule string, format string, a ...interface{}) { c.addErrMultiLoc([]errors.Location{loc}, rule, format, a...) } func (c *context) addErrMultiLoc(locs []errors.Location, rule string, format string, a ...interface{}) { c.errs = append(c.errs, &errors.QueryError{ Message: fmt.Sprintf(format, a...), Locations: locs, Rule: rule, }) } type opContext struct { *context ops []*ast.OperationDefinition } func newContext(s *ast.Schema, doc *ast.ExecutableDefinition, maxDepth int) *context { return &context{ schema: s, doc: doc, opErrs: make(map[*ast.OperationDefinition][]*errors.QueryError), usedVars: make(map[*ast.OperationDefinition]varSet), fieldMap: make(map[*ast.Field]fieldInfo), overlapValidated: make(map[selectionPair]struct{}), maxDepth: maxDepth, } } func Validate(s *ast.Schema, doc *ast.ExecutableDefinition, variables map[string]interface{}, maxDepth int) []*errors.QueryError { c := newContext(s, doc, maxDepth) opNames := make(nameSet, len(doc.Operations)) fragUsedBy := make(map[*ast.FragmentDefinition][]*ast.OperationDefinition) for _, op := range doc.Operations { c.usedVars[op] = make(varSet) opc := &opContext{c, []*ast.OperationDefinition{op}} // Check if max depth is exceeded, if it's set. If max depth is exceeded, // don't continue to validate the document and exit early. if validateMaxDepth(opc, op.Selections, nil, 1) { return c.errs } if op.Name.Name == "" && len(doc.Operations) != 1 { c.addErr(op.Loc, "LoneAnonymousOperationRule", "This anonymous operation must be the only defined operation.") } if n := op.Name.Name; n != "" { opNames[n] = append(opNames[n], op.Name.Loc) } varNames := make(nameSet, len(op.Vars)) for _, v := range op.Vars { varNames[v.Name.Name] = append(varNames[v.Name.Name], v.Name.Loc) validateDirectives(opc, "VARIABLE_DEFINITION", v.Directives) t := resolveType(c, v.Type) if !canBeInput(t) { c.addErr(v.TypeLoc, "VariablesAreInputTypesRule", "Variable %q cannot be non-input type %q.", "$"+v.Name.Name, t) } validateValue(opc, v, variables[v.Name.Name], t) if v.Default != nil { validateLiteral(opc, v.Default) if t != nil { if nn, ok := t.(*ast.NonNull); ok { c.addErr(v.Default.Location(), "DefaultValuesOfCorrectType", "Variable %q of type %q is required and will not use the default value. Perhaps you meant to use type %q.", "$"+v.Name.Name, t, nn.OfType) } if ok, reason := validateValueType(opc, v.Default, t); !ok { c.addErr(v.Default.Location(), "DefaultValuesOfCorrectType", "Variable %q of type %q has invalid default value %s.\n%s", "$"+v.Name.Name, t, v.Default, reason) } } } } validateDirectives(opc, string(op.Type), op.Directives) for n, locs := range varNames { validateName(c, locs, n, "UniqueVariableNamesRule", "variable") } var entryPoint ast.NamedType switch op.Type { case query.Query: entryPoint = s.RootOperationTypes["query"] case query.Mutation: entryPoint = s.RootOperationTypes["mutation"] case query.Subscription: entryPoint = s.RootOperationTypes["subscription"] default: panic("unreachable") } validateSelectionSet(opc, op.Selections, entryPoint) fragUsed := make(map[*ast.FragmentDefinition]struct{}) markUsedFragments(c, op.Selections, fragUsed) for frag := range fragUsed { fragUsedBy[frag] = append(fragUsedBy[frag], op) } } for n, locs := range opNames { validateName(c, locs, n, "UniqueOperationNamesRule", "operation") } fragNames := make(nameSet, len(doc.Fragments)) fragVisited := make(map[*ast.FragmentDefinition]struct{}) for _, frag := range doc.Fragments { opc := &opContext{c, fragUsedBy[frag]} fragNames[frag.Name.Name] = append(fragNames[frag.Name.Name], frag.Name.Loc) validateDirectives(opc, "FRAGMENT_DEFINITION", frag.Directives) t := unwrapType(resolveType(c, &frag.On)) // continue even if t is nil if t != nil && !canBeFragment(t) { c.addErr(frag.On.Loc, "FragmentsOnCompositeTypesRule", "Fragment %q cannot condition on non composite type %q.", frag.Name.Name, t) continue } validateSelectionSet(opc, frag.Selections, t) if _, ok := fragVisited[frag]; !ok { detectFragmentCycle(c, frag.Selections, fragVisited, nil, map[string]int{frag.Name.Name: 0}) } } for n, locs := range fragNames { validateName(c, locs, n, "UniqueFragmentNamesRule", "fragment") } for _, frag := range doc.Fragments { if len(fragUsedBy[frag]) == 0 { c.addErr(frag.Loc, "NoUnusedFragmentsRule", "Fragment %q is never used.", frag.Name.Name) } } for _, op := range doc.Operations { c.errs = append(c.errs, c.opErrs[op]...) opUsedVars := c.usedVars[op] for _, v := range op.Vars { if _, ok := opUsedVars[v]; !ok { opSuffix := "" if op.Name.Name != "" { opSuffix = fmt.Sprintf(" in operation %q", op.Name.Name) } c.addErr(v.Loc, "NoUnusedVariablesRule", "Variable %q is never used%s.", "$"+v.Name.Name, opSuffix) } } } return c.errs } func validateValue(c *opContext, v *ast.InputValueDefinition, val interface{}, t ast.Type) { switch t := t.(type) { case *ast.NonNull: if val == nil { c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid value null.\nExpected type \"%s\", found null.", v.Name.Name, t) return } validateValue(c, v, val, t.OfType) case *ast.List: if val == nil { return } vv, ok := val.([]interface{}) if !ok { // Input coercion rules allow single items without wrapping array validateValue(c, v, val, t.OfType) return } for _, elem := range vv { validateValue(c, v, elem, t.OfType) } case *ast.EnumTypeDefinition: if val == nil { return } e, ok := val.(string) if !ok { c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid type %T.\nExpected type \"%s\", found %v.", v.Name.Name, val, t, val) return } for _, option := range t.EnumValuesDefinition { if option.EnumValue == e { return } } c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid value %s.\nExpected type \"%s\", found %s.", v.Name.Name, e, t, e) case *ast.InputObject: if val == nil { return } in, ok := val.(map[string]interface{}) if !ok { c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid type %T.\nExpected type \"%s\", found %s.", v.Name.Name, val, t, val) return } for _, f := range t.Values { fieldVal := in[f.Name.Name] validateValue(c, f, fieldVal, f.Type) } } } // validates the query doesn't go deeper than maxDepth (if set). Returns whether // or not query validated max depth to avoid excessive recursion. // // The visited map is necessary to ensure that max depth validation does not get stuck in cyclical // fragment spreads. func validateMaxDepth(c *opContext, sels []ast.Selection, visited map[*ast.FragmentDefinition]struct{}, depth int) bool { // maxDepth checking is turned off when maxDepth is 0 if c.maxDepth == 0 { return false } exceededMaxDepth := false if visited == nil { visited = map[*ast.FragmentDefinition]struct{}{} } for _, sel := range sels { switch sel := sel.(type) { case *ast.Field: if depth > c.maxDepth { exceededMaxDepth = true c.addErr(sel.Alias.Loc, "MaxDepthExceeded", "Field %q has depth %d that exceeds max depth %d", sel.Name.Name, depth, c.maxDepth) continue } exceededMaxDepth = exceededMaxDepth || validateMaxDepth(c, sel.SelectionSet, visited, depth+1) case *ast.InlineFragment: // Depth is not checked because inline fragments resolve to other fields which are checked. // Depth is not incremented because inline fragments have the same depth as neighboring fields exceededMaxDepth = exceededMaxDepth || validateMaxDepth(c, sel.Selections, visited, depth) case *ast.FragmentSpread: // Depth is not checked because fragments resolve to other fields which are checked. frag := c.doc.Fragments.Get(sel.Name.Name) if frag == nil { // In case of unknown fragment (invalid request), ignore max depth evaluation c.addErr(sel.Loc, "MaxDepthEvaluationError", "Unknown fragment %q. Unable to evaluate depth.", sel.Name.Name) continue } if _, ok := visited[frag]; ok { // we've already seen this fragment, don't check depth again. continue } visited[frag] = struct{}{} // Depth is not incremented because fragments have the same depth as surrounding fields exceededMaxDepth = exceededMaxDepth || validateMaxDepth(c, frag.Selections, visited, depth) } } return exceededMaxDepth } func validateSelectionSet(c *opContext, sels []ast.Selection, t ast.NamedType) { for _, sel := range sels { validateSelection(c, sel, t) } for i, a := range sels { for _, b := range sels[i+1:] { c.validateOverlap(a, b, nil, nil) } } } func validateSelection(c *opContext, sel ast.Selection, t ast.NamedType) { switch sel := sel.(type) { case *ast.Field: validateDirectives(c, "FIELD", sel.Directives) fieldName := sel.Name.Name var f *ast.FieldDefinition switch fieldName { case "__typename": f = &ast.FieldDefinition{ Name: "__typename", Type: c.schema.Types["String"], } case "__schema": f = &ast.FieldDefinition{ Name: "__schema", Type: c.schema.Types["__Schema"], } case "__type": f = &ast.FieldDefinition{ Name: "__type", Arguments: ast.ArgumentsDefinition{ &ast.InputValueDefinition{ Name: ast.Ident{Name: "name"}, Type: &ast.NonNull{OfType: c.schema.Types["String"]}, }, }, Type: c.schema.Types["__Type"], } default: f = fields(t).Get(fieldName) if f == nil && t != nil { suggestion := makeSuggestion("Did you mean", fields(t).Names(), fieldName) c.addErr(sel.Alias.Loc, "FieldsOnCorrectTypeRule", "Cannot query field %q on type %q.%s", fieldName, t, suggestion) } } c.fieldMap[sel] = fieldInfo{sf: f, parent: t} validateArgumentLiterals(c, sel.Arguments) if f != nil { validateArgumentTypes(c, sel.Arguments, f.Arguments, sel.Alias.Loc, func() string { return fmt.Sprintf(`field "%s.%s"`, t, fieldName) }, func() string { return fmt.Sprintf("Field %q", fieldName) }, ) } var ft ast.Type if f != nil { ft = f.Type sf := hasSubfields(ft) if sf && sel.SelectionSet == nil { c.addErr(sel.Alias.Loc, "ScalarLeafsRule", "Field %q of type %q must have a selection of subfields. Did you mean \"%s { ... }\"?", fieldName, ft, fieldName) } if !sf && sel.SelectionSet != nil { c.addErr(sel.SelectionSetLoc, "ScalarLeafsRule", "Field %q must not have a selection since type %q has no subfields.", fieldName, ft) } } if sel.SelectionSet != nil { validateSelectionSet(c, sel.SelectionSet, unwrapType(ft)) } case *ast.InlineFragment: validateDirectives(c, "INLINE_FRAGMENT", sel.Directives) if sel.On.Name != "" { fragTyp := unwrapType(resolveType(c.context, &sel.On)) if fragTyp != nil && !compatible(t, fragTyp) { c.addErr(sel.Loc, "PossibleFragmentSpreadsRule", "Fragment cannot be spread here as objects of type %q can never be of type %q.", t, fragTyp) } t = fragTyp // continue even if t is nil } if t != nil && !canBeFragment(t) { c.addErr(sel.On.Loc, "FragmentsOnCompositeTypesRule", "Fragment cannot condition on non composite type %q.", t) return } validateSelectionSet(c, sel.Selections, unwrapType(t)) case *ast.FragmentSpread: validateDirectives(c, "FRAGMENT_SPREAD", sel.Directives) frag := c.doc.Fragments.Get(sel.Name.Name) if frag == nil { c.addErr(sel.Name.Loc, "KnownFragmentNamesRule", "Unknown fragment %q.", sel.Name.Name) return } fragTyp := c.schema.Types[frag.On.Name] if !compatible(t, fragTyp) { c.addErr(sel.Loc, "PossibleFragmentSpreadsRule", "Fragment %q cannot be spread here as objects of type %q can never be of type %q.", frag.Name.Name, t, fragTyp) } default: panic("unreachable") } } func compatible(a, b ast.Type) bool { for _, pta := range possibleTypes(a) { for _, ptb := range possibleTypes(b) { if pta == ptb { return true } } } return false } func possibleTypes(t ast.Type) []*ast.ObjectTypeDefinition { switch t := t.(type) { case *ast.ObjectTypeDefinition: return []*ast.ObjectTypeDefinition{t} case *ast.InterfaceTypeDefinition: return t.PossibleTypes case *ast.Union: return t.UnionMemberTypes default: return nil } } func markUsedFragments(c *context, sels []ast.Selection, fragUsed map[*ast.FragmentDefinition]struct{}) { for _, sel := range sels { switch sel := sel.(type) { case *ast.Field: if sel.SelectionSet != nil { markUsedFragments(c, sel.SelectionSet, fragUsed) } case *ast.InlineFragment: markUsedFragments(c, sel.Selections, fragUsed) case *ast.FragmentSpread: frag := c.doc.Fragments.Get(sel.Name.Name) if frag == nil { return } if _, ok := fragUsed[frag]; ok { continue } fragUsed[frag] = struct{}{} markUsedFragments(c, frag.Selections, fragUsed) default: panic("unreachable") } } } func detectFragmentCycle(c *context, sels []ast.Selection, fragVisited map[*ast.FragmentDefinition]struct{}, spreadPath []*ast.FragmentSpread, spreadPathIndex map[string]int) { for _, sel := range sels { detectFragmentCycleSel(c, sel, fragVisited, spreadPath, spreadPathIndex) } } func detectFragmentCycleSel(c *context, sel ast.Selection, fragVisited map[*ast.FragmentDefinition]struct{}, spreadPath []*ast.FragmentSpread, spreadPathIndex map[string]int) { switch sel := sel.(type) { case *ast.Field: if sel.SelectionSet != nil { detectFragmentCycle(c, sel.SelectionSet, fragVisited, spreadPath, spreadPathIndex) } case *ast.InlineFragment: detectFragmentCycle(c, sel.Selections, fragVisited, spreadPath, spreadPathIndex) case *ast.FragmentSpread: frag := c.doc.Fragments.Get(sel.Name.Name) if frag == nil { return } spreadPath = append(spreadPath, sel) if i, ok := spreadPathIndex[frag.Name.Name]; ok { cyclePath := spreadPath[i:] via := "" if len(cyclePath) > 1 { names := make([]string, len(cyclePath)-1) for i, frag := range cyclePath[:len(cyclePath)-1] { names[i] = fmt.Sprintf("%q", frag.Name.Name) } via = " via " + strings.Join(names, ", ") } locs := make([]errors.Location, len(cyclePath)) for i, frag := range cyclePath { locs[i] = frag.Loc } c.addErrMultiLoc(locs, "NoFragmentCyclesRule", "Cannot spread fragment %q within itself%s.", frag.Name.Name, via) return } if _, ok := fragVisited[frag]; ok { return } fragVisited[frag] = struct{}{} spreadPathIndex[frag.Name.Name] = len(spreadPath) detectFragmentCycle(c, frag.Selections, fragVisited, spreadPath, spreadPathIndex) delete(spreadPathIndex, frag.Name.Name) default: panic("unreachable") } } func (c *context) validateOverlap(a, b ast.Selection, reasons *[]string, locs *[]errors.Location) { if a == b { return } if _, ok := c.overlapValidated[selectionPair{a, b}]; ok { return } c.overlapValidated[selectionPair{a, b}] = struct{}{} c.overlapValidated[selectionPair{b, a}] = struct{}{} switch a := a.(type) { case *ast.Field: switch b := b.(type) { case *ast.Field: if b.Alias.Loc.Before(a.Alias.Loc) { a, b = b, a } if reasons2, locs2 := c.validateFieldOverlap(a, b); len(reasons2) != 0 { locs2 = append(locs2, a.Alias.Loc, b.Alias.Loc) if reasons == nil { c.addErrMultiLoc(locs2, "OverlappingFieldsCanBeMergedRule", "Fields %q conflict because %s. Use different aliases on the fields to fetch both if this was intentional.", a.Alias.Name, strings.Join(reasons2, " and ")) return } for _, r := range reasons2 { *reasons = append(*reasons, fmt.Sprintf("subfields %q conflict because %s", a.Alias.Name, r)) } *locs = append(*locs, locs2...) } case *ast.InlineFragment: for _, sel := range b.Selections { c.validateOverlap(a, sel, reasons, locs) } case *ast.FragmentSpread: if frag := c.doc.Fragments.Get(b.Name.Name); frag != nil { for _, sel := range frag.Selections { c.validateOverlap(a, sel, reasons, locs) } } default: panic("unreachable") } case *ast.InlineFragment: for _, sel := range a.Selections { c.validateOverlap(sel, b, reasons, locs) } case *ast.FragmentSpread: if frag := c.doc.Fragments.Get(a.Name.Name); frag != nil { for _, sel := range frag.Selections { c.validateOverlap(sel, b, reasons, locs) } } default: panic("unreachable") } } func (c *context) validateFieldOverlap(a, b *ast.Field) ([]string, []errors.Location) { if a.Alias.Name != b.Alias.Name { return nil, nil } if asf := c.fieldMap[a].sf; asf != nil { if bsf := c.fieldMap[b].sf; bsf != nil { if !typesCompatible(asf.Type, bsf.Type) { return []string{fmt.Sprintf("they return conflicting types %q and %q", asf.Type, bsf.Type)}, nil } } } at := c.fieldMap[a].parent bt := c.fieldMap[b].parent if at == nil || bt == nil || at == bt { if a.Name.Name != b.Name.Name { return []string{fmt.Sprintf("%q and %q are different fields", a.Name.Name, b.Name.Name)}, nil } if argumentsConflict(a.Arguments, b.Arguments) { return []string{"they have differing arguments"}, nil } } var reasons []string var locs []errors.Location for _, a2 := range a.SelectionSet { for _, b2 := range b.SelectionSet { c.validateOverlap(a2, b2, &reasons, &locs) } } return reasons, locs } func argumentsConflict(a, b ast.ArgumentList) bool { if len(a) != len(b) { return true } for _, argA := range a { valB, ok := b.Get(argA.Name.Name) if !ok || !reflect.DeepEqual(argA.Value.Deserialize(nil), valB.Deserialize(nil)) { return true } } return false } func fields(t ast.Type) ast.FieldsDefinition { switch t := t.(type) { case *ast.ObjectTypeDefinition: return t.Fields case *ast.InterfaceTypeDefinition: return t.Fields default: return nil } } func unwrapType(t ast.Type) ast.NamedType { if t == nil { return nil } for { switch t2 := t.(type) { case ast.NamedType: return t2 case *ast.List: t = t2.OfType case *ast.NonNull: t = t2.OfType default: panic("unreachable") } } } func resolveType(c *context, t ast.Type) ast.Type { t2, err := common.ResolveType(t, c.schema.Resolve) if err != nil { c.errs = append(c.errs, err) } return t2 } func validateDirectives(c *opContext, loc string, directives ast.DirectiveList) { directiveNames := make(nameSet, len(directives)) for _, d := range directives { dirName := d.Name.Name directiveNames[dirName] = append(directiveNames[dirName], d.Name.Loc) validateArgumentLiterals(c, d.Arguments) dd, ok := c.schema.Directives[dirName] if !ok { c.addErr(d.Name.Loc, "KnownDirectivesRule", "Unknown directive %q.", "@"+dirName) continue } locOK := false for _, allowedLoc := range dd.Locations { if loc == allowedLoc { locOK = true break } } if !locOK { c.addErr(d.Name.Loc, "KnownDirectivesRule", "Directive %q may not be used on %s.", "@"+dirName, loc) } validateArgumentTypes(c, d.Arguments, dd.Arguments, d.Name.Loc, func() string { return fmt.Sprintf("directive %q", "@"+dirName) }, func() string { return fmt.Sprintf("Directive %q", "@"+dirName) }, ) } // Iterating in the declared order, rather than using the directiveNames ordering which is random for _, d := range directives { n := d.Name.Name ds := directiveNames[n] if len(ds) <= 1 { continue } dd, ok := c.schema.Directives[n] if !ok { // Invalid directive will have been flagged already continue } if dd.Repeatable { continue } for _, loc := range ds[1:] { // Duplicate directive errors are inconsistent with the behaviour for other types in graphql-js // Instead of reporting a single error with all locations, errors are reported for each duplicate after the first declaration // with the original location, and the duplicate. Behaviour is replicated here, as we use those tests to validate the implementation validateNameCustomMsg(c.context, []errors.Location{ds[0], loc}, "UniqueDirectivesPerLocationRule", func() string { return fmt.Sprintf("The directive %q can only be used once at this location.", "@"+n) }) } // drop the name from the set to prevent the same errors being re-added for duplicates delete(directiveNames, n) } } func validateName(c *context, locs []errors.Location, name string, rule string, kind string) { validateNameCustomMsg(c, locs, rule, func() string { if kind == "variable" { return fmt.Sprintf("There can be only one %s named %q.", kind, "$"+name) } return fmt.Sprintf("There can be only one %s named %q.", kind, name) }) } func validateNameCustomMsg(c *context, locs []errors.Location, rule string, msg func() string) { if len(locs) > 1 { c.addErrMultiLoc(locs, rule, msg()) return } } func validateArgumentTypes(c *opContext, args ast.ArgumentList, argDecls ast.ArgumentsDefinition, loc errors.Location, owner1, owner2 func() string) { for _, selArg := range args { arg := argDecls.Get(selArg.Name.Name) if arg == nil { suggestion := makeSuggestion("Did you mean", argDecls.Names(), selArg.Name.Name) c.addErr(selArg.Name.Loc, "KnownArgumentNamesRule", "Unknown argument %q on %s.%s", selArg.Name.Name, owner1(), suggestion) continue } value := selArg.Value if ok, reason := validateValueType(c, value, arg.Type); !ok { c.addErr(value.Location(), "ArgumentsOfCorrectType", "Argument %q has invalid value %s.\n%s", arg.Name.Name, value, reason) } } for _, decl := range argDecls { if _, ok := decl.Type.(*ast.NonNull); ok { if _, ok := args.Get(decl.Name.Name); !ok { if decl.Default != nil { continue } c.addErr(loc, "ProvidedRequiredArgumentsRule", "%s argument %q of type %q is required, but it was not provided.", owner2(), decl.Name.Name, decl.Type) } } } } func validateArgumentLiterals(c *opContext, args ast.ArgumentList) { argNames := make(nameSet, len(args)) for _, arg := range args { validateLiteral(c, arg.Value) argNames[arg.Name.Name] = append(argNames[arg.Name.Name], arg.Name.Loc) } for n, locs := range argNames { validateName(c.context, locs, n, "UniqueArgumentNamesRule", "argument") } } func validateLiteral(c *opContext, l ast.Value) { switch l := l.(type) { case *ast.ObjectValue: fieldNames := make(nameSet, len(l.Fields)) for _, f := range l.Fields { fieldNames[f.Name.Name] = append(fieldNames[f.Name.Name], f.Name.Loc) validateLiteral(c, f.Value) } for n, locs := range fieldNames { if len(locs) <= 1 { continue } // Similar to for directives, duplicates here aren't all reported together but using an error for each duplicate for _, loc := range locs[1:] { validateName(c.context, []errors.Location{locs[0], loc}, n, "UniqueInputFieldNamesRule", "input field") } } case *ast.ListValue: for _, entry := range l.Values { validateLiteral(c, entry) } case *ast.Variable: for _, op := range c.ops { v := op.Vars.Get(l.Name) if v == nil { byOp := "" if op.Name.Name != "" { byOp = fmt.Sprintf(" by operation %q", op.Name.Name) } c.opErrs[op] = append(c.opErrs[op], &errors.QueryError{ Message: fmt.Sprintf("Variable %q is not defined%s.", "$"+l.Name, byOp), Locations: []errors.Location{l.Loc, op.Loc}, Rule: "NoUndefinedVariablesRule", }) continue } validateValueType(c, l, resolveType(c.context, v.Type)) c.usedVars[op][v] = struct{}{} } } } func validateValueType(c *opContext, v ast.Value, t ast.Type) (bool, string) { if v, ok := v.(*ast.Variable); ok { for _, op := range c.ops { if v2 := op.Vars.Get(v.Name); v2 != nil { t2, err := common.ResolveType(v2.Type, c.schema.Resolve) if _, ok := t2.(*ast.NonNull); !ok && v2.Default != nil { if _, ok := v2.Default.(*ast.NullValue); !ok { t2 = &ast.NonNull{OfType: t2} } } if err == nil && !typeCanBeUsedAs(t2, t) { c.addErrMultiLoc([]errors.Location{v2.Loc, v.Loc}, "VariablesInAllowedPositionRule", "Variable %q of type %q used in position expecting type %q.", "$"+v.Name, t2, t) } } } return true, "" } if nn, ok := t.(*ast.NonNull); ok { if isNull(v) { return false, fmt.Sprintf("Expected %q, found null.", t) } t = nn.OfType } if isNull(v) { return true, "" } switch t := t.(type) { case *ast.ScalarTypeDefinition, *ast.EnumTypeDefinition: if lit, ok := v.(*ast.PrimitiveValue); ok { if validateBasicLit(lit, t) { return true, "" } return false, fmt.Sprintf("Expected type %q, found %s.", t, v) } return true, "" case *ast.List: list, ok := v.(*ast.ListValue) if !ok { return validateValueType(c, v, t.OfType) // single value instead of list } for i, entry := range list.Values { if ok, reason := validateValueType(c, entry, t.OfType); !ok { return false, fmt.Sprintf("In element #%d: %s", i, reason) } } return true, "" case *ast.InputObject: v, ok := v.(*ast.ObjectValue) if !ok { return false, fmt.Sprintf("Expected %q, found not an object.", t) } for _, f := range v.Fields { name := f.Name.Name iv := t.Values.Get(name) if iv == nil { return false, fmt.Sprintf("In field %q: Unknown field.", name) } if ok, reason := validateValueType(c, f.Value, iv.Type); !ok { return false, fmt.Sprintf("In field %q: %s", name, reason) } } for _, iv := range t.Values { found := false for _, f := range v.Fields { if f.Name.Name == iv.Name.Name { found = true break } } if !found { if _, ok := iv.Type.(*ast.NonNull); ok && iv.Default == nil { return false, fmt.Sprintf("In field %q: Expected %q, found null.", iv.Name.Name, iv.Type) } } } return true, "" } return false, fmt.Sprintf("Expected type %q, found %s.", t, v) } func validateBasicLit(v *ast.PrimitiveValue, t ast.Type) bool { switch t := t.(type) { case *ast.ScalarTypeDefinition: switch t.Name { case "Int": if v.Type != scanner.Int { return false } return validateBuiltInScalar(v.Text, "Int") case "Float": return (v.Type == scanner.Int || v.Type == scanner.Float) && validateBuiltInScalar(v.Text, "Float") case "String": return v.Type == scanner.String && validateBuiltInScalar(v.Text, "String") case "Boolean": return v.Type == scanner.Ident && validateBuiltInScalar(v.Text, "Boolean") case "ID": return (v.Type == scanner.Int && validateBuiltInScalar(v.Text, "Int")) || (v.Type == scanner.String && validateBuiltInScalar(v.Text, "String")) default: //TODO: Type-check against expected type by Unmarshalling return true } case *ast.EnumTypeDefinition: if v.Type != scanner.Ident { return false } for _, option := range t.EnumValuesDefinition { if option.EnumValue == v.Text { return true } } return false } return false } func validateBuiltInScalar(v string, n string) bool { switch n { case "Int": f, err := strconv.ParseFloat(v, 64) if err != nil { return false } return f >= math.MinInt32 && f <= math.MaxInt32 case "Float": f, fe := strconv.ParseFloat(v, 64) return fe == nil && f <= math.MaxFloat64 case "String": vl := len(v) return vl >= 2 && v[0] == '"' && v[vl-1] == '"' case "Boolean": return v == "true" || v == "false" default: return false } } func canBeFragment(t ast.Type) bool { switch t.(type) { case *ast.ObjectTypeDefinition, *ast.InterfaceTypeDefinition, *ast.Union: return true default: return false } } func canBeInput(t ast.Type) bool { switch t := t.(type) { case *ast.InputObject, *ast.ScalarTypeDefinition, *ast.EnumTypeDefinition: return true case *ast.List: return canBeInput(t.OfType) case *ast.NonNull: return canBeInput(t.OfType) case nil: return true default: return false } } func hasSubfields(t ast.Type) bool { switch t := t.(type) { case *ast.ObjectTypeDefinition, *ast.InterfaceTypeDefinition, *ast.Union: return true case *ast.List: return hasSubfields(t.OfType) case *ast.NonNull: return hasSubfields(t.OfType) default: return false } } func isLeaf(t ast.Type) bool { switch t.(type) { case *ast.ScalarTypeDefinition, *ast.EnumTypeDefinition: return true default: return false } } func isNull(lit interface{}) bool { _, ok := lit.(*ast.NullValue) return ok } func typesCompatible(a, b ast.Type) bool { al, aIsList := a.(*ast.List) bl, bIsList := b.(*ast.List) if aIsList || bIsList { return aIsList && bIsList && typesCompatible(al.OfType, bl.OfType) } ann, aIsNN := a.(*ast.NonNull) bnn, bIsNN := b.(*ast.NonNull) if aIsNN || bIsNN { return aIsNN && bIsNN && typesCompatible(ann.OfType, bnn.OfType) } if isLeaf(a) || isLeaf(b) { return a == b } return true } func typeCanBeUsedAs(t, as ast.Type) bool { nnT, okT := t.(*ast.NonNull) if okT { t = nnT.OfType } nnAs, okAs := as.(*ast.NonNull) if okAs { as = nnAs.OfType if !okT { return false // nullable can not be used as non-null } } if t == as { return true } if lT, ok := t.(*ast.List); ok { if lAs, ok := as.(*ast.List); ok { return typeCanBeUsedAs(lT.OfType, lAs.OfType) } } return false } graphql-go-1.6.0/internal/validation/validation_test.go000066400000000000000000000070521475633407000232620ustar00rootroot00000000000000package validation_test import ( "os" "reflect" "sort" "testing" "encoding/json" "github.com/graph-gophers/graphql-go/ast" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/internal/query" "github.com/graph-gophers/graphql-go/internal/schema" "github.com/graph-gophers/graphql-go/internal/validation" ) type Schema struct { ID string SDL string } type Test struct { Name string Rule string Schema string Query string Vars map[string]interface{} Errors []*errors.QueryError } func TestValidate(t *testing.T) { skip := map[string]struct{}{ // Minor issue: reporting extra error under PossibleFragmentSpreadsRule which is not intended "Validate: Possible fragment spreads/ignores incorrect type (caught by FragmentsOnCompositeTypesRule)": {}, // graphql-js test case parses SDL as if it was a query here, which fails since we only accept a query "Validate: Directives Are Unique Per Location/unknown directives must be ignored": {}, // The meta schema always includes the standard types, so this isn't applicable "Validate: Known type names/references to standard scalars that are missing in schema": {}, // Ignore tests using experimental @stream "Validate: Overlapping fields can be merged/different stream directive label": {}, "Validate: Overlapping fields can be merged/different stream directive initialCount": {}, "Validate: Overlapping fields can be merged/different stream directive first missing args": {}, "Validate: Overlapping fields can be merged/different stream directive second missing args": {}, "Validate: Overlapping fields can be merged/different stream directive extra argument": {}, "Validate: Overlapping fields can be merged/mix of stream and no stream": {}, } f, err := os.Open("testdata/tests.json") if err != nil { t.Fatal(err) } var testData struct { Schemas []Schema Tests []*Test } if err := json.NewDecoder(f).Decode(&testData); err != nil { t.Fatal(err) } schemas := make(map[string]*ast.Schema, len(testData.Schemas)) for _, sc := range testData.Schemas { s := schema.New() s.Directives["oneOf"] = &ast.DirectiveDefinition{ // graphql-js includes support for @oneOf, currently in RFC // This is not available in graphql-go, nor is it expected to be unless the RFC is accepted // See https://github.com/graphql/graphql-js/pull/3513 & https://github.com/graphql/graphql-spec/pull/825/ Name: "oneOf", Desc: "Indicates exactly one field must be supplied and this field must not be `null`.", Locations: []string{"INPUT_OBJECT"}, } err := schema.Parse(s, sc.SDL, false) if err != nil { t.Fatal(err) } schemas[sc.ID] = s } for _, test := range testData.Tests { t.Run(test.Name, func(t *testing.T) { if _, ok := skip[test.Name]; ok { t.Skip("Test ignored") } d, err := query.Parse(test.Query) if err != nil { t.Fatalf("failed to parse query: %s", err) } errs := validation.Validate(schemas[test.Schema], d, test.Vars, 0) got := []*errors.QueryError{} for _, err := range errs { if err.Rule == test.Rule { err.Rule = "" got = append(got, err) } } sortLocations(test.Errors) sortLocations(got) if !reflect.DeepEqual(test.Errors, got) { t.Errorf("wrong errors for rule %q\nexpected: %v\ngot: %v", test.Rule, test.Errors, got) } }) } } func sortLocations(errs []*errors.QueryError) { for _, err := range errs { locs := err.Locations sort.Slice(locs, func(i, j int) bool { return locs[i].Before(locs[j]) }) } } graphql-go-1.6.0/introspection.go000066400000000000000000000041721475633407000170230ustar00rootroot00000000000000package graphql import ( "context" "encoding/json" "github.com/graph-gophers/graphql-go/internal/exec/resolvable" "github.com/graph-gophers/graphql-go/introspection" ) // Inspect allows inspection of the given schema. func (s *Schema) Inspect() *introspection.Schema { return introspection.WrapSchema(s.schema) } // ToJSON encodes the schema in a JSON format used by tools like Relay. func (s *Schema) ToJSON() ([]byte, error) { result := s.exec(context.Background(), introspectionQuery, "", nil, &resolvable.Schema{ Meta: s.res.Meta, Query: &resolvable.Object{}, Schema: *s.schema, }) if len(result.Errors) != 0 { panic(result.Errors[0]) } return json.MarshalIndent(result.Data, "", "\t") } var introspectionQuery = ` query { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } } } ` graphql-go-1.6.0/introspection/000077500000000000000000000000001475633407000164705ustar00rootroot00000000000000graphql-go-1.6.0/introspection/introspection.go000066400000000000000000000143041475633407000217210ustar00rootroot00000000000000package introspection import ( "sort" "github.com/graph-gophers/graphql-go/ast" ) type Schema struct { schema *ast.Schema } // WrapSchema is only used internally. func WrapSchema(schema *ast.Schema) *Schema { return &Schema{schema} } func (r *Schema) Types() []*Type { var names []string for name := range r.schema.Types { names = append(names, name) } sort.Strings(names) l := make([]*Type, len(names)) for i, name := range names { l[i] = &Type{r.schema.Types[name]} } return l } func (r *Schema) Directives() []*Directive { var names []string for name := range r.schema.Directives { names = append(names, name) } sort.Strings(names) l := make([]*Directive, len(names)) for i, name := range names { l[i] = &Directive{r.schema.Directives[name]} } return l } func (r *Schema) QueryType() *Type { t, ok := r.schema.RootOperationTypes["query"] if !ok { return nil } return &Type{t} } func (r *Schema) MutationType() *Type { t, ok := r.schema.RootOperationTypes["mutation"] if !ok { return nil } return &Type{t} } func (r *Schema) SubscriptionType() *Type { t, ok := r.schema.RootOperationTypes["subscription"] if !ok { return nil } return &Type{t} } type Type struct { typ ast.Type } // WrapType is only used internally. func WrapType(typ ast.Type) *Type { return &Type{typ} } func (r *Type) Kind() string { return r.typ.Kind() } func (r *Type) Name() *string { if named, ok := r.typ.(ast.NamedType); ok { name := named.TypeName() return &name } return nil } func (r *Type) Description() *string { if named, ok := r.typ.(ast.NamedType); ok { desc := named.Description() if desc == "" { return nil } return &desc } return nil } func (r *Type) Fields(args *struct{ IncludeDeprecated bool }) *[]*Field { var fields ast.FieldsDefinition switch t := r.typ.(type) { case *ast.ObjectTypeDefinition: fields = t.Fields case *ast.InterfaceTypeDefinition: fields = t.Fields default: return nil } var l []*Field for _, f := range fields { if d := f.Directives.Get("deprecated"); d == nil || args.IncludeDeprecated { l = append(l, &Field{field: f}) } } return &l } func (r *Type) Interfaces() *[]*Type { t, ok := r.typ.(*ast.ObjectTypeDefinition) if !ok { return nil } l := make([]*Type, len(t.Interfaces)) for i, intf := range t.Interfaces { l[i] = &Type{intf} } return &l } func (r *Type) PossibleTypes() *[]*Type { var possibleTypes []*ast.ObjectTypeDefinition switch t := r.typ.(type) { case *ast.InterfaceTypeDefinition: possibleTypes = t.PossibleTypes case *ast.Union: possibleTypes = t.UnionMemberTypes default: return nil } l := make([]*Type, len(possibleTypes)) for i, intf := range possibleTypes { l[i] = &Type{intf} } return &l } func (r *Type) EnumValues(args *struct{ IncludeDeprecated bool }) *[]*EnumValue { t, ok := r.typ.(*ast.EnumTypeDefinition) if !ok { return nil } var l []*EnumValue for _, v := range t.EnumValuesDefinition { if d := v.Directives.Get("deprecated"); d == nil || args.IncludeDeprecated { l = append(l, &EnumValue{v}) } } return &l } func (r *Type) InputFields() *[]*InputValue { t, ok := r.typ.(*ast.InputObject) if !ok { return nil } l := make([]*InputValue, len(t.Values)) for i, v := range t.Values { l[i] = &InputValue{v} } return &l } func (r *Type) OfType() *Type { switch t := r.typ.(type) { case *ast.List: return &Type{t.OfType} case *ast.NonNull: return &Type{t.OfType} default: return nil } } func (r *Type) SpecifiedByURL() *string { switch t := r.typ.(type) { case *ast.ScalarTypeDefinition: if d := t.Directives.Get("specifiedBy"); d != nil { arg := d.Arguments.MustGet("url") url := arg.Deserialize(nil).(string) return &url } default: return nil } return nil } type Field struct { field *ast.FieldDefinition } func (r *Field) Name() string { return r.field.Name } func (r *Field) Description() *string { if r.field.Desc == "" { return nil } return &r.field.Desc } func (r *Field) Args() []*InputValue { l := make([]*InputValue, len(r.field.Arguments)) for i, v := range r.field.Arguments { l[i] = &InputValue{v} } return l } func (r *Field) Type() *Type { return &Type{r.field.Type} } func (r *Field) IsDeprecated() bool { return r.field.Directives.Get("deprecated") != nil } func (r *Field) DeprecationReason() *string { d := r.field.Directives.Get("deprecated") if d == nil { return nil } reason := d.Arguments.MustGet("reason").Deserialize(nil).(string) return &reason } type InputValue struct { value *ast.InputValueDefinition } func (r *InputValue) Name() string { return r.value.Name.Name } func (r *InputValue) Description() *string { if r.value.Desc == "" { return nil } return &r.value.Desc } func (r *InputValue) Type() *Type { return &Type{r.value.Type} } func (r *InputValue) DefaultValue() *string { if r.value.Default == nil { return nil } s := r.value.Default.String() return &s } func (r *InputValue) IsDeprecated() bool { return r.value.Directives.Get("deprecated") != nil } func (r *InputValue) DeprecationReason() *string { d := r.value.Directives.Get("deprecated") if d == nil { return nil } reason := d.Arguments.MustGet("reason").Deserialize(nil).(string) return &reason } type EnumValue struct { value *ast.EnumValueDefinition } func (r *EnumValue) Name() string { return r.value.EnumValue } func (r *EnumValue) Description() *string { if r.value.Desc == "" { return nil } return &r.value.Desc } func (r *EnumValue) IsDeprecated() bool { return r.value.Directives.Get("deprecated") != nil } func (r *EnumValue) DeprecationReason() *string { d := r.value.Directives.Get("deprecated") if d == nil { return nil } reason := d.Arguments.MustGet("reason").Deserialize(nil).(string) return &reason } type Directive struct { directive *ast.DirectiveDefinition } func (r *Directive) Name() string { return r.directive.Name } func (r *Directive) Description() *string { if r.directive.Desc == "" { return nil } return &r.directive.Desc } func (r *Directive) Locations() []string { return r.directive.Locations } func (r *Directive) Args() []*InputValue { l := make([]*InputValue, len(r.directive.Arguments)) for i, v := range r.directive.Arguments { l[i] = &InputValue{v} } return l } graphql-go-1.6.0/introspection_test.go000066400000000000000000000036411475633407000200620ustar00rootroot00000000000000package graphql_test import ( "bytes" "encoding/json" "os" "testing" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/social" "github.com/graph-gophers/graphql-go/example/starwars" ) func TestSchema_ToJSON(t *testing.T) { t.Parallel() type args struct { Schema *graphql.Schema } type want struct { JSON []byte } testTable := []struct { Name string Args args Want want }{ { Name: "Social Schema", Args: args{Schema: graphql.MustParseSchema(social.Schema, &social.Resolver{}, graphql.UseFieldResolvers())}, Want: want{JSON: mustReadFile("example/social/introspect.json")}, }, { Name: "Star Wars Schema", Args: args{Schema: graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{})}, Want: want{JSON: mustReadFile("example/starwars/introspect.json")}, }, { Name: "Star Wars Schema without Resolver", Args: args{Schema: graphql.MustParseSchema(starwars.Schema, nil)}, Want: want{JSON: mustReadFile("example/starwars/introspect.json")}, }, } for _, tt := range testTable { t.Run(tt.Name, func(t *testing.T) { j, err := tt.Args.Schema.ToJSON() if err != nil { t.Fatalf("invalid schema %s", err.Error()) } // Verify JSON to avoid red herring errors. got, err := formatJSON(j) if err != nil { t.Fatalf("got: invalid JSON: %s", err) } want, err := formatJSON(tt.Want.JSON) if err != nil { t.Fatalf("want: invalid JSON: %s", err) } if !bytes.Equal(got, want) { t.Logf("got: %s", got) t.Logf("want: %s", want) t.Fail() } }) } } func formatJSON(data []byte) ([]byte, error) { var v interface{} if err := json.Unmarshal(data, &v); err != nil { return nil, err } formatted, err := json.Marshal(v) if err != nil { return nil, err } return formatted, nil } func mustReadFile(filename string) []byte { b, err := os.ReadFile(filename) if err != nil { panic(err) } return b } graphql-go-1.6.0/log/000077500000000000000000000000001475633407000143515ustar00rootroot00000000000000graphql-go-1.6.0/log/log.go000066400000000000000000000012731475633407000154640ustar00rootroot00000000000000package log import ( "context" "log" "runtime" ) // Logger is the interface used to log panics that occur during query execution. It is settable via graphql.ParseSchema type Logger interface { LogPanic(ctx context.Context, value interface{}) } // DefaultLogger is the default logger used to log panics that occur during query execution type DefaultLogger struct{} // LogPanic is used to log recovered panic values that occur during query execution func (l *DefaultLogger) LogPanic(ctx context.Context, value interface{}) { const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] log.Printf("graphql: panic occurred: %v\n%s\ncontext: %v", value, buf, ctx) } graphql-go-1.6.0/nullable_types.go000066400000000000000000000076311475633407000171500ustar00rootroot00000000000000package graphql import ( "fmt" "math" ) // NullID is an ID that can be null. Use it in input structs to // differentiate a value explicitly set to null from an omitted value. // When the value is defined (either null or a value) Set is true. type NullID struct { Value *ID Set bool } func (NullID) ImplementsGraphQLType(name string) bool { return name == "ID" } func (s *NullID) UnmarshalGraphQL(input interface{}) error { s.Set = true if input == nil { return nil } s.Value = new(ID) return s.Value.UnmarshalGraphQL(input) } func (s *NullID) Nullable() {} // NullString is a string that can be null. Use it in input structs to // differentiate a value explicitly set to null from an omitted value. // When the value is defined (either null or a value) Set is true. type NullString struct { Value *string Set bool } func (NullString) ImplementsGraphQLType(name string) bool { return name == "String" } func (s *NullString) UnmarshalGraphQL(input interface{}) error { s.Set = true if input == nil { return nil } switch v := input.(type) { case string: s.Value = &v return nil default: return fmt.Errorf("wrong type for String: %T", v) } } func (s *NullString) Nullable() {} // NullBool is a boolean that can be null. Use it in input structs to // differentiate a value explicitly set to null from an omitted value. // When the value is defined (either null or a value) Set is true. type NullBool struct { Value *bool Set bool } func (NullBool) ImplementsGraphQLType(name string) bool { return name == "Boolean" } func (s *NullBool) UnmarshalGraphQL(input interface{}) error { s.Set = true if input == nil { return nil } switch v := input.(type) { case bool: s.Value = &v return nil default: return fmt.Errorf("wrong type for Boolean: %T", v) } } func (s *NullBool) Nullable() {} // NullInt is an int that can be null. Use it in input structs to // differentiate a value explicitly set to null from an omitted value. // When the value is defined (either null or a value) Set is true. type NullInt struct { Value *int32 Set bool } func (NullInt) ImplementsGraphQLType(name string) bool { return name == "Int" } func (s *NullInt) UnmarshalGraphQL(input interface{}) error { s.Set = true if input == nil { return nil } switch v := input.(type) { case int32: s.Value = &v return nil case float64: coerced := int32(v) if v < math.MinInt32 || v > math.MaxInt32 || float64(coerced) != v { return fmt.Errorf("not a 32-bit integer") } s.Value = &coerced return nil default: return fmt.Errorf("wrong type for Int: %T", v) } } func (s *NullInt) Nullable() {} // NullFloat is a float that can be null. Use it in input structs to // differentiate a value explicitly set to null from an omitted value. // When the value is defined (either null or a value) Set is true. type NullFloat struct { Value *float64 Set bool } func (NullFloat) ImplementsGraphQLType(name string) bool { return name == "Float" } func (s *NullFloat) UnmarshalGraphQL(input interface{}) error { s.Set = true if input == nil { return nil } switch v := input.(type) { case float64: s.Value = &v return nil case int32: coerced := float64(v) s.Value = &coerced return nil case int: coerced := float64(v) s.Value = &coerced return nil default: return fmt.Errorf("wrong type for Float: %T", v) } } func (s *NullFloat) Nullable() {} // NullTime is a time value that can be null. Use it in input structs to // differentiate a value explicitly set to null from an omitted value. // When the value is defined (either null or a value) Set is true. type NullTime struct { Value *Time Set bool } func (NullTime) ImplementsGraphQLType(name string) bool { return name == "Time" } func (s *NullTime) UnmarshalGraphQL(input interface{}) error { s.Set = true if input == nil { return nil } s.Value = new(Time) return s.Value.UnmarshalGraphQL(input) } func (s *NullTime) Nullable() {} graphql-go-1.6.0/nullable_types_test.go000066400000000000000000000130051475633407000201770ustar00rootroot00000000000000package graphql_test import ( "math" "testing" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/decode" ) func TestNullID_ImplementsUnmarshaler(t *testing.T) { defer func() { if err := recover(); err != nil { t.Error(err) } }() // assert *NullID implements decode.Unmarshaler interface var _ decode.Unmarshaler = (*graphql.NullID)(nil) } func TestNullID_UnmarshalGraphQL(t *testing.T) { type args struct { input interface{} } good := graphql.ID("1234") ref := graphql.NullID{ Value: &good, Set: true, } t.Run("invalid", func(t *testing.T) { tests := []struct { name string args args wantErr string }{ { name: "boolean", args: args{input: true}, wantErr: "wrong type for ID: bool", }, { name: "int", args: args{input: 1}, wantErr: "wrong type for ID: int", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gt := &graphql.NullID{} if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { if err.Error() != tt.wantErr { t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr) } return } t.Error("UnmarshalGraphQL() expected error not raised") }) } }) tests := []struct { name string args args wantEq graphql.NullID }{ { name: "string", args: args{ input: string(good), }, wantEq: ref, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gt := new(graphql.NullID) if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { t.Errorf("UnmarshalGraphQL() error = %v", err) return } if *gt.Value != *tt.wantEq.Value { t.Errorf("UnmarshalGraphQL() got = %v, want = %v", *gt.Value, *tt.wantEq.Value) } }) } } func TestNullInt_ImplementsUnmarshaler(t *testing.T) { defer func() { if err := recover(); err != nil { t.Error(err) } }() // assert *NullInt implements decode.Unmarshaler interface var _ decode.Unmarshaler = (*graphql.NullInt)(nil) } func TestNullInt_UnmarshalGraphQL(t *testing.T) { type args struct { input interface{} } a := float64(math.MaxInt32 + 1) b := float64(math.MinInt32 - 1) c := 1234.6 good := int32(1234) ref := graphql.NullInt{ Value: &good, Set: true, } t.Run("invalid", func(t *testing.T) { tests := []struct { name string args args wantErr string }{ { name: "boolean", args: args{input: true}, wantErr: "wrong type for Int: bool", }, { name: "int32 out of range (+)", args: args{ input: a, }, wantErr: "not a 32-bit integer", }, { name: "int32 out of range (-)", args: args{ input: b, }, wantErr: "not a 32-bit integer", }, { name: "non-integer", args: args{ input: c, }, wantErr: "not a 32-bit integer", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gt := &graphql.NullInt{} if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { if err.Error() != tt.wantErr { t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr) } return } t.Error("UnmarshalGraphQL() expected error not raised") }) } }) tests := []struct { name string args args wantEq graphql.NullInt }{ { name: "int32", args: args{ input: good, }, wantEq: ref, }, { name: "float64", args: args{ input: float64(good), }, wantEq: ref, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gt := new(graphql.NullInt) if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { t.Errorf("UnmarshalGraphQL() error = %v", err) return } if *gt.Value != *tt.wantEq.Value { t.Errorf("UnmarshalGraphQL() got = %v, want = %v", *gt.Value, *tt.wantEq.Value) } }) } } func TestNullFloat_ImplementsUnmarshaler(t *testing.T) { defer func() { if err := recover(); err != nil { t.Error(err) } }() // assert *NullFloat implements decode.Unmarshaler interface var _ decode.Unmarshaler = (*graphql.NullFloat)(nil) } func TestNullFloat_UnmarshalGraphQL(t *testing.T) { type args struct { input interface{} } good := float64(1234) ref := graphql.NullFloat{ Value: &good, Set: true, } t.Run("invalid", func(t *testing.T) { tests := []struct { name string args args wantErr string }{ { name: "boolean", args: args{input: true}, wantErr: "wrong type for Float: bool", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gt := new(graphql.NullFloat) if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { if err.Error() != tt.wantErr { t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr) } return } t.Error("UnmarshalGraphQL() expected error not raised") }) } }) tests := []struct { name string args args wantEq graphql.NullFloat }{ { name: "int", args: args{ input: int(good), }, wantEq: ref, }, { name: "int32", args: args{ input: int32(good), }, wantEq: ref, }, { name: "float64", args: args{ input: good, }, wantEq: ref, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gt := new(graphql.NullFloat) if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { t.Errorf("UnmarshalGraphQL() error = %v", err) return } if *gt.Value != *tt.wantEq.Value { t.Errorf("UnmarshalGraphQL() got = %v, want = %v", *gt.Value, *tt.wantEq.Value) } }) } } graphql-go-1.6.0/relay/000077500000000000000000000000001475633407000147045ustar00rootroot00000000000000graphql-go-1.6.0/relay/relay.go000066400000000000000000000031711475633407000163510ustar00rootroot00000000000000package relay import ( "encoding/base64" "encoding/json" "errors" "fmt" "net/http" "strings" graphql "github.com/graph-gophers/graphql-go" ) func MarshalID(kind string, spec interface{}) graphql.ID { d, err := json.Marshal(spec) if err != nil { panic(fmt.Errorf("relay.MarshalID: %s", err)) } return graphql.ID(base64.URLEncoding.EncodeToString(append([]byte(kind+":"), d...))) } func UnmarshalKind(id graphql.ID) string { s, err := base64.URLEncoding.DecodeString(string(id)) if err != nil { return "" } i := strings.IndexByte(string(s), ':') if i == -1 { return "" } return string(s[:i]) } func UnmarshalSpec(id graphql.ID, v interface{}) error { s, err := base64.URLEncoding.DecodeString(string(id)) if err != nil { return err } i := strings.IndexByte(string(s), ':') if i == -1 { return errors.New("invalid graphql.ID") } return json.Unmarshal(s[i+1:], v) } type Handler struct { Schema *graphql.Schema } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { var params struct { Query string `json:"query"` OperationName string `json:"operationName"` Variables map[string]interface{} `json:"variables"` } if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } response := h.Schema.Exec(r.Context(), params.Query, params.OperationName, params.Variables) responseJSON, err := json.Marshal(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(responseJSON) } graphql-go-1.6.0/relay/relay_test.go000066400000000000000000000020751475633407000174120ustar00rootroot00000000000000package relay_test import ( "net/http/httptest" "strings" "testing" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" "github.com/graph-gophers/graphql-go/relay" ) var starwarsSchema = graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{}) func TestServeHTTP(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("POST", "/some/path/here", strings.NewReader(`{"query":"{ hero { name } }", "operationName":"", "variables": null}`)) h := relay.Handler{Schema: starwarsSchema} h.ServeHTTP(w, r) if w.Code != 200 { t.Fatalf("Expected status code 200, got %d.", w.Code) } contentType := w.Header().Get("Content-Type") if contentType != "application/json" { t.Fatalf("Invalid content-type. Expected [application/json], but instead got [%s]", contentType) } expectedResponse := `{"data":{"hero":{"name":"R2-D2"}}}` actualResponse := w.Body.String() if expectedResponse != actualResponse { t.Fatalf("Invalid response. Expected [%s], but instead got [%s]", expectedResponse, actualResponse) } } graphql-go-1.6.0/scripts/000077500000000000000000000000001475633407000152575ustar00rootroot00000000000000graphql-go-1.6.0/scripts/golangci_install.sh000077500000000000000000000247621475633407000211420ustar00rootroot00000000000000#!/bin/sh set -e # Code generated by godownloader. DO NOT EDIT. # usage() { this=$1 cat </dev/null } echoerr() { echo "$@" 1>&2 } log_prefix() { echo "$0" } _logp=6 log_set_priority() { _logp="$1" } log_priority() { if test -z "$1"; then echo "$_logp" return fi [ "$1" -le "$_logp" ] } log_tag() { case $1 in 0) echo "emerg" ;; 1) echo "alert" ;; 2) echo "crit" ;; 3) echo "err" ;; 4) echo "warning" ;; 5) echo "notice" ;; 6) echo "info" ;; 7) echo "debug" ;; *) echo "$1" ;; esac } log_debug() { log_priority 7 || return 0 echoerr "$(log_prefix)" "$(log_tag 7)" "$@" } log_info() { log_priority 6 || return 0 echoerr "$(log_prefix)" "$(log_tag 6)" "$@" } log_err() { log_priority 3 || return 0 echoerr "$(log_prefix)" "$(log_tag 3)" "$@" } log_crit() { log_priority 2 || return 0 echoerr "$(log_prefix)" "$(log_tag 2)" "$@" } uname_os() { os=$(uname -s | tr '[:upper:]' '[:lower:]') case "$os" in cygwin_nt*) os="windows" ;; mingw*) os="windows" ;; msys_nt*) os="windows" ;; esac echo "$os" } uname_arch() { arch=$(uname -m) case $arch in x86_64) arch="amd64" ;; x86) arch="386" ;; i686) arch="386" ;; i386) arch="386" ;; aarch64) arch="arm64" ;; armv5*) arch="armv5" ;; armv6*) arch="armv6" ;; armv7*) arch="armv7" ;; esac echo ${arch} } uname_os_check() { os=$(uname_os) case "$os" in darwin) return 0 ;; dragonfly) return 0 ;; freebsd) return 0 ;; linux) return 0 ;; android) return 0 ;; nacl) return 0 ;; netbsd) return 0 ;; openbsd) return 0 ;; plan9) return 0 ;; solaris) return 0 ;; windows) return 0 ;; esac log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" return 1 } uname_arch_check() { arch=$(uname_arch) case "$arch" in 386) return 0 ;; amd64) return 0 ;; arm64) return 0 ;; armv5) return 0 ;; armv6) return 0 ;; armv7) return 0 ;; ppc64) return 0 ;; ppc64le) return 0 ;; mips) return 0 ;; mipsle) return 0 ;; mips64) return 0 ;; mips64le) return 0 ;; s390x) return 0 ;; amd64p32) return 0 ;; esac log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" return 1 } untar() { tarball=$1 case "${tarball}" in *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;; *.tar) tar --no-same-owner -xf "${tarball}" ;; *.zip) unzip "${tarball}" ;; *) log_err "untar unknown archive format for ${tarball}" return 1 ;; esac } http_download_curl() { local_file=$1 source_url=$2 header=$3 if [ -z "$header" ]; then code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") else code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") fi if [ "$code" != "200" ]; then log_debug "http_download_curl received HTTP status $code" return 1 fi return 0 } http_download_wget() { local_file=$1 source_url=$2 header=$3 if [ -z "$header" ]; then wget -q -O "$local_file" "$source_url" else wget -q --header "$header" -O "$local_file" "$source_url" fi } http_download() { log_debug "http_download $2" if is_command curl; then http_download_curl "$@" return elif is_command wget; then http_download_wget "$@" return fi log_crit "http_download unable to find wget or curl" return 1 } http_copy() { tmp=$(mktemp) http_download "${tmp}" "$1" "$2" || return 1 body=$(cat "$tmp") rm -f "${tmp}" echo "$body" } github_release() { owner_repo=$1 version=$2 test -z "$version" && version="latest" giturl="https://github.com/${owner_repo}/releases/${version}" json=$(http_copy "$giturl" "Accept:application/json") test -z "$json" && return 1 version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') test -z "$version" && return 1 echo "$version" } hash_sha256() { TARGET=${1:-/dev/stdin} if is_command gsha256sum; then hash=$(gsha256sum "$TARGET") || return 1 echo "$hash" | cut -d ' ' -f 1 elif is_command sha256sum; then hash=$(sha256sum "$TARGET") || return 1 echo "$hash" | cut -d ' ' -f 1 elif is_command shasum; then hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 echo "$hash" | cut -d ' ' -f 1 elif is_command openssl; then hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 echo "$hash" | cut -d ' ' -f a else log_crit "hash_sha256 unable to find command to compute sha-256 hash" return 1 fi } hash_sha256_verify() { TARGET=$1 checksums=$2 if [ -z "$checksums" ]; then log_err "hash_sha256_verify checksum file not specified in arg2" return 1 fi BASENAME=${TARGET##*/} want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) if [ -z "$want" ]; then log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" return 1 fi got=$(hash_sha256 "$TARGET") if [ "$want" != "$got" ]; then log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" return 1 fi } cat /dev/null <= 1e10 { sec := input / 1e9 nsec := input - (sec * 1e9) t.Time = time.Unix(sec, nsec) } else { t.Time = time.Unix(input, 0) } return nil case float64: t.Time = time.Unix(int64(input), 0) return nil default: return fmt.Errorf("wrong type for Time: %T", input) } } // MarshalJSON is a custom marshaler for Time // // This function will be called whenever you // query for fields that use the Time type func (t Time) MarshalJSON() ([]byte, error) { return json.Marshal(t.Time) } graphql-go-1.6.0/time_test.go000066400000000000000000000063501475633407000161200ustar00rootroot00000000000000package graphql_test import ( "bytes" "encoding/json" "testing" "time" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/decode" ) func TestTime_ImplementsUnmarshaler(t *testing.T) { defer func() { if err := recover(); err != nil { t.Error(err) } }() // assert *Time implements decode.Unmarshaler interface var _ decode.Unmarshaler = (*graphql.Time)(nil) } func TestTime_ImplementsGraphQLType(t *testing.T) { gt := &graphql.Time{} if gt.ImplementsGraphQLType("foobar") { t.Error("Type *Time must not claim to implement GraphQL type 'foobar'") } if !gt.ImplementsGraphQLType("Time") { t.Error("Failed asserting *Time implements GraphQL type Time") } } func TestTime_MarshalJSON(t *testing.T) { var err error var b1, b2 []byte ref := time.Date(2021, time.April, 20, 12, 3, 23, 551476231, time.UTC) if b1, err = json.Marshal(ref); err != nil { t.Error(err) return } if b2, err = json.Marshal(graphql.Time{Time: ref}); err != nil { t.Errorf("MarshalJSON() error = %v", err) return } if !bytes.Equal(b1, b2) { t.Errorf("MarshalJSON() got = %s, want = %s", b2, b1) } } func TestTime_UnmarshalGraphQL(t *testing.T) { type args struct { input interface{} } ref := time.Date(2021, time.April, 20, 12, 3, 23, 551476231, time.UTC) refZeroNano := time.Unix(ref.Unix(), 0) t.Run("invalid", func(t *testing.T) { tests := []struct { name string args args wantErr string }{ { name: "boolean", args: args{input: true}, wantErr: "wrong type for Time: bool", }, { name: "invalid format", args: args{input: ref.Format(time.ANSIC)}, wantErr: `parsing time "Tue Apr 20 12:03:23 2021" as "2006-01-02T15:04:05Z07:00": cannot parse "Tue Apr 20 12:03:23 2021" as "2006"`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gt := &graphql.Time{} if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { if err.Error() != tt.wantErr { t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr) } return } t.Error("UnmarshalGraphQL() expected error not raised") }) } }) tests := []struct { name string args args wantEq time.Time }{ { name: "time.Time", args: args{ input: ref, }, wantEq: ref, }, { name: "string", args: args{ input: ref.Format(time.RFC3339), }, wantEq: refZeroNano, }, { name: "bytes", args: args{ input: []byte(ref.Format(time.RFC3339)), }, wantEq: refZeroNano, }, { name: "int32", args: args{ input: int32(ref.Unix()), }, wantEq: refZeroNano, }, { name: "int64", args: args{ input: ref.Unix(), }, wantEq: refZeroNano, }, { name: "int64-nano", args: args{ input: ref.UnixNano(), }, wantEq: ref, }, { name: "float64", args: args{ input: float64(ref.Unix()), }, wantEq: refZeroNano, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gt := &graphql.Time{} if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { t.Errorf("UnmarshalGraphQL() error = %v", err) return } if !gt.Equal(tt.wantEq) { t.Errorf("UnmarshalGraphQL() got = %v, want = %v", gt, tt.wantEq) } }) } } graphql-go-1.6.0/trace/000077500000000000000000000000001475633407000146665ustar00rootroot00000000000000graphql-go-1.6.0/trace/noop/000077500000000000000000000000001475633407000156415ustar00rootroot00000000000000graphql-go-1.6.0/trace/noop/trace.go000066400000000000000000000015361475633407000172730ustar00rootroot00000000000000// Package noop defines a no-op tracer implementation. package noop import ( "context" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/introspection" ) // Tracer is a no-op tracer that does nothing. type Tracer struct{} func (Tracer) TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*errors.QueryError)) { return ctx, func(errs []*errors.QueryError) {} } func (Tracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*errors.QueryError)) { return ctx, func(err *errors.QueryError) {} } func (Tracer) TraceValidation(context.Context) func([]*errors.QueryError) { return func(errs []*errors.QueryError) {} } graphql-go-1.6.0/trace/noop/trace_test.go000066400000000000000000000010311475633407000203200ustar00rootroot00000000000000package noop_test import ( "testing" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" "github.com/graph-gophers/graphql-go/trace/noop" "github.com/graph-gophers/graphql-go/trace/tracer" ) func TestInterfaceImplementation(t *testing.T) { var _ tracer.ValidationTracer = &noop.Tracer{} var _ tracer.Tracer = &noop.Tracer{} } func TestTracerOption(t *testing.T) { _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(noop.Tracer{})) if err != nil { t.Fatal(err) } } graphql-go-1.6.0/trace/opentracing/000077500000000000000000000000001475633407000171775ustar00rootroot00000000000000graphql-go-1.6.0/trace/opentracing/trace.go000066400000000000000000000044031475633407000206250ustar00rootroot00000000000000// The opentracing package provides tracing functionality using OpenTracing. package opentracing import ( "context" "fmt" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/introspection" opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/log" ) // Tracer implements the graphql-go Tracer interface and creates OpenTracing spans. type Tracer struct{} func (Tracer) TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*errors.QueryError)) { span, spanCtx := opentracing.StartSpanFromContext(ctx, "GraphQL request") span.SetTag("graphql.query", queryString) if operationName != "" { span.SetTag("graphql.operationName", operationName) } if len(variables) != 0 { span.LogFields(log.Object("graphql.variables", variables)) } return spanCtx, func(errs []*errors.QueryError) { if len(errs) > 0 { msg := errs[0].Error() if len(errs) > 1 { msg += fmt.Sprintf(" (and %d more errors)", len(errs)-1) } ext.Error.Set(span, true) span.SetTag("graphql.error", msg) } span.Finish() } } func (Tracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*errors.QueryError)) { if trivial { return ctx, noop } span, spanCtx := opentracing.StartSpanFromContext(ctx, label) span.SetTag("graphql.type", typeName) span.SetTag("graphql.field", fieldName) for name, value := range args { span.SetTag("graphql.args."+name, value) } return spanCtx, func(err *errors.QueryError) { if err != nil { ext.Error.Set(span, true) span.SetTag("graphql.error", err.Error()) } span.Finish() } } func (Tracer) TraceValidation(ctx context.Context) func([]*errors.QueryError) { span, _ := opentracing.StartSpanFromContext(ctx, "Validate Query") return func(errs []*errors.QueryError) { if len(errs) > 0 { msg := errs[0].Error() if len(errs) > 1 { msg += fmt.Sprintf(" (and %d more errors)", len(errs)-1) } ext.Error.Set(span, true) span.SetTag("graphql.error", msg) } span.Finish() } } func noop(*errors.QueryError) {} graphql-go-1.6.0/trace/opentracing/trace_test.go000066400000000000000000000010741475633407000216650ustar00rootroot00000000000000package opentracing_test import ( "testing" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" "github.com/graph-gophers/graphql-go/trace/opentracing" "github.com/graph-gophers/graphql-go/trace/tracer" ) func TestInterfaceImplementation(t *testing.T) { var _ tracer.ValidationTracer = &opentracing.Tracer{} var _ tracer.Tracer = &opentracing.Tracer{} } func TestTracerOption(t *testing.T) { _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(opentracing.Tracer{})) if err != nil { t.Fatal(err) } } graphql-go-1.6.0/trace/otel/000077500000000000000000000000001475633407000156315ustar00rootroot00000000000000graphql-go-1.6.0/trace/otel/trace.go000066400000000000000000000053351475633407000172640ustar00rootroot00000000000000// The otel package provides tracing functionality using OpenTelemetry. package otel import ( "context" "fmt" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" oteltrace "go.opentelemetry.io/otel/trace" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/introspection" ) // DefaultTracer creates a tracer using a default name. func DefaultTracer() *Tracer { return &Tracer{ Tracer: otel.Tracer("graphql-go"), } } // Tracer is an OpenTelemetry implementation for graphql-go. Set the Tracer // property to your tracer instance as required. type Tracer struct { Tracer oteltrace.Tracer } func (t *Tracer) TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*errors.QueryError)) { spanCtx, span := t.Tracer.Start(ctx, "GraphQL Request") var attributes []attribute.KeyValue attributes = append(attributes, attribute.String("graphql.query", queryString)) if operationName != "" { attributes = append(attributes, attribute.String("graphql.operationName", operationName)) } if len(variables) != 0 { attributes = append(attributes, attribute.String("graphql.variables", fmt.Sprintf("%v", variables))) } span.SetAttributes(attributes...) return spanCtx, func(errs []*errors.QueryError) { if len(errs) > 0 { msg := errs[0].Error() if len(errs) > 1 { msg += fmt.Sprintf(" (and %d more errors)", len(errs)-1) } span.SetStatus(codes.Error, msg) } span.End() } } func (t *Tracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*errors.QueryError)) { if trivial { return ctx, func(*errors.QueryError) {} } var attributes []attribute.KeyValue spanCtx, span := t.Tracer.Start(ctx, fmt.Sprintf("Field: %v", label)) attributes = append(attributes, attribute.String("graphql.type", typeName)) attributes = append(attributes, attribute.String("graphql.field", fieldName)) for name, value := range args { attributes = append(attributes, attribute.String("graphql.args."+name, fmt.Sprintf("%v", value))) } span.SetAttributes(attributes...) return spanCtx, func(err *errors.QueryError) { if err != nil { span.SetStatus(codes.Error, err.Error()) } span.End() } } func (t *Tracer) TraceValidation(ctx context.Context) func([]*errors.QueryError) { _, span := t.Tracer.Start(ctx, "GraphQL Validate") return func(errs []*errors.QueryError) { if len(errs) > 0 { msg := errs[0].Error() if len(errs) > 1 { msg += fmt.Sprintf(" (and %d more errors)", len(errs)-1) } span.SetStatus(codes.Error, msg) } span.End() } } graphql-go-1.6.0/trace/otel/trace_test.go000066400000000000000000000013731475633407000203210ustar00rootroot00000000000000package otel_test import ( "testing" "go.opentelemetry.io/otel" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" otelgraphql "github.com/graph-gophers/graphql-go/trace/otel" "github.com/graph-gophers/graphql-go/trace/tracer" ) func TestInterfaceImplementation(t *testing.T) { var _ tracer.ValidationTracer = &otelgraphql.Tracer{} var _ tracer.Tracer = &otelgraphql.Tracer{} } func TestTracerOption(t *testing.T) { _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(otelgraphql.DefaultTracer())) if err != nil { t.Fatal(err) } _, err = graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(&otelgraphql.Tracer{Tracer: otel.Tracer("example")})) if err != nil { t.Fatal(err) } } graphql-go-1.6.0/trace/trace.go000066400000000000000000000017271475633407000163220ustar00rootroot00000000000000// The trace package provides tracing functionality. // Deprecated: this package has been deprecated. Use package trace/tracer instead. package trace import ( "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/trace/noop" "github.com/graph-gophers/graphql-go/trace/opentracing" "github.com/graph-gophers/graphql-go/trace/tracer" ) // Deprecated: this type has been deprecated. Use [tracer.QueryFinishFunc] instead. type TraceQueryFinishFunc = func([]*errors.QueryError) // Deprecated: this type has been deprecated. Use [tarcer.FieldFinishFunc] instead. type TraceFieldFinishFunc = func(*errors.QueryError) // Deprecated: this interface has been deprecated. Use [tracer.Tracer] instead. type Tracer = tracer.Tracer // Deprecated: this type has been deprecated. Use [opentracing.Tracer] instead. type OpenTracingTracer = opentracing.Tracer // Deprecated: this type has been deprecated. Use [noop.Tracer] instead. type NoopTracer = noop.Tracer graphql-go-1.6.0/trace/trace_test.go000066400000000000000000000023651475633407000173600ustar00rootroot00000000000000package trace_test import ( "testing" "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/example/starwars" "github.com/graph-gophers/graphql-go/trace" "github.com/graph-gophers/graphql-go/trace/tracer" ) func TestInterfaceImplementation(t *testing.T) { var _ tracer.ValidationTracer = &trace.OpenTracingTracer{} var _ tracer.Tracer = &trace.OpenTracingTracer{} var _ tracer.ValidationTracer = &trace.NoopTracer{} var _ tracer.Tracer = &trace.NoopTracer{} } func TestTracerOption(t *testing.T) { _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(trace.OpenTracingTracer{})) if err != nil { t.Fatal(err) } } // MockVlidationTracer is a struct that implements the tracer.LegacyValidationTracer inteface. type MockValidationTracer struct{} func (MockValidationTracer) TraceValidation() func([]*errors.QueryError) { return func([]*errors.QueryError) {} } func TestValidationTracer(t *testing.T) { // test the legacy validation tracer interface (validating without using context) to ensure backwards compatibility vt := MockValidationTracer{} _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.ValidationTracer(vt)) if err != nil { t.Fatal(err) } } graphql-go-1.6.0/trace/tracer/000077500000000000000000000000001475633407000161465ustar00rootroot00000000000000graphql-go-1.6.0/trace/tracer/tracer.go000066400000000000000000000023231475633407000177550ustar00rootroot00000000000000// The tracer package provides tracing functionality. package tracer import ( "context" "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/introspection" ) type QueryFinishFunc = func([]*errors.QueryError) type FieldFinishFunc = func(*errors.QueryError) type ValidationFinishFunc = func([]*errors.QueryError) type Tracer interface { TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, QueryFinishFunc) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, FieldFinishFunc) } type ValidationTracer interface { TraceValidation(ctx context.Context) ValidationFinishFunc } // Deprecated: use [ValidationTracer] instead. type LegacyValidationTracer interface { TraceValidation() func([]*errors.QueryError) } // Deprecated: use a Tracer which implements [ValidationTracer]. type LegacyNoopValidationTracer struct{} // Deprecated: use a Tracer which implements [ValidationTracer]. func (LegacyNoopValidationTracer) TraceValidation() func([]*errors.QueryError) { return func(errs []*errors.QueryError) {} } graphql-go-1.6.0/trace/validation_trace.go000066400000000000000000000012541475633407000205270ustar00rootroot00000000000000package trace import ( "github.com/graph-gophers/graphql-go/errors" "github.com/graph-gophers/graphql-go/trace/tracer" ) // Deprecated: this type has been deprecated. Use tracer.ValidationFinishFunc instead. type TraceValidationFinishFunc = func([]*errors.QueryError) // Deprecated: use ValidationTracerContext. type ValidationTracer = tracer.LegacyValidationTracer //nolint:staticcheck // Deprecated: this type has been deprecated. Use tracer.ValidationTracer instead. type ValidationTracerContext = tracer.ValidationTracer // Deprecated: use a tracer that implements ValidationTracerContext. type NoopValidationTracer = tracer.LegacyNoopValidationTracer //nolint:staticcheck graphql-go-1.6.0/types/000077500000000000000000000000001475633407000147345ustar00rootroot00000000000000graphql-go-1.6.0/types/schema.go000066400000000000000000000101401475633407000165170ustar00rootroot00000000000000/* Package types represents all types from the GraphQL specification in code. The names of the Go types, whenever possible, match 1:1 with the names from the specification. Deprecated: Use package [ast] instead. This package will be deleted in future versions of the library. */ package types import "github.com/graph-gophers/graphql-go/ast" // Schema is here for backwards compatibility. // // Deprecated: use [ast.Schema] instead. type Schema = ast.Schema // Deprecated: use [ast.Argument] instead. type Argument = ast.Argument // Deprecated: use [ast.ArgumentList] instead. type ArgumentList = ast.ArgumentList // Deprecated: use [ast.ArgumentsDefinition] instead. type ArgumentsDefinition = ast.ArgumentsDefinition // Deprecated: use [ast.Directive] instead. type Directive = ast.Directive // Deprecated: use [ast.DirectiveDefinition] instead. type DirectiveDefinition = ast.DirectiveDefinition // Deprecated: use [ast.DirectiveList] instead. type DirectiveList = ast.DirectiveList // Deprecated: use [ast.EnumTypeDefinition] instead. type EnumTypeDefinition = ast.EnumTypeDefinition // Deprecated: use [ast.EnumValueDefinition] instead. type EnumValueDefinition = ast.EnumValueDefinition // Deprecated: use [ast.Extension] instead. type Extension = ast.Extension // Deprecated: use [ast.FieldDefinition] instead. type FieldDefinition = ast.FieldDefinition // Deprecated: use [ast.FieldsDefinition] instead. type FieldsDefinition = ast.FieldsDefinition // Deprecated: use [ast.Fragment] instead. type Fragment = ast.Fragment // Deprecated: use [ast.InlineFragment] instead. type InlineFragment = ast.InlineFragment // Deprecated: use [ast.FragmentDefinition] instead. type FragmentDefinition = ast.FragmentDefinition // Deprecated: use [ast.FragmentSpread] instead. type FragmentSpread = ast.FragmentSpread // Deprecated: use [ast.FragmentList] instead. type FragmentList = ast.FragmentList // Deprecated: use [ast.InputValueDefinition] instead. type InputValueDefinition = ast.InputValueDefinition // Deprecated: use [ast.InputValueDefinitionList] instead. type InputValueDefinitionList = ast.InputValueDefinitionList // Deprecated: use [ast.InputObject] instead. type InputObject = ast.InputObject // Deprecated: use [ast.InterfaceTypeDefinition] instead. type InterfaceTypeDefinition = ast.InterfaceTypeDefinition // Deprecated: use [ast.ObjectTypeDefinition] instead. type ObjectTypeDefinition = ast.ObjectTypeDefinition // Deprecated: use [ast.ExecutableDefinition] instead. type ExecutableDefinition = ast.ExecutableDefinition // Deprecated: use [ast.OperationDefinition] instead. type OperationDefinition = ast.OperationDefinition // Deprecated: use [ast.OperationType] instead. type OperationType = ast.OperationType // Deprecated: use [ast.Selection] instead. type Selection = ast.Selection // Deprecated: use [ast.SelectionSet] instead. type SelectionSet = ast.SelectionSet // Deprecated: use [ast.Field] instead. type Field = ast.Field // Deprecated: use [ast.OperationList] instead. type OperationList = ast.OperationList // Deprecated: use [ast.ScalarTypeDefinition] instead. type ScalarTypeDefinition = ast.ScalarTypeDefinition // Deprecated: use [ast.TypeName] instead. type TypeName = ast.TypeName // Deprecated: use [ast.NamedType] instead. type NamedType = ast.NamedType // Deprecated: use [ast.Ident] instead. type Ident = ast.Ident // Deprecated: use [ast.Type] instead. type Type = ast.Type // Deprecated: use [ast.List] instead. type List = ast.List // Deprecated: use [ast.NonNull] instead. type NonNull = ast.NonNull // Deprecated: use [ast.Union] instead. type Union = ast.Union // Deprecated: use [ast.Value] instead. type Value = ast.Value // Deprecated: use [ast.PrimitiveValue] instead. type PrimitiveValue = ast.PrimitiveValue // Deprecated: use [ast.ListValue] instead. type ListValue = ast.ListValue // Deprecated: use [ast.ObjectValue] instead. type ObjectValue = ast.ObjectValue // Deprecated: use [ast.ObjectField] instead. type ObjectField = ast.ObjectField // Deprecated: use [ast.NullValue] instead. type NullValue = ast.NullValue // Deprecated: use [ast.Variable] instead. type Variable = ast.Variable