├── .DS_Store ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ └── bench.yml ├── .gitignore ├── README.md ├── analyze.sh ├── assets ├── architecture.png ├── canvas.excalidraw ├── latency_histogram1.png ├── latency_histogram2.png ├── latency_histogram3.png ├── req_sec_histogram1.png ├── req_sec_histogram2.png └── req_sec_histogram3.png ├── graphql ├── apollo_server │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── run.sh │ └── setup.sh ├── async_graphql │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.sh │ ├── run.sh │ ├── setup.sh │ └── src │ │ └── main.rs ├── caliban │ ├── .gitignore │ ├── .scalafmt.conf │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── run.sh │ ├── sbt │ ├── setup.sh │ └── src │ │ └── main │ │ └── scala │ │ ├── Api.scala │ │ ├── Client.scala │ │ ├── Main.scala │ │ └── Service.scala ├── gqlgen │ ├── build.sh │ ├── go.mod │ ├── go.sum │ ├── gqlgen.yml │ ├── graph │ │ ├── generated.go │ │ ├── model │ │ │ └── models_gen.go │ │ ├── resolver.go │ │ ├── schema.graphqls │ │ └── schema.resolvers.go │ ├── main.go │ ├── run.sh │ ├── setup.sh │ └── tools.go ├── graphql_jit │ ├── package-lock.json │ ├── package.json │ ├── run.sh │ ├── server.js │ └── setup.sh ├── hasura │ ├── config.yaml │ ├── handler.js │ ├── kill.sh │ ├── metadata │ │ ├── actions.graphql │ │ ├── actions.yaml │ │ ├── databases │ │ │ └── databases.yaml │ │ └── version.yaml │ ├── package-lock.json │ ├── package.json │ ├── run.sh │ └── setup.sh ├── netflix_dgs │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── run.sh │ ├── settings.gradle │ ├── setup.sh │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── netflixdgs │ │ │ │ ├── AppConfig.java │ │ │ │ ├── GreetDataFetcher.java │ │ │ │ ├── NetflixdgsApplication.java │ │ │ │ ├── Post.java │ │ │ │ ├── PostsDataFetcher.java │ │ │ │ ├── User.java │ │ │ │ └── UserDataLoader.java │ │ └── resources │ │ │ ├── application.properties │ │ │ └── schema │ │ │ └── schema.graphql │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── netflixdgs │ │ └── NetflixdgsApplicationTests.java └── tailcall │ ├── benchmark.graphql │ ├── build.sh │ ├── package-lock.json │ ├── package.json │ ├── run.sh │ └── setup.sh ├── nginx ├── nginx.conf └── run.sh ├── renovate.json ├── results.md ├── run_analyze_script.sh ├── run_benchmarks.sh ├── setup.sh ├── test_query1.sh ├── test_query2.sh ├── test_query3.sh └── wrk ├── bench.sh ├── wrk1.lua ├── wrk2.lua └── wrk3.lua /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/.DS_Store -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Start with the Microsoft's Ubuntu 22.04 base image for Codespaces 2 | FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu-22.04 3 | 4 | # Install necessary packages and tools 5 | RUN apt-get update && apt-get install -y \ 6 | build-essential \ 7 | libssl-dev \ 8 | pkg-config \ 9 | nginx \ 10 | openjdk-17-jdk \ 11 | git \ 12 | curl \ 13 | wget \ 14 | wrk \ 15 | gnuplot \ 16 | postgresql \ 17 | postgresql-client \ 18 | && rm -rf /var/lib/apt/lists/* 19 | 20 | # Install the latest LTS version of Node.js 21 | RUN curl -L https://deb.nodesource.com/nsolid_setup_deb.sh | bash -s -- 18 && \ 22 | apt-get install -y nodejs 23 | 24 | # Install Go 25 | RUN wget https://dl.google.com/go/go1.21.1.linux-amd64.tar.gz -O- | tar xz -C /usr/local 26 | ENV PATH=$PATH:/usr/local/go/bin 27 | 28 | # Nginx configurations 29 | RUN mkdir -p /var/cache/nginx-cache \ 30 | /var/lib/nginx/body \ 31 | /var/lib/nginx/proxy \ 32 | /var/lib/nginx/fastcgi \ 33 | /var/lib/nginx/uwsgi \ 34 | /var/lib/nginx/scgi \ 35 | /var/log/nginx \ 36 | && touch /var/log/nginx/access.log /var/log/nginx/error.log \ 37 | && chown -R www-data:www-data /var/cache/nginx-cache /var/log/nginx /var/lib/nginx 38 | 39 | # Set the working directory 40 | WORKDIR /workspace 41 | 42 | # Expose necessary ports 43 | EXPOSE 8000 3000 44 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tc-benchmarks Codespace", 3 | "features": { 4 | "docker-from-docker": { 5 | "version": "latest", 6 | "moby": true 7 | } 8 | }, 9 | "build": { 10 | "dockerfile": "Dockerfile", 11 | "context": ".." 12 | }, 13 | "runArgs": ["-v", "/var/run/docker.sock:/var/run/docker.sock", "--network=host"], 14 | "forwardPorts": [8000, 3000], 15 | "postCreateCommand": "bash ./setup.sh" 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/bench.yml: -------------------------------------------------------------------------------- 1 | name: "Run benchmark" 2 | on: 3 | pull_request_target: 4 | types: [assigned, opened, synchronize, reopened, edited] 5 | push: 6 | branches: 7 | - main 8 | permissions: 9 | contents: write 10 | packages: write 11 | pull-requests: write 12 | issues: write 13 | jobs: 14 | build: 15 | runs-on: 16 | group: benchmarking-runner 17 | if: github.event.head_commit.message != 'Update performance results in README.md' 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | strategy: 21 | matrix: 22 | service: 23 | [ 24 | apollo_server, 25 | caliban, 26 | netflix_dgs, 27 | gqlgen, 28 | tailcall, 29 | async_graphql, 30 | hasura, 31 | graphql_jit, 32 | ] 33 | steps: 34 | - name: Checkout (GitHub) 35 | uses: actions/checkout@v4 36 | with: 37 | token: ${{ secrets.GITHUB_TOKEN }} 38 | ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }} 39 | - name: Login to GitHub Container Registry 40 | uses: docker/login-action@v3 41 | with: 42 | registry: ghcr.io 43 | username: ${{ github.repository_owner }} 44 | password: ${{ secrets.GITHUB_TOKEN }} 45 | - name: Build devcontainer and run setup and benchmark 46 | uses: devcontainers/ci@v0.3 47 | with: 48 | imageName: ghcr.io/tailcallhq/graphql-benchmark 49 | push: always 50 | runCmd: | 51 | bash ./graphql/${{ matrix.service }}/setup.sh 52 | bash run_benchmarks.sh ${{ matrix.service }} 53 | 54 | - name: List benchmark files 55 | run: | 56 | ls -la bench*.txt || echo "No matching files found" 57 | 58 | - name: Upload benchmark results 59 | uses: actions/upload-artifact@v3 60 | with: 61 | name: benchmark-results 62 | path: bench*.txt 63 | 64 | analyze: 65 | needs: build 66 | runs-on: ubuntu-latest 67 | steps: 68 | - name: Checkout (GitHub) 69 | uses: actions/checkout@v4 70 | 71 | - name: Download all benchmark results 72 | uses: actions/download-artifact@v3 73 | with: 74 | name: benchmark-results 75 | path: . 76 | 77 | - name: List downloaded artifacts 78 | run: ls -la bench*.txt || echo "No matching files found" 79 | 80 | - name: Analyze results 81 | run: | 82 | bash run_analyze_script.sh 83 | 84 | - name: Print benchmark results 85 | run: cat ./results.md 86 | 87 | - name: Comment benchmark results on PR 88 | if: github.event_name == 'pull_request_target' 89 | uses: peter-evans/commit-comment@v3 90 | with: 91 | sha: ${{ github.event.pull_request.head.sha }} 92 | body-path: "results.md" 93 | reactions: eyes 94 | 95 | - name: Commit and push changes (on main branch) 96 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 97 | uses: stefanzweifel/git-auto-commit-action@v5 98 | with: 99 | branch: main 100 | commit_author: Author 101 | commit_message: "[ci skip] update performance results in README.md" 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | #Netflix DGS 3 | *.class 4 | *.log 5 | *.jar 6 | *.war 7 | *.nar 8 | *.ear 9 | *.zip 10 | *.tar.gz 11 | *.rar 12 | logs 13 | #Apollo 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | lerna-debug.log* 18 | .pnpm-debug.log* 19 | node_modules/ 20 | /.idea 21 | # gqlgen 22 | graphql/gqlgen/main 23 | # Tailcall 24 | tailcall-src 25 | 26 | metals.* 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL Benchmarks 2 | 3 | [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/tailcallhq/graphql-benchmarks) 4 | 5 | Explore and compare the performance of the fastest GraphQL frameworks through our comprehensive benchmarks. 6 | 7 | - [Introduction](#introduction) 8 | - [Quick Start](#quick-start) 9 | - [Benchmark Results](#benchmark-results) 10 | - [Throughput (Higher is better)](#throughput-higher-is-better) 11 | - [Latency (Lower is better)](#latency-lower-is-better) 12 | - [Architecture](#architecture) 13 | - [WRK](#wrk) 14 | - [GraphQL](#graphql) 15 | - [Nginx](#nginx) 16 | - [Jsonplaceholder](#jsonplaceholder) 17 | - [GraphQL Schema](#graphql-schema) 18 | - [Contribute](#contribute) 19 | 20 | [Tailcall]: https://github.com/tailcallhq/tailcall 21 | [Gqlgen]: https://github.com/99designs/gqlgen 22 | [Apollo GraphQL]: https://github.com/apollographql/apollo-server 23 | [Netflix DGS]: https://github.com/netflix/dgs-framework 24 | [Caliban]: https://github.com/ghostdogpr/caliban 25 | [async-graphql]: https://github.com/async-graphql/async-graphql 26 | [Hasura]: https://github.com/hasura/graphql-engine 27 | [GraphQL JIT]: https://github.com/zalando-incubator/graphql-jit 28 | 29 | ## Introduction 30 | 31 | This document presents a comparative analysis of several renowned GraphQL frameworks. Dive deep into the performance metrics, and get insights into their throughput and latency. 32 | 33 | > **NOTE:** This is a work in progress suite of benchmarks, and we would appreciate help from the community to add more frameworks or tune the existing ones for better performance. 34 | 35 | ## Quick Start 36 | 37 | Get started with the benchmarks: 38 | 39 | 1. Click on this [link](https://codespaces.new/tailcallhq/graphql-benchmarks) to set up on GitHub Codespaces. 40 | 2. Once set up in Codespaces, initiate the benchmark tests: 41 | 42 | ```bash 43 | ./setup.sh 44 | ./run_benchmarks.sh 45 | ``` 46 | 47 | ## Benchmark Results 48 | 49 | 50 | 51 | | Query | Server | Requests/sec | Latency (ms) | Relative | 52 | |-------:|--------:|--------------:|--------------:|---------:| 53 | | 1 | `{ posts { id userId title user { id name email }}}` | 54 | || [Tailcall] | `22,805.00` | `4.30` | `214.34x` | 55 | || [async-graphql] | `1,080.88` | `91.86` | `10.16x` | 56 | || [GraphQL JIT] | `1,038.83` | `95.78` | `9.76x` | 57 | || [Caliban] | `902.18` | `110.94` | `8.48x` | 58 | || [Gqlgen] | `400.68` | `246.33` | `3.77x` | 59 | || [Netflix DGS] | `191.44` | `506.58` | `1.80x` | 60 | || [Apollo GraphQL] | `133.94` | `687.14` | `1.26x` | 61 | || [Hasura] | `106.40` | `824.18` | `1.00x` | 62 | | 2 | `{ posts { title }}` | 63 | || [Tailcall] | `36,300.30` | `2.69` | `82.05x` | 64 | || [Caliban] | `5,554.80` | `18.07` | `12.55x` | 65 | || [async-graphql] | `5,296.11` | `18.90` | `11.97x` | 66 | || [Gqlgen] | `1,112.44` | `98.53` | `2.51x` | 67 | || [GraphQL JIT] | `1,066.55` | `93.57` | `2.41x` | 68 | || [Apollo GraphQL] | `896.38` | `112.08` | `2.03x` | 69 | || [Netflix DGS] | `818.73` | `149.69` | `1.85x` | 70 | || [Hasura] | `442.44` | `226.86` | `1.00x` | 71 | | 3 | `{ greet }` | 72 | || [Caliban] | `47,657.70` | `2.04` | `30.11x` | 73 | || [Tailcall] | `46,811.10` | `2.09` | `29.57x` | 74 | || [Gqlgen] | `25,725.90` | `4.91` | `16.25x` | 75 | || [async-graphql] | `25,616.50` | `3.92` | `16.18x` | 76 | || [GraphQL JIT] | `4,434.19` | `22.51` | `2.80x` | 77 | || [Netflix DGS] | `4,119.79` | `27.30` | `2.60x` | 78 | || [Apollo GraphQL] | `4,071.10` | `27.92` | `2.57x` | 79 | || [Hasura] | `1,583.00` | `64.75` | `1.00x` | 80 | 81 | 82 | 83 | 84 | 85 | ### 1. `{posts {title body user {name}}}` 86 | #### Throughput (Higher is better) 87 | 88 | ![Throughput Histogram](assets/req_sec_histogram1.png) 89 | 90 | #### Latency (Lower is better) 91 | 92 | ![Latency Histogram](assets/latency_histogram1.png) 93 | 94 | ### 2. `{posts {title body}}` 95 | #### Throughput (Higher is better) 96 | 97 | ![Throughput Histogram](assets/req_sec_histogram2.png) 98 | 99 | #### Latency (Lower is better) 100 | 101 | ![Latency Histogram](assets/latency_histogram2.png) 102 | 103 | ### 3. `{greet}` 104 | #### Throughput (Higher is better) 105 | 106 | ![Throughput Histogram](assets/req_sec_histogram3.png) 107 | 108 | #### Latency (Lower is better) 109 | 110 | ![Latency Histogram](assets/latency_histogram3.png) 111 | 112 | ## Architecture 113 | 114 | ![Architecture Diagram](assets/architecture.png) 115 | 116 | A client (`wrk`) sends requests to a GraphQL server to fetch post titles. The GraphQL server, in turn, retrieves data from an external source, `jsonplaceholder.typicode.com`, routed through the `nginx` reverse proxy. 117 | 118 | ### WRK 119 | 120 | `wrk` serves as our test client, sending GraphQL requests at a high rate. 121 | 122 | ### GraphQL 123 | 124 | Our tested GraphQL server. We evaluated various implementations, ensuring no caching on the GraphQL server side. 125 | 126 | ### Nginx 127 | 128 | A reverse-proxy that caches every response, mitigating rate-limiting and reducing network uncertainties. 129 | 130 | ### Jsonplaceholder 131 | 132 | The primary upstream service forming the base for our GraphQL API. We query its `/posts` API via the GraphQL server. 133 | 134 | ## GraphQL Schema 135 | 136 | Inspect the generated GraphQL schema employed for the benchmarks: 137 | 138 | ```graphql 139 | schema { 140 | query: Query 141 | } 142 | 143 | type Query { 144 | posts: [Post] 145 | } 146 | 147 | type Post { 148 | id: Int! 149 | userId: Int! 150 | title: String! 151 | body: String! 152 | user: User 153 | } 154 | 155 | type User { 156 | id: Int! 157 | name: String! 158 | username: String! 159 | email: String! 160 | phone: String 161 | website: String 162 | } 163 | ``` 164 | 165 | ## Contribute 166 | 167 | Your insights are invaluable! Test these benchmarks, share feedback, or contribute by adding more GraphQL frameworks or refining existing ones. Open an issue or a pull request, and let's build a robust benchmarking resource together! 168 | -------------------------------------------------------------------------------- /analyze.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Install gnuplot 4 | sudo apt-get update && sudo apt-get install -y gnuplot 5 | 6 | function extractMetric() { 7 | local file="$1" 8 | local metric="$2" 9 | grep "$metric" "$file" | awk '{print $2}' | sed 's/ms//' 10 | } 11 | 12 | function average() { 13 | echo "$@" | awk '{for(i=1;i<=NF;i++) s+=$i; print s/NF}' 14 | } 15 | 16 | declare -A formattedServerNames 17 | formattedServerNames=( 18 | ["tailcall"]="Tailcall" 19 | ["gqlgen"]="Gqlgen" 20 | ["apollo"]="Apollo GraphQL" 21 | ["netflixdgs"]="Netflix DGS" 22 | ["caliban"]="Caliban" 23 | ["async_graphql"]="async-graphql" 24 | ["hasura"]="Hasura" 25 | ["graphql_jit"]="GraphQL JIT" 26 | ) 27 | 28 | servers=("apollo" "caliban" "netflixdgs" "gqlgen" "tailcall" "async_graphql" "hasura" "graphql_jit") 29 | resultFiles=("$@") 30 | declare -A avgReqSecs 31 | declare -A avgLatencies 32 | 33 | # Extract metrics and calculate averages 34 | for idx in "${!servers[@]}"; do 35 | startIdx=$((idx * 3)) 36 | reqSecVals=() 37 | latencyVals=() 38 | for j in 0 1 2; do 39 | fileIdx=$((startIdx + j)) 40 | reqSecVals+=($(extractMetric "${resultFiles[$fileIdx]}" "Requests/sec")) 41 | latencyVals+=($(extractMetric "${resultFiles[$fileIdx]}" "Latency")) 42 | done 43 | avgReqSecs[${servers[$idx]}]=$(average "${reqSecVals[@]}") 44 | avgLatencies[${servers[$idx]}]=$(average "${latencyVals[@]}") 45 | done 46 | 47 | # Generating data files for gnuplot 48 | reqSecData="/tmp/reqSec.dat" 49 | latencyData="/tmp/latency.dat" 50 | 51 | echo "Server Value" >"$reqSecData" 52 | for server in "${servers[@]}"; do 53 | echo "$server ${avgReqSecs[$server]}" >>"$reqSecData" 54 | done 55 | 56 | echo "Server Value" >"$latencyData" 57 | for server in "${servers[@]}"; do 58 | echo "$server ${avgLatencies[$server]}" >>"$latencyData" 59 | done 60 | 61 | whichBench=1 62 | if [[ $1 == bench2* ]]; then 63 | whichBench=2 64 | elif [[ $1 == bench3* ]]; then 65 | whichBench=3 66 | fi 67 | 68 | reqSecHistogramFile="req_sec_histogram${whichBench}.png" 69 | latencyHistogramFile="latency_histogram${whichBench}.png" 70 | 71 | # Plotting using gnuplot 72 | gnuplot <<-EOF 73 | set term pngcairo size 1280,720 enhanced font "Courier,12" 74 | set output "$reqSecHistogramFile" 75 | set style data histograms 76 | set style histogram cluster gap 1 77 | set style fill solid border -1 78 | set xtics rotate by -45 79 | set boxwidth 0.9 80 | set title "Requests/Sec" 81 | stats "$reqSecData" using 2 nooutput 82 | set yrange [0:STATS_max*1.2] 83 | set key outside right top 84 | plot "$reqSecData" using 2:xtic(1) title "Req/Sec" 85 | 86 | set output "$latencyHistogramFile" 87 | set title "Latency (in ms)" 88 | stats "$latencyData" using 2 nooutput 89 | set yrange [0:STATS_max*1.2] 90 | plot "$latencyData" using 2:xtic(1) title "Latency" 91 | EOF 92 | 93 | # Move PNGs to assets 94 | mkdir -p assets 95 | mv $reqSecHistogramFile assets/ 96 | mv $latencyHistogramFile assets/ 97 | 98 | # Declare an associative array for server RPS 99 | declare -A serverRPS 100 | 101 | # Populate the serverRPS array 102 | for server in "${servers[@]}"; do 103 | serverRPS[$server]=${avgReqSecs[$server]} 104 | done 105 | 106 | # Get the servers sorted by RPS in descending order 107 | IFS=$'\n' sortedServers=($(for server in "${!serverRPS[@]}"; do echo "$server ${serverRPS[$server]}"; done | sort -rn -k2 | cut -d' ' -f1)) 108 | 109 | echo "Sorted servers: ${sortedServers[@]}" 110 | lastServer="${sortedServers[-1]}" 111 | lastServerReqSecs=${avgReqSecs[$lastServer]} 112 | 113 | # Start building the resultsTable 114 | if [[ $whichBench == 1 ]]; then 115 | resultsTable="\n\n| Query | Server | Requests/sec | Latency (ms) | Relative |\n|-------:|--------:|--------------:|--------------:|---------:|\n| $whichBench | \`{ posts { id userId title user { id name email }}}\` |" 116 | elif [[ $whichBench == 2 ]]; then 117 | resultsTable="| $whichBench | \`{ posts { title }}\` |" 118 | elif [[ $whichBench == 3 ]]; then 119 | resultsTable="| $whichBench | \`{ greet }\` |" 120 | fi 121 | 122 | # Build the resultsTable with sorted servers and formatted numbers 123 | for server in "${sortedServers[@]}"; do 124 | formattedReqSecs=$(printf "%.2f" ${avgReqSecs[$server]} | perl -pe 's/(?<=\d)(?=(\d{3})+(\.\d*)?$)/,/g') 125 | formattedLatencies=$(printf "%.2f" ${avgLatencies[$server]} | perl -pe 's/(?<=\d)(?=(\d{3})+(\.\d*)?$)/,/g') 126 | # Calculate the relative performance 127 | relativePerformance=$(echo "${avgReqSecs[$server]} $lastServerReqSecs" | awk '{printf "%.2f", $1 / $2}') 128 | 129 | resultsTable+="\n|| [${formattedServerNames[$server]}] | \`${formattedReqSecs}\` | \`${formattedLatencies}\` | \`${relativePerformance}x\` |" 130 | done 131 | 132 | if [[ $whichBench == 3 ]]; then 133 | resultsTable+="\n\n" 134 | fi 135 | 136 | echo "resultsTable: $resultsTable" 137 | 138 | # Print the results table in a new file 139 | resultsFile="results.md" 140 | echo -e $resultsTable >> $resultsFile 141 | 142 | 143 | if [[ $whichBench == 3 ]]; then 144 | finalResults=$(printf '%s\n' "$(cat $resultsFile)" | sed 's/$/\\n/'| tr -d '\n') 145 | # Remove the last newline character 146 | finalResults=${finalResults::-2} 147 | 148 | # Print the results as a table in the terminal 149 | echo -e $finalResults | sed "s///;s///" 150 | # Check if the markers are present 151 | if grep -q "PERFORMANCE_RESULTS_START" README.md; then 152 | # Replace the old results with the new results 153 | sed -i "/PERFORMANCE_RESULTS_START/,/PERFORMANCE_RESULTS_END/c\\$finalResults" README.md 154 | else 155 | # Append the results at the end of the README.md file 156 | echo -e "\n$finalResults" >> README.md 157 | fi 158 | fi 159 | 160 | # Move the generated images to the assets folder 161 | mv $reqSecHistogramFile assets/ 162 | mv $latencyHistogramFile assets/ 163 | 164 | # Delete the result TXT files 165 | for file in "${resultFiles[@]}"; do 166 | rm "$file" 167 | done -------------------------------------------------------------------------------- /assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/assets/architecture.png -------------------------------------------------------------------------------- /assets/latency_histogram1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/assets/latency_histogram1.png -------------------------------------------------------------------------------- /assets/latency_histogram2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/assets/latency_histogram2.png -------------------------------------------------------------------------------- /assets/latency_histogram3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/assets/latency_histogram3.png -------------------------------------------------------------------------------- /assets/req_sec_histogram1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/assets/req_sec_histogram1.png -------------------------------------------------------------------------------- /assets/req_sec_histogram2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/assets/req_sec_histogram2.png -------------------------------------------------------------------------------- /assets/req_sec_histogram3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/assets/req_sec_histogram3.png -------------------------------------------------------------------------------- /graphql/apollo_server/index.js: -------------------------------------------------------------------------------- 1 | import { ApolloServer } from "@apollo/server"; 2 | import { startStandaloneServer } from "@apollo/server/standalone"; 3 | import axios from "axios"; 4 | import { Agent } from "http"; 5 | import DataLoader from "dataloader"; 6 | 7 | // Create a new axios instance with connection pooling. 8 | const httpAgent = new Agent({ keepAlive: true }); 9 | const axiosInstance = axios.create({ 10 | httpAgent, 11 | }); 12 | 13 | const typeDefs = `#graphql 14 | 15 | type User { 16 | id: Int! 17 | name: String! 18 | username: String! 19 | email: String! 20 | phone: String 21 | website: String 22 | } 23 | 24 | type Post { 25 | id: Int! 26 | userId: Int! 27 | title: String! 28 | body: String! 29 | user: User 30 | } 31 | 32 | type Query { 33 | posts: [Post] 34 | greet: String! 35 | } 36 | `; 37 | 38 | async function batchUsers(usersIds) { 39 | const requests = usersIds.map(async (id) => { 40 | const response = await axiosInstance.get( 41 | `http://jsonplaceholder.typicode.com/users/${id}`, 42 | { 43 | proxy: { 44 | protocol: "http", 45 | host: "127.0.0.1", 46 | port: 3000, 47 | }, 48 | }, 49 | ); 50 | return response.data; 51 | }); 52 | return await Promise.all(requests); 53 | } 54 | 55 | const resolvers = { 56 | Query: { 57 | posts: async () => { 58 | try { 59 | const response = await axiosInstance.get( 60 | "http://jsonplaceholder.typicode.com/posts", 61 | { 62 | proxy: { 63 | protocol: "http", 64 | host: "127.0.0.1", 65 | port: 3000, 66 | }, 67 | }, 68 | ); 69 | return response.data; 70 | } catch (error) { 71 | throw new Error("Failed to fetch posts"); 72 | } 73 | }, 74 | greet: () => { 75 | return "Hello World!"; 76 | }, 77 | }, 78 | Post: { 79 | user: async (post, _, { userLoader }) => { 80 | return userLoader.load(post.userId); 81 | }, 82 | }, 83 | }; 84 | 85 | const server = new ApolloServer({ 86 | typeDefs, 87 | resolvers, 88 | }); 89 | 90 | const { url } = await startStandaloneServer(server, { 91 | context: async () => { 92 | return { 93 | userLoader: new DataLoader(batchUsers, { 94 | batchScheduleFn: (callback) => setTimeout(callback, 1), 95 | }), 96 | }; 97 | }, 98 | listen: { port: 8000 }, 99 | }); 100 | 101 | console.log(`🚀 Server ready at: ${url}`); 102 | -------------------------------------------------------------------------------- /graphql/apollo_server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "pm2 start index.js -i max", 9 | "stop": "pm2 stop index", 10 | "delete-from-pm2": "pm2 delete index", 11 | "logs": "pm2 logs", 12 | "stats": "pm2 status" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "@apollo/server": "^4.9.3", 19 | "axios": "^1.5.0", 20 | "dataloader": "^2.2.2", 21 | "graphql": "^16.8.0", 22 | "http-proxy-agent": "^7.0.0", 23 | "pm2": "^5.3.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /graphql/apollo_server/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pwd 3 | cd graphql/apollo_server 4 | npm i 5 | npm start -------------------------------------------------------------------------------- /graphql/apollo_server/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # For apollo server: 4 | cd graphql/apollo_server 5 | npm i 6 | cd ../../ -------------------------------------------------------------------------------- /graphql/async_graphql/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /graphql/async_graphql/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_graphql_tailcall" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.82" 8 | async-graphql = { version = "7.0.3", features = ["dataloader"] } 9 | async-graphql-axum = "7.0.3" 10 | axum = "0.8.0" 11 | tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "net", "parking_lot"] } 12 | reqwest = { version = "0.12.4", features = ["json"] } 13 | serde = "1.0.200" 14 | serde_json = "1.0.116" 15 | futures = "0.3.30" 16 | mimalloc = { version = "0.1.41", default-features = false } -------------------------------------------------------------------------------- /graphql/async_graphql/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd graphql/async_graphql 3 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 4 | ~/.cargo/bin/cargo build --release 5 | -------------------------------------------------------------------------------- /graphql/async_graphql/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd graphql/async_graphql 6 | ./target/release/async_graphql_tailcall 7 | -------------------------------------------------------------------------------- /graphql/async_graphql/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # For async-graphql 4 | ./graphql/async_graphql/build.sh -------------------------------------------------------------------------------- /graphql/async_graphql/src/main.rs: -------------------------------------------------------------------------------- 1 | use mimalloc::MiMalloc; 2 | 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | 6 | use std::{collections::HashMap, sync::Arc, time::Duration}; 7 | 8 | use async_graphql::{ 9 | dataloader::{DataLoader, Loader}, 10 | http::GraphiQLSource, 11 | Context, EmptyMutation, EmptySubscription, Object, Schema, SimpleObject, 12 | }; 13 | use async_graphql_axum::GraphQL; 14 | use axum::{ 15 | response::{self, IntoResponse}, 16 | routing::get, 17 | Router, 18 | }; 19 | 20 | use futures::future::try_join_all; 21 | use reqwest::Proxy; 22 | use tokio::net::TcpListener; 23 | 24 | async fn graphiql() -> impl IntoResponse { 25 | response::Html(GraphiQLSource::build().endpoint("/").finish()) 26 | } 27 | 28 | #[derive(SimpleObject, serde::Serialize, serde::Deserialize, Debug, Clone)] 29 | pub struct User { 30 | id: u32, 31 | name: String, 32 | username: String, 33 | email: String, 34 | phone: String, 35 | website: String, 36 | } 37 | 38 | #[derive(Debug, SimpleObject, serde::Serialize, serde::Deserialize)] 39 | pub struct Post { 40 | id: u32, 41 | #[serde(rename = "userId")] 42 | user_id: u32, 43 | title: String, 44 | body: String, 45 | user: Option, 46 | } 47 | 48 | pub struct QueryRoot; 49 | 50 | const BASE_URL_POSTS: &str = "http://jsonplaceholder.typicode.com/posts"; 51 | const BASE_URL_USERS: &str = "http://jsonplaceholder.typicode.com/users"; 52 | 53 | pub struct UserLoader { 54 | client: reqwest::Client, 55 | } 56 | 57 | impl Loader for UserLoader { 58 | type Value = User; 59 | type Error = Arc; 60 | 61 | async fn load(&self, keys: &[u32]) -> Result, Self::Error> { 62 | let mut results = HashMap::new(); 63 | for &key in keys { 64 | let user_url = format!("{}/{}", BASE_URL_USERS, key); 65 | let user = self 66 | .client 67 | .get(&user_url) 68 | .send() 69 | .await? 70 | .json::() 71 | .await?; 72 | results.insert(key, user); 73 | } 74 | Ok(results) 75 | } 76 | } 77 | 78 | #[Object] 79 | impl QueryRoot { 80 | pub async fn posts(&self, ctx: &Context<'_>) -> anyhow::Result> { 81 | let client = ctx.data_unchecked::(); 82 | let posts = client 83 | .get(BASE_URL_POSTS) 84 | .send() 85 | .await? 86 | .json::>() 87 | .await?; 88 | 89 | if !ctx.look_ahead().field("user").exists() { 90 | return Ok(posts); 91 | } 92 | 93 | let user_loader = Arc::new(DataLoader::new( 94 | UserLoader { 95 | client: client.clone(), 96 | }, 97 | tokio::spawn, 98 | )); 99 | 100 | let mut post_user_futures = Vec::new(); 101 | for post in posts { 102 | let user_loader = user_loader.clone(); 103 | post_user_futures.push(async move { 104 | match user_loader.load_one(post.user_id).await { 105 | Ok(Some(user)) => Ok(Post { 106 | user: Some(user), 107 | ..post 108 | }), 109 | Ok(None) => Ok(post), 110 | Err(e) => Err(e), 111 | } 112 | }); 113 | } 114 | 115 | let posts_with_users: Vec = try_join_all(post_user_futures).await?; 116 | 117 | Ok(posts_with_users) 118 | } 119 | pub async fn greet(&self) -> String { 120 | "Hello World!".to_string() 121 | } 122 | } 123 | 124 | fn main() { 125 | let cpus: usize = std::thread::available_parallelism().unwrap().into(); 126 | let listener = std::net::TcpListener::bind("127.0.0.1:8000").unwrap(); 127 | 128 | let mut threads = vec![]; 129 | 130 | for _ in 0..cpus { 131 | let listener = listener.try_clone().unwrap(); 132 | 133 | let handle = std::thread::spawn(move || { 134 | let _ = tokio::runtime::Builder::new_current_thread() 135 | .worker_threads(8) 136 | .enable_all() 137 | .build() 138 | .unwrap() 139 | .block_on(async move { 140 | let client = reqwest::ClientBuilder::new() 141 | .proxy(Proxy::all("http://127.0.0.1:3000").unwrap()) 142 | .tcp_keepalive(Some(Duration::from_secs(60))) 143 | .tcp_nodelay(true) 144 | .no_gzip() 145 | .http2_keep_alive_timeout(Duration::from_secs(60)) 146 | .pool_max_idle_per_host(200) 147 | .build() 148 | .unwrap(); 149 | 150 | let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription) 151 | .data(client) 152 | .finish(); 153 | 154 | let app = Router::new() 155 | .route("/graphql", get(graphiql).post_service(GraphQL::new(schema))); 156 | 157 | println!("GraphiQL IDE: http://localhost:8000"); 158 | // For each thread 159 | listener.set_nonblocking(true).unwrap(); 160 | 161 | axum::serve(TcpListener::from_std(listener).unwrap(), app) 162 | .await 163 | .unwrap(); 164 | }); 165 | }); 166 | 167 | threads.push(handle); 168 | } 169 | 170 | for handle in threads { 171 | handle.join().unwrap(); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /graphql/caliban/.gitignore: -------------------------------------------------------------------------------- 1 | **/target/** 2 | .bsp 3 | .idea 4 | -------------------------------------------------------------------------------- /graphql/caliban/.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "3.7.14" 2 | runner.dialect = "scala3" 3 | 4 | align.preset = more 5 | maxColumn = 120 6 | -------------------------------------------------------------------------------- /graphql/caliban/build.sbt: -------------------------------------------------------------------------------- 1 | ThisBuild / scalaVersion := "3.6.3" 2 | ThisBuild / version := "0.1.0-SNAPSHOT" 3 | ThisBuild / organization := "com.example" 4 | ThisBuild / organizationName := "example" 5 | 6 | lazy val root = (project in file(".")) 7 | .settings( 8 | name := "scala-caliban", 9 | run / fork := true, 10 | run / javaOptions ++= Seq("-Xms4G", "-Xmx4G"), 11 | assembly / mainClass := Some("Main"), 12 | assembly / assemblyMergeStrategy := { 13 | case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard 14 | case PathList("META-INF", "io.netty.versions.properties") => MergeStrategy.first 15 | case x => MergeStrategy.first 16 | }, 17 | libraryDependencies ++= Seq( 18 | "com.github.ghostdogpr" %% "caliban-quick" % "2.9.1", 19 | "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.33.1", 20 | "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.33.1" % Provided, 21 | "org.apache.httpcomponents.client5" % "httpclient5" % "5.4.1", 22 | "dev.zio" %% "zio" % "2.1.14" 23 | ) 24 | ) 25 | 26 | resolvers ++= Resolver.sonatypeOssRepos("snapshots") 27 | -------------------------------------------------------------------------------- /graphql/caliban/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.10.7 2 | -------------------------------------------------------------------------------- /graphql/caliban/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1") 2 | -------------------------------------------------------------------------------- /graphql/caliban/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | pwd 6 | cd graphql/caliban 7 | JAR_FILE=$(find . -name "scala-caliban-assembly*") 8 | java -Xms4G -Xmx4G -jar $JAR_FILE 9 | -------------------------------------------------------------------------------- /graphql/caliban/sbt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # A more capable sbt runner, coincidentally also called sbt. 4 | # Author: Paul Phillips 5 | # https://github.com/paulp/sbt-extras 6 | # 7 | # Generated from http://www.opensource.org/licenses/bsd-license.php 8 | # Copyright (c) 2011, Paul Phillips. All rights reserved. 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are 12 | # met: 13 | # 14 | # * Redistributions of source code must retain the above copyright 15 | # notice, this list of conditions and the following disclaimer. 16 | # * Redistributions in binary form must reproduce the above copyright 17 | # notice, this list of conditions and the following disclaimer in the 18 | # documentation and/or other materials provided with the distribution. 19 | # * Neither the name of the author nor the names of its contributors 20 | # may be used to endorse or promote products derived from this software 21 | # without specific prior written permission. 22 | # 23 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | set -o pipefail 36 | 37 | declare -r sbt_release_version="1.9.7" 38 | declare -r sbt_unreleased_version="1.9.7" 39 | 40 | declare -r latest_213="2.13.12" 41 | declare -r latest_212="2.12.18" 42 | declare -r latest_211="2.11.12" 43 | declare -r latest_210="2.10.7" 44 | declare -r latest_29="2.9.3" 45 | declare -r latest_28="2.8.2" 46 | 47 | declare -r buildProps="project/build.properties" 48 | 49 | declare -r sbt_launch_ivy_release_repo="https://repo.typesafe.com/typesafe/ivy-releases" 50 | declare -r sbt_launch_ivy_snapshot_repo="https://repo.scala-sbt.org/scalasbt/ivy-snapshots" 51 | declare -r sbt_launch_mvn_release_repo="https://repo1.maven.org/maven2" 52 | declare -r sbt_launch_mvn_snapshot_repo="https://repo.scala-sbt.org/scalasbt/maven-snapshots" 53 | 54 | declare -r default_jvm_opts_common="-Xms512m -Xss2m -XX:MaxInlineLevel=18" 55 | declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy -Dsbt.coursier.home=project/.coursier" 56 | 57 | declare sbt_jar sbt_dir sbt_create sbt_version sbt_script sbt_new 58 | declare sbt_explicit_version 59 | declare verbose noshare batch trace_level 60 | 61 | declare java_cmd="java" 62 | declare sbt_launch_dir="$HOME/.sbt/launchers" 63 | declare sbt_launch_repo 64 | 65 | # pull -J and -D options to give to java. 66 | declare -a java_args scalac_args sbt_commands residual_args 67 | 68 | # args to jvm/sbt via files or environment variables 69 | declare -a extra_jvm_opts extra_sbt_opts 70 | 71 | echoerr() { echo >&2 "$@"; } 72 | vlog() { [[ -n "$verbose" ]] && echoerr "$@"; } 73 | die() { 74 | echo "Aborting: $*" 75 | exit 1 76 | } 77 | 78 | setTrapExit() { 79 | # save stty and trap exit, to ensure echo is re-enabled if we are interrupted. 80 | SBT_STTY="$(stty -g 2>/dev/null)" 81 | export SBT_STTY 82 | 83 | # restore stty settings (echo in particular) 84 | onSbtRunnerExit() { 85 | [ -t 0 ] || return 86 | vlog "" 87 | vlog "restoring stty: $SBT_STTY" 88 | stty "$SBT_STTY" 89 | } 90 | 91 | vlog "saving stty: $SBT_STTY" 92 | trap onSbtRunnerExit EXIT 93 | } 94 | 95 | # this seems to cover the bases on OSX, and someone will 96 | # have to tell me about the others. 97 | get_script_path() { 98 | local path="$1" 99 | [[ -L "$path" ]] || { 100 | echo "$path" 101 | return 102 | } 103 | 104 | local -r target="$(readlink "$path")" 105 | if [[ "${target:0:1}" == "/" ]]; then 106 | echo "$target" 107 | else 108 | echo "${path%/*}/$target" 109 | fi 110 | } 111 | 112 | script_path="$(get_script_path "${BASH_SOURCE[0]}")" 113 | declare -r script_path 114 | script_name="${script_path##*/}" 115 | declare -r script_name 116 | 117 | init_default_option_file() { 118 | local overriding_var="${!1}" 119 | local default_file="$2" 120 | if [[ ! -r "$default_file" && "$overriding_var" =~ ^@(.*)$ ]]; then 121 | local envvar_file="${BASH_REMATCH[1]}" 122 | if [[ -r "$envvar_file" ]]; then 123 | default_file="$envvar_file" 124 | fi 125 | fi 126 | echo "$default_file" 127 | } 128 | 129 | sbt_opts_file="$(init_default_option_file SBT_OPTS .sbtopts)" 130 | sbtx_opts_file="$(init_default_option_file SBTX_OPTS .sbtxopts)" 131 | jvm_opts_file="$(init_default_option_file JVM_OPTS .jvmopts)" 132 | 133 | build_props_sbt() { 134 | [[ -r "$buildProps" ]] && 135 | grep '^sbt\.version' "$buildProps" | tr '=\r' ' ' | awk '{ print $2; }' 136 | } 137 | 138 | set_sbt_version() { 139 | sbt_version="${sbt_explicit_version:-$(build_props_sbt)}" 140 | [[ -n "$sbt_version" ]] || sbt_version=$sbt_release_version 141 | export sbt_version 142 | } 143 | 144 | url_base() { 145 | local version="$1" 146 | 147 | case "$version" in 148 | 0.7.*) echo "https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/simple-build-tool" ;; 149 | 0.10.*) echo "$sbt_launch_ivy_release_repo" ;; 150 | 0.11.[12]) echo "$sbt_launch_ivy_release_repo" ;; 151 | 0.*-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]) # ie "*-yyyymmdd-hhMMss" 152 | echo "$sbt_launch_ivy_snapshot_repo" ;; 153 | 0.*) echo "$sbt_launch_ivy_release_repo" ;; 154 | *-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]) # ie "*-yyyymmddThhMMss" 155 | echo "$sbt_launch_mvn_snapshot_repo" ;; 156 | *) echo "$sbt_launch_mvn_release_repo" ;; 157 | esac 158 | } 159 | 160 | make_url() { 161 | local version="$1" 162 | 163 | local base="${sbt_launch_repo:-$(url_base "$version")}" 164 | 165 | case "$version" in 166 | 0.7.*) echo "$base/sbt-launch-0.7.7.jar" ;; 167 | 0.10.*) echo "$base/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;; 168 | 0.11.[12]) echo "$base/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;; 169 | 0.*) echo "$base/org.scala-sbt/sbt-launch/$version/sbt-launch.jar" ;; 170 | *) echo "$base/org/scala-sbt/sbt-launch/$version/sbt-launch-${version}.jar" ;; 171 | esac 172 | } 173 | 174 | addJava() { 175 | vlog "[addJava] arg = '$1'" 176 | java_args+=("$1") 177 | } 178 | addSbt() { 179 | vlog "[addSbt] arg = '$1'" 180 | sbt_commands+=("$1") 181 | } 182 | addScalac() { 183 | vlog "[addScalac] arg = '$1'" 184 | scalac_args+=("$1") 185 | } 186 | addResidual() { 187 | vlog "[residual] arg = '$1'" 188 | residual_args+=("$1") 189 | } 190 | 191 | addResolver() { addSbt "set resolvers += $1"; } 192 | 193 | addDebugger() { addJava "-Xdebug" && addJava "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"; } 194 | 195 | setThisBuild() { 196 | vlog "[addBuild] args = '$*'" 197 | local key="$1" && shift 198 | addSbt "set $key in ThisBuild := $*" 199 | } 200 | setScalaVersion() { 201 | [[ "$1" == *"-SNAPSHOT" ]] && addResolver 'Resolver.sonatypeRepo("snapshots")' 202 | addSbt "++ $1" 203 | } 204 | setJavaHome() { 205 | java_cmd="$1/bin/java" 206 | setThisBuild javaHome "_root_.scala.Some(file(\"$1\"))" 207 | export JAVA_HOME="$1" 208 | export JDK_HOME="$1" 209 | export PATH="$JAVA_HOME/bin:$PATH" 210 | } 211 | 212 | getJavaVersion() { 213 | local -r str=$("$1" -version 2>&1 | grep -E -e '(java|openjdk) version' | awk '{ print $3 }' | tr -d '"') 214 | 215 | # java -version on java8 says 1.8.x 216 | # but on 9 and 10 it's 9.x.y and 10.x.y. 217 | if [[ "$str" =~ ^1\.([0-9]+)(\..*)?$ ]]; then 218 | echo "${BASH_REMATCH[1]}" 219 | # Fixes https://github.com/dwijnand/sbt-extras/issues/326 220 | elif [[ "$str" =~ ^([0-9]+)(\..*)?(-ea)?$ ]]; then 221 | echo "${BASH_REMATCH[1]}" 222 | elif [[ -n "$str" ]]; then 223 | echoerr "Can't parse java version from: $str" 224 | fi 225 | } 226 | 227 | checkJava() { 228 | # Warn if there is a Java version mismatch between PATH and JAVA_HOME/JDK_HOME 229 | 230 | [[ -n "$JAVA_HOME" && -e "$JAVA_HOME/bin/java" ]] && java="$JAVA_HOME/bin/java" 231 | [[ -n "$JDK_HOME" && -e "$JDK_HOME/lib/tools.jar" ]] && java="$JDK_HOME/bin/java" 232 | 233 | if [[ -n "$java" ]]; then 234 | pathJavaVersion=$(getJavaVersion java) 235 | homeJavaVersion=$(getJavaVersion "$java") 236 | if [[ "$pathJavaVersion" != "$homeJavaVersion" ]]; then 237 | echoerr "Warning: Java version mismatch between PATH and JAVA_HOME/JDK_HOME, sbt will use the one in PATH" 238 | echoerr " Either: fix your PATH, remove JAVA_HOME/JDK_HOME or use -java-home" 239 | echoerr " java version from PATH: $pathJavaVersion" 240 | echoerr " java version from JAVA_HOME/JDK_HOME: $homeJavaVersion" 241 | fi 242 | fi 243 | } 244 | 245 | java_version() { 246 | local -r version=$(getJavaVersion "$java_cmd") 247 | vlog "Detected Java version: $version" 248 | echo "$version" 249 | } 250 | 251 | is_apple_silicon() { [[ "$(uname -s)" == "Darwin" && "$(uname -m)" == "arm64" ]]; } 252 | 253 | # MaxPermSize critical on pre-8 JVMs but incurs noisy warning on 8+ 254 | default_jvm_opts() { 255 | local -r v="$(java_version)" 256 | if [[ $v -ge 17 ]]; then 257 | echo "$default_jvm_opts_common" 258 | elif [[ $v -ge 10 ]]; then 259 | if is_apple_silicon; then 260 | # As of Dec 2020, JVM for Apple Silicon (M1) doesn't support JVMCI 261 | echo "$default_jvm_opts_common" 262 | else 263 | echo "$default_jvm_opts_common -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler" 264 | fi 265 | elif [[ $v -ge 8 ]]; then 266 | echo "$default_jvm_opts_common" 267 | else 268 | echo "-XX:MaxPermSize=384m $default_jvm_opts_common" 269 | fi 270 | } 271 | 272 | execRunner() { 273 | # print the arguments one to a line, quoting any containing spaces 274 | vlog "# Executing command line:" && { 275 | for arg; do 276 | if [[ -n "$arg" ]]; then 277 | if printf "%s\n" "$arg" | grep -q ' '; then 278 | printf >&2 "\"%s\"\n" "$arg" 279 | else 280 | printf >&2 "%s\n" "$arg" 281 | fi 282 | fi 283 | done 284 | vlog "" 285 | } 286 | 287 | setTrapExit 288 | 289 | if [[ -n "$batch" ]]; then 290 | "$@" /dev/null 2>&1; then 312 | curl --fail --silent --location "$url" --output "$jar" 313 | elif command -v wget >/dev/null 2>&1; then 314 | wget -q -O "$jar" "$url" 315 | fi 316 | } && [[ -r "$jar" ]] 317 | } 318 | 319 | acquire_sbt_jar() { 320 | { 321 | sbt_jar="$(jar_file "$sbt_version")" 322 | [[ -r "$sbt_jar" ]] 323 | } || { 324 | sbt_jar="$HOME/.ivy2/local/org.scala-sbt/sbt-launch/$sbt_version/jars/sbt-launch.jar" 325 | [[ -r "$sbt_jar" ]] 326 | } || { 327 | sbt_jar="$(jar_file "$sbt_version")" 328 | jar_url="$(make_url "$sbt_version")" 329 | 330 | echoerr "Downloading sbt launcher for ${sbt_version}:" 331 | echoerr " From ${jar_url}" 332 | echoerr " To ${sbt_jar}" 333 | 334 | download_url "${jar_url}" "${sbt_jar}" 335 | 336 | case "${sbt_version}" in 337 | 0.*) 338 | vlog "SBT versions < 1.0 do not have published MD5 checksums, skipping check" 339 | echo "" 340 | ;; 341 | *) verify_sbt_jar "${sbt_jar}" ;; 342 | esac 343 | } 344 | } 345 | 346 | verify_sbt_jar() { 347 | local jar="${1}" 348 | local md5="${jar}.md5" 349 | md5url="$(make_url "${sbt_version}").md5" 350 | 351 | echoerr "Downloading sbt launcher ${sbt_version} md5 hash:" 352 | echoerr " From ${md5url}" 353 | echoerr " To ${md5}" 354 | 355 | download_url "${md5url}" "${md5}" >/dev/null 2>&1 356 | 357 | if command -v md5sum >/dev/null 2>&1; then 358 | if echo "$(cat "${md5}") ${jar}" | md5sum -c -; then 359 | rm -rf "${md5}" 360 | return 0 361 | else 362 | echoerr "Checksum does not match" 363 | return 1 364 | fi 365 | elif command -v md5 >/dev/null 2>&1; then 366 | if [ "$(md5 -q "${jar}")" == "$(cat "${md5}")" ]; then 367 | rm -rf "${md5}" 368 | return 0 369 | else 370 | echoerr "Checksum does not match" 371 | return 1 372 | fi 373 | elif command -v openssl >/dev/null 2>&1; then 374 | if [ "$(openssl md5 -r "${jar}" | awk '{print $1}')" == "$(cat "${md5}")" ]; then 375 | rm -rf "${md5}" 376 | return 0 377 | else 378 | echoerr "Checksum does not match" 379 | return 1 380 | fi 381 | else 382 | echoerr "Could not find an MD5 command" 383 | return 1 384 | fi 385 | } 386 | 387 | usage() { 388 | set_sbt_version 389 | cat < display stack traces with a max of frames (default: -1, traces suppressed) 402 | -debug-inc enable debugging log for the incremental compiler 403 | -no-colors disable ANSI color codes 404 | -sbt-create start sbt even if current directory contains no sbt project 405 | -sbt-dir path to global settings/plugins directory (default: ~/.sbt/) 406 | -sbt-boot path to shared boot directory (default: ~/.sbt/boot in 0.11+) 407 | -ivy path to local Ivy repository (default: ~/.ivy2) 408 | -no-share use all local caches; no sharing 409 | -offline put sbt in offline mode 410 | -jvm-debug Turn on JVM debugging, open at the given port. 411 | -batch Disable interactive mode 412 | -prompt Set the sbt prompt; in expr, 's' is the State and 'e' is Extracted 413 | -script Run the specified file as a scala script 414 | 415 | # sbt version (default: sbt.version from $buildProps if present, otherwise $sbt_release_version) 416 | -sbt-version use the specified version of sbt (default: $sbt_release_version) 417 | -sbt-force-latest force the use of the latest release of sbt: $sbt_release_version 418 | -sbt-dev use the latest pre-release version of sbt: $sbt_unreleased_version 419 | -sbt-jar use the specified jar as the sbt launcher 420 | -sbt-launch-dir directory to hold sbt launchers (default: $sbt_launch_dir) 421 | -sbt-launch-repo repo url for downloading sbt launcher jar (default: $(url_base "$sbt_version")) 422 | 423 | # scala version (default: as chosen by sbt) 424 | -28 use $latest_28 425 | -29 use $latest_29 426 | -210 use $latest_210 427 | -211 use $latest_211 428 | -212 use $latest_212 429 | -213 use $latest_213 430 | -scala-home use the scala build at the specified directory 431 | -scala-version use the specified version of scala 432 | -binary-version use the specified scala version when searching for dependencies 433 | 434 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) 435 | -java-home alternate JAVA_HOME 436 | 437 | # passing options to the jvm - note it does NOT use JAVA_OPTS due to pollution 438 | # The default set is used if JVM_OPTS is unset and no -jvm-opts file is found 439 | $(default_jvm_opts) 440 | JVM_OPTS environment variable holding either the jvm args directly, or 441 | the reference to a file containing jvm args if given path is prepended by '@' (e.g. '@/etc/jvmopts') 442 | Note: "@"-file is overridden by local '.jvmopts' or '-jvm-opts' argument. 443 | -jvm-opts file containing jvm args (if not given, .jvmopts in project root is used if present) 444 | -Dkey=val pass -Dkey=val directly to the jvm 445 | -J-X pass option -X directly to the jvm (-J is stripped) 446 | 447 | # passing options to sbt, OR to this runner 448 | SBT_OPTS environment variable holding either the sbt args directly, or 449 | the reference to a file containing sbt args if given path is prepended by '@' (e.g. '@/etc/sbtopts') 450 | Note: "@"-file is overridden by local '.sbtopts' or '-sbt-opts' argument. 451 | -sbt-opts file containing sbt args (if not given, .sbtopts in project root is used if present) 452 | -S-X add -X to sbt's scalacOptions (-S is stripped) 453 | 454 | # passing options exclusively to this runner 455 | SBTX_OPTS environment variable holding either the sbt-extras args directly, or 456 | the reference to a file containing sbt-extras args if given path is prepended by '@' (e.g. '@/etc/sbtxopts') 457 | Note: "@"-file is overridden by local '.sbtxopts' or '-sbtx-opts' argument. 458 | -sbtx-opts file containing sbt-extras args (if not given, .sbtxopts in project root is used if present) 459 | EOM 460 | exit 0 461 | } 462 | 463 | process_args() { 464 | require_arg() { 465 | local type="$1" 466 | local opt="$2" 467 | local arg="$3" 468 | 469 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 470 | die "$opt requires <$type> argument" 471 | fi 472 | } 473 | while [[ $# -gt 0 ]]; do 474 | case "$1" in 475 | -h | -help) usage ;; 476 | -v) verbose=true && shift ;; 477 | -d) addSbt "--debug" && shift ;; 478 | -w) addSbt "--warn" && shift ;; 479 | -q) addSbt "--error" && shift ;; 480 | -x) shift ;; # currently unused 481 | -trace) require_arg integer "$1" "$2" && trace_level="$2" && shift 2 ;; 482 | -debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;; 483 | 484 | -no-colors) addJava "-Dsbt.log.noformat=true" && addJava "-Dsbt.color=false" && shift ;; 485 | -sbt-create) sbt_create=true && shift ;; 486 | -sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;; 487 | -sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;; 488 | -ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;; 489 | -no-share) noshare=true && shift ;; 490 | -offline) addSbt "set offline in Global := true" && shift ;; 491 | -jvm-debug) require_arg port "$1" "$2" && addDebugger "$2" && shift 2 ;; 492 | -batch) batch=true && shift ;; 493 | -prompt) require_arg "expr" "$1" "$2" && setThisBuild shellPrompt "(s => { val e = Project.extract(s) ; $2 })" && shift 2 ;; 494 | -script) require_arg file "$1" "$2" && sbt_script="$2" && addJava "-Dsbt.main.class=sbt.ScriptMain" && shift 2 ;; 495 | 496 | -sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;; 497 | -sbt-force-latest) sbt_explicit_version="$sbt_release_version" && shift ;; 498 | -sbt-dev) sbt_explicit_version="$sbt_unreleased_version" && shift ;; 499 | -sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;; 500 | -sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;; 501 | -sbt-launch-repo) require_arg path "$1" "$2" && sbt_launch_repo="$2" && shift 2 ;; 502 | 503 | -28) setScalaVersion "$latest_28" && shift ;; 504 | -29) setScalaVersion "$latest_29" && shift ;; 505 | -210) setScalaVersion "$latest_210" && shift ;; 506 | -211) setScalaVersion "$latest_211" && shift ;; 507 | -212) setScalaVersion "$latest_212" && shift ;; 508 | -213) setScalaVersion "$latest_213" && shift ;; 509 | 510 | -scala-version) require_arg version "$1" "$2" && setScalaVersion "$2" && shift 2 ;; 511 | -binary-version) require_arg version "$1" "$2" && setThisBuild scalaBinaryVersion "\"$2\"" && shift 2 ;; 512 | -scala-home) require_arg path "$1" "$2" && setThisBuild scalaHome "_root_.scala.Some(file(\"$2\"))" && shift 2 ;; 513 | -java-home) require_arg path "$1" "$2" && setJavaHome "$2" && shift 2 ;; 514 | -sbt-opts) require_arg path "$1" "$2" && sbt_opts_file="$2" && shift 2 ;; 515 | -sbtx-opts) require_arg path "$1" "$2" && sbtx_opts_file="$2" && shift 2 ;; 516 | -jvm-opts) require_arg path "$1" "$2" && jvm_opts_file="$2" && shift 2 ;; 517 | 518 | -D*) addJava "$1" && shift ;; 519 | -J*) addJava "${1:2}" && shift ;; 520 | -S*) addScalac "${1:2}" && shift ;; 521 | 522 | new) sbt_new=true && : ${sbt_explicit_version:=$sbt_release_version} && addResidual "$1" && shift ;; 523 | 524 | *) addResidual "$1" && shift ;; 525 | esac 526 | done 527 | } 528 | 529 | # process the direct command line arguments 530 | process_args "$@" 531 | 532 | # skip #-styled comments and blank lines 533 | readConfigFile() { 534 | local end=false 535 | until $end; do 536 | read -r || end=true 537 | [[ $REPLY =~ ^# ]] || [[ -z $REPLY ]] || echo "$REPLY" 538 | done <"$1" 539 | } 540 | 541 | # if there are file/environment sbt_opts, process again so we 542 | # can supply args to this runner 543 | if [[ -r "$sbt_opts_file" ]]; then 544 | vlog "Using sbt options defined in file $sbt_opts_file" 545 | while read -r opt; do extra_sbt_opts+=("$opt"); done < <(readConfigFile "$sbt_opts_file") 546 | elif [[ -n "$SBT_OPTS" && ! ("$SBT_OPTS" =~ ^@.*) ]]; then 547 | vlog "Using sbt options defined in variable \$SBT_OPTS" 548 | IFS=" " read -r -a extra_sbt_opts <<<"$SBT_OPTS" 549 | else 550 | vlog "No extra sbt options have been defined" 551 | fi 552 | 553 | # if there are file/environment sbtx_opts, process again so we 554 | # can supply args to this runner 555 | if [[ -r "$sbtx_opts_file" ]]; then 556 | vlog "Using sbt options defined in file $sbtx_opts_file" 557 | while read -r opt; do extra_sbt_opts+=("$opt"); done < <(readConfigFile "$sbtx_opts_file") 558 | elif [[ -n "$SBTX_OPTS" && ! ("$SBTX_OPTS" =~ ^@.*) ]]; then 559 | vlog "Using sbt options defined in variable \$SBTX_OPTS" 560 | IFS=" " read -r -a extra_sbt_opts <<<"$SBTX_OPTS" 561 | else 562 | vlog "No extra sbt options have been defined" 563 | fi 564 | 565 | [[ -n "${extra_sbt_opts[*]}" ]] && process_args "${extra_sbt_opts[@]}" 566 | 567 | # reset "$@" to the residual args 568 | set -- "${residual_args[@]}" 569 | argumentCount=$# 570 | 571 | # set sbt version 572 | set_sbt_version 573 | 574 | checkJava 575 | 576 | # only exists in 0.12+ 577 | setTraceLevel() { 578 | case "$sbt_version" in 579 | "0.7."* | "0.10."* | "0.11."*) echoerr "Cannot set trace level in sbt version $sbt_version" ;; 580 | *) setThisBuild traceLevel "$trace_level" ;; 581 | esac 582 | } 583 | 584 | # set scalacOptions if we were given any -S opts 585 | [[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[*]}\"" 586 | 587 | [[ -n "$sbt_explicit_version" && -z "$sbt_new" ]] && addJava "-Dsbt.version=$sbt_explicit_version" 588 | vlog "Detected sbt version $sbt_version" 589 | 590 | if [[ -n "$sbt_script" ]]; then 591 | residual_args=("$sbt_script" "${residual_args[@]}") 592 | else 593 | # no args - alert them there's stuff in here 594 | ((argumentCount > 0)) || { 595 | vlog "Starting $script_name: invoke with -help for other options" 596 | residual_args=(shell) 597 | } 598 | fi 599 | 600 | # verify this is an sbt dir, -create was given or user attempts to run a scala script 601 | [[ -r ./build.sbt || -d ./project || -n "$sbt_create" || -n "$sbt_script" || -n "$sbt_new" ]] || { 602 | cat < String = () => "Hello World!" 13 | ) derives ServiceSchema.SemiAuto 14 | 15 | case class User( 16 | id: Int, 17 | name: String, 18 | username: String, 19 | email: String, 20 | phone: Option[String], 21 | website: Option[String] 22 | ) derives ServiceSchema.SemiAuto 23 | 24 | case class Post( 25 | userId: Int, 26 | id: Int, 27 | title: String, 28 | body: String 29 | ) derives ServiceSchema.SemiAuto { 30 | @GQLField def user: RQuery[Service, User] = Service.user(userId) 31 | } 32 | -------------------------------------------------------------------------------- /graphql/caliban/src/main/scala/Client.scala: -------------------------------------------------------------------------------- 1 | import com.github.plokhotnyuk.jsoniter_scala.core.* 2 | import org.apache.hc.client5.http.async.methods.* 3 | import org.apache.hc.client5.http.impl.async.{CloseableHttpAsyncClient, HttpAsyncClients} 4 | import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder 5 | import org.apache.hc.core5.concurrent.FutureCallback 6 | import org.apache.hc.core5.http.HttpHost 7 | import zio.* 8 | 9 | import java.net.* 10 | 11 | trait Client { 12 | def get[A](url: URI)(using JsonValueCodec[A]): Task[A] 13 | } 14 | 15 | object Client { 16 | private final class Live(client: CloseableHttpAsyncClient) extends Client { 17 | 18 | private def mkCallback[A](cb: ZIO[Any, Throwable, A] => Unit)(using JsonValueCodec[A], Trace) = 19 | new FutureCallback[SimpleHttpResponse] { 20 | def completed(result: SimpleHttpResponse): Unit = cb(ZIO.attempt(readFromArray[A](result.getBodyBytes))) 21 | def failed(ex: Exception): Unit = cb(ZIO.fail(ex)) 22 | def cancelled(): Unit = cb(ZIO.fail(new InterruptedException("cancelled"))) 23 | } 24 | 25 | def get[A](uri: URI)(using JsonValueCodec[A]): Task[A] = 26 | ZIO.async[Any, Throwable, A] { cb => 27 | client.execute( 28 | SimpleRequestProducer.create(SimpleRequestBuilder.get().setUri(uri).build), 29 | SimpleResponseConsumer.create(), 30 | mkCallback(cb) 31 | ) 32 | } 33 | } 34 | 35 | private val httpClient: TaskLayer[CloseableHttpAsyncClient] = ZLayer.scoped( 36 | ZIO.fromAutoCloseable( 37 | ZIO 38 | .succeed { 39 | HttpAsyncClients 40 | .custom() 41 | .setProxy(new HttpHost("http", "127.0.0.1", 3000)) 42 | .setConnectionManager({ 43 | PoolingAsyncClientConnectionManagerBuilder 44 | .create() 45 | .setMaxConnTotal(2000) 46 | .setMaxConnPerRoute(200) 47 | .build() 48 | }) 49 | .build() 50 | } 51 | .tap(c => ZIO.succeed(c.start())) 52 | ) 53 | ) 54 | 55 | val live: TaskLayer[Client] = ZLayer.make[Client](httpClient, ZLayer.derive[Live]) 56 | } 57 | -------------------------------------------------------------------------------- /graphql/caliban/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | import caliban.* 2 | import caliban.execution.QueryExecution 3 | import caliban.quick.* 4 | import zio.* 5 | 6 | object Main extends ZIOAppDefault { 7 | override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = 8 | Runtime.removeDefaultLoggers ++ Runtime.disableFlags(RuntimeFlag.FiberRoots) 9 | 10 | private val api = graphQL(RootResolver(Query(Service.posts))) 11 | def run = 12 | api 13 | .runServer(8000, apiPath = "/graphql") 14 | .provide(Service.layer, Client.live, ZLayer.scoped(Configurator.setQueryExecution(QueryExecution.Batched))) 15 | } 16 | -------------------------------------------------------------------------------- /graphql/caliban/src/main/scala/Service.scala: -------------------------------------------------------------------------------- 1 | import com.github.plokhotnyuk.jsoniter_scala.core.* 2 | import com.github.plokhotnyuk.jsoniter_scala.macros.* 3 | import zio.query.* 4 | import zio.{RIO, RLayer, Task, ZIO, ZLayer} 5 | 6 | import java.net.URI 7 | 8 | trait Service { 9 | val posts: Task[List[Post]] 10 | def user(id: Int): TaskQuery[User] 11 | } 12 | 13 | object Service { 14 | val posts: RIO[Service, List[Post]] = ZIO.serviceWithZIO(_.posts) 15 | def user(id: Int): RQuery[Service, User] = ZQuery.serviceWithQuery(_.user(id)) 16 | 17 | val layer: RLayer[Client, Service] = ZLayer.derive[Live] 18 | 19 | private class Live(client: Client) extends Service { 20 | private inline val BaseUrl = "http://jsonplaceholder.typicode.com" 21 | 22 | val posts: Task[List[Post]] = { 23 | val uri = URI.create(BaseUrl + "/posts") 24 | client.get[List[Post]](uri) 25 | } 26 | 27 | def user(id: Int): TaskQuery[User] = 28 | UsersDataSource.get(id) 29 | 30 | private object UsersDataSource { 31 | def get(id: Int): TaskQuery[User] = ZQuery.fromRequest(Req(id))(usersDS) 32 | 33 | private case class Req(id: Int) extends Request[Throwable, User] 34 | 35 | private val usersDS = DataSource.fromFunctionZIO("UsersDataSource") { (req: Req) => 36 | client.get[User](URI.create(BaseUrl + "/users/" + req.id)) 37 | } 38 | 39 | private given JsonValueCodec[User] = JsonCodecMaker.make(CodecMakerConfig.withDecodingOnly(true)) 40 | } 41 | 42 | private given JsonValueCodec[List[Post]] = JsonCodecMaker.make(CodecMakerConfig.withDecodingOnly(true)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /graphql/gqlgen/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pwd 3 | cd graphql/gqlgen 4 | pwd 5 | go build -o main main.go -------------------------------------------------------------------------------- /graphql/gqlgen/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/gqlgen-users 2 | 3 | go 1.22.5 4 | 5 | toolchain go1.23.5 6 | 7 | require ( 8 | github.com/99designs/gqlgen v0.17.64 9 | github.com/vektah/gqlparser/v2 v2.5.22 10 | github.com/vikstrous/dataloadgen v0.0.6 11 | ) 12 | 13 | require ( 14 | github.com/agnivade/levenshtein v1.2.0 // indirect 15 | github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect 16 | github.com/go-viper/mapstructure/v2 v2.2.1 // indirect 17 | github.com/google/uuid v1.6.0 // indirect 18 | github.com/gorilla/websocket v1.5.0 // indirect 19 | github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect 20 | github.com/mitchellh/mapstructure v1.5.0 // indirect 21 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 22 | github.com/sosodev/duration v1.3.1 // indirect 23 | github.com/urfave/cli/v2 v2.27.5 // indirect 24 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect 25 | go.opentelemetry.io/otel v1.27.0 // indirect 26 | go.opentelemetry.io/otel/trace v1.27.0 // indirect 27 | golang.org/x/mod v0.20.0 // indirect 28 | golang.org/x/sync v0.10.0 // indirect 29 | golang.org/x/text v0.21.0 // indirect 30 | golang.org/x/tools v0.24.0 // indirect 31 | gopkg.in/yaml.v3 v3.0.1 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /graphql/gqlgen/go.sum: -------------------------------------------------------------------------------- 1 | github.com/99designs/gqlgen v0.17.49 h1:b3hNGexHd33fBSAd4NDT/c3NCcQzcAVkknhN9ym36YQ= 2 | github.com/99designs/gqlgen v0.17.49/go.mod h1:tC8YFVZMed81x7UJ7ORUwXF4Kn6SXuucFqQBhN8+BU0= 3 | github.com/99designs/gqlgen v0.17.50 h1:c0CggpVl4Eh9jDU8QVB/PRqwDBrJzd2RQYZCO7e+ZRs= 4 | github.com/99designs/gqlgen v0.17.50/go.mod h1:77/+pVe6zlTsz++oUg2m8VLgzdUPHxjoAG3BxI5y8Rc= 5 | github.com/99designs/gqlgen v0.17.51 h1:KHLvUckplsZi14Zv1JdHkemXSkulksN/Dwe7VflePSQ= 6 | github.com/99designs/gqlgen v0.17.51/go.mod h1:77/+pVe6zlTsz++oUg2m8VLgzdUPHxjoAG3BxI5y8Rc= 7 | github.com/99designs/gqlgen v0.17.53 h1:FJOJaF96d7Y5EBpoaLG96fz1NR6B8bFdCZI1yZwYArM= 8 | github.com/99designs/gqlgen v0.17.53/go.mod h1:77/+pVe6zlTsz++oUg2m8VLgzdUPHxjoAG3BxI5y8Rc= 9 | github.com/99designs/gqlgen v0.17.54 h1:AsF49k/7RJlwA00RQYsYN0T8cQuaosnV/7G1dHC3Uh8= 10 | github.com/99designs/gqlgen v0.17.54/go.mod h1:77/+pVe6zlTsz++oUg2m8VLgzdUPHxjoAG3BxI5y8Rc= 11 | github.com/99designs/gqlgen v0.17.55 h1:3vzrNWYyzSZjGDFo68e5j9sSauLxfKvLp+6ioRokVtM= 12 | github.com/99designs/gqlgen v0.17.55/go.mod h1:3Bq768f8hgVPGZxL8aY9MaYmbxa6llPM/qu1IGH1EJo= 13 | github.com/99designs/gqlgen v0.17.56 h1:+J42ARAHvnysH6klO9Wq+tCsGF32cpAgU3SyF0VRJtI= 14 | github.com/99designs/gqlgen v0.17.56/go.mod h1:rmB6vLvtL8uf9F9w0/irJ5alBkD8DJvj35ET31BKbtY= 15 | github.com/99designs/gqlgen v0.17.57 h1:Ak4p60BRq6QibxY0lEc0JnQhDurfhxA67sp02lMjmPc= 16 | github.com/99designs/gqlgen v0.17.57/go.mod h1:Jx61hzOSTcR4VJy/HFIgXiQ5rJ0Ypw8DxWLjbYDAUw0= 17 | github.com/99designs/gqlgen v0.17.58 h1:IqoOcSLMk6LnKvLECkRNPP5Z/jApXYvnzm+qvGkYdhc= 18 | github.com/99designs/gqlgen v0.17.58/go.mod h1:vQJzWXyGya2TYL7cig1G4OaCQzyck031MgYBlUwaI9I= 19 | github.com/99designs/gqlgen v0.17.59 h1:1hvrBXMlHrnAFZIIHtT049Y2FnlntPESuwRi1SygLqQ= 20 | github.com/99designs/gqlgen v0.17.59/go.mod h1:vQJzWXyGya2TYL7cig1G4OaCQzyck031MgYBlUwaI9I= 21 | github.com/99designs/gqlgen v0.17.60 h1:xxl7kQDCNw79itzWQtCUSXgkovCyq9r+ogSXfZpKPYM= 22 | github.com/99designs/gqlgen v0.17.60/go.mod h1:vQJzWXyGya2TYL7cig1G4OaCQzyck031MgYBlUwaI9I= 23 | github.com/99designs/gqlgen v0.17.61 h1:vE7xLRC066n9wehgjeplILOWtwz75zbzcV2/Iv9i3pw= 24 | github.com/99designs/gqlgen v0.17.61/go.mod h1:rFU1T3lhv/tPeAlww/DJ4ol2YxT/pPpue+xxPbkd3r4= 25 | github.com/99designs/gqlgen v0.17.62 h1:Wovt1+XJN9dTWYh92537Y9a5FuMVSkrQL4bn0a8v5Rg= 26 | github.com/99designs/gqlgen v0.17.62/go.mod h1:sVCM2iwIZisJjTI/DEC3fpH+HFgxY1496ZJ+jbT9IjA= 27 | github.com/99designs/gqlgen v0.17.63 h1:HCdaYDPd9HqUXRchEvmE3EFzELRwLlaJ8DBuyC8Cqto= 28 | github.com/99designs/gqlgen v0.17.63/go.mod h1:sVCM2iwIZisJjTI/DEC3fpH+HFgxY1496ZJ+jbT9IjA= 29 | github.com/99designs/gqlgen v0.17.64 h1:BzpqO5ofQXyy2XOa93Q6fP1BHLRjTOeU35ovTEsbYlw= 30 | github.com/99designs/gqlgen v0.17.64/go.mod h1:kaxLetFxPGeBBwiuKk75NxuI1fe9HRvob17In74v/Zc= 31 | github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= 32 | github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= 33 | github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= 34 | github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= 35 | github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= 36 | github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= 37 | github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= 38 | github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= 39 | github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= 40 | github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= 41 | github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= 42 | github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= 43 | github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= 44 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 45 | github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= 46 | github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 47 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 48 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 49 | github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= 50 | github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= 51 | github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= 52 | github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 53 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 54 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 55 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 56 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 57 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 58 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 59 | github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= 60 | github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 61 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 62 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 63 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 64 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 65 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 66 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 67 | github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= 68 | github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= 69 | github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= 70 | github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= 71 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 72 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 73 | github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= 74 | github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= 75 | github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= 76 | github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= 77 | github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= 78 | github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= 79 | github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8= 80 | github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww= 81 | github.com/vektah/gqlparser/v2 v2.5.17 h1:9At7WblLV7/36nulgekUgIaqHZWn5hxqluxrxGUhOmI= 82 | github.com/vektah/gqlparser/v2 v2.5.17/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww= 83 | github.com/vektah/gqlparser/v2 v2.5.18 h1:zSND3GtutylAQ1JpWnTHcqtaRZjl+y3NROeW8vuNo6Y= 84 | github.com/vektah/gqlparser/v2 v2.5.18/go.mod h1:6HLzf7JKv9Fi3APymudztFQNmLXR5qJeEo6BOFcXVfc= 85 | github.com/vektah/gqlparser/v2 v2.5.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egplt7iSg= 86 | github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U= 87 | github.com/vektah/gqlparser/v2 v2.5.20 h1:kPaWbhBntxoZPaNdBaIPT1Kh0i1b/onb5kXgEdP5JCo= 88 | github.com/vektah/gqlparser/v2 v2.5.20/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM= 89 | github.com/vektah/gqlparser/v2 v2.5.21 h1:Zw1rG2dr1pRR4wqwbVq4d6+xk2f4ut/yo+hwr4QjE08= 90 | github.com/vektah/gqlparser/v2 v2.5.21/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM= 91 | github.com/vektah/gqlparser/v2 v2.5.22 h1:yaaeJ0fu+nv1vUMW0Hl+aS1eiv1vMfapBNjpffAda1I= 92 | github.com/vektah/gqlparser/v2 v2.5.22/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM= 93 | github.com/vikstrous/dataloadgen v0.0.6 h1:A7s/fI3QNnH80CA9vdNbWK7AsbLjIxNHpZnV+VnOT1s= 94 | github.com/vikstrous/dataloadgen v0.0.6/go.mod h1:8vuQVpBH0ODbMKAPUdCAPcOGezoTIhgAjgex51t4vbg= 95 | github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= 96 | github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= 97 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= 98 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= 99 | go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= 100 | go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= 101 | go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= 102 | go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= 103 | golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= 104 | golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 105 | golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= 106 | golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 107 | golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= 108 | golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= 109 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 110 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 111 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 112 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 113 | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= 114 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 115 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 116 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 117 | golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= 118 | golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 119 | golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= 120 | golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 121 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 122 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 123 | golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= 124 | golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 125 | golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= 126 | golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= 127 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 128 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 129 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 130 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 131 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 132 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 133 | -------------------------------------------------------------------------------- /graphql/gqlgen/gqlgen.yml: -------------------------------------------------------------------------------- 1 | # Where are all the schema files located? globs are supported eg src/**/*.graphqls 2 | schema: 3 | - graph/*.graphqls 4 | 5 | # Where should the generated server code go? 6 | exec: 7 | filename: graph/generated.go 8 | package: graph 9 | 10 | # Uncomment to enable federation 11 | # federation: 12 | # filename: graph/federation.go 13 | # package: graph 14 | 15 | # Where should any generated models go? 16 | model: 17 | filename: graph/model/models_gen.go 18 | package: model 19 | 20 | # Where should the resolver implementations go? 21 | resolver: 22 | layout: follow-schema 23 | dir: graph 24 | package: graph 25 | filename_template: "{name}.resolvers.go" 26 | # Optional: turn on to not generate template comments above resolvers 27 | # omit_template_comment: false 28 | 29 | # Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models 30 | # struct_tag: json 31 | 32 | # Optional: turn on to use []Thing instead of []*Thing 33 | # omit_slice_element_pointers: false 34 | 35 | # Optional: turn on to omit Is() methods to interface and unions 36 | # omit_interface_checks : true 37 | 38 | # Optional: turn on to skip generation of ComplexityRoot struct content and Complexity function 39 | # omit_complexity: false 40 | 41 | # Optional: turn on to not generate any file notice comments in generated files 42 | # omit_gqlgen_file_notice: false 43 | 44 | # Optional: turn on to exclude the gqlgen version in the generated file notice. No effect if `omit_gqlgen_file_notice` is true. 45 | # omit_gqlgen_version_in_file_notice: false 46 | 47 | # Optional: turn off to make struct-type struct fields not use pointers 48 | # e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing } 49 | # struct_fields_always_pointers: true 50 | 51 | # Optional: turn off to make resolvers return values instead of pointers for structs 52 | # resolvers_always_return_pointers: true 53 | 54 | # Optional: turn on to return pointers instead of values in unmarshalInput 55 | # return_pointers_in_unmarshalinput: false 56 | 57 | # Optional: wrap nullable input fields with Omittable 58 | # nullable_input_omittable: true 59 | 60 | # Optional: set to speed up generation time by not performing a final validation pass. 61 | # skip_validation: true 62 | 63 | # Optional: set to skip running `go mod tidy` when generating server code 64 | # skip_mod_tidy: true 65 | 66 | # gqlgen will search for any type names in the schema in these go packages 67 | # if they match it will use them, otherwise it will generate them. 68 | autobind: 69 | # - "example.com/gqlgen-users/graph/model" 70 | 71 | # This section declares type mapping between the GraphQL and go type systems 72 | # 73 | # The first line in each type will be used as defaults for resolver arguments and 74 | # modelgen, the others will be allowed when binding to fields. Configure them to 75 | # your liking 76 | models: 77 | ID: 78 | model: 79 | - github.com/99designs/gqlgen/graphql.ID 80 | - github.com/99designs/gqlgen/graphql.Int 81 | - github.com/99designs/gqlgen/graphql.Int64 82 | - github.com/99designs/gqlgen/graphql.Int32 83 | Int: 84 | model: 85 | - github.com/99designs/gqlgen/graphql.Int 86 | - github.com/99designs/gqlgen/graphql.Int64 87 | - github.com/99designs/gqlgen/graphql.Int32 88 | User: 89 | fields: 90 | posts: 91 | resolver: true 92 | -------------------------------------------------------------------------------- /graphql/gqlgen/graph/model/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | type Post struct { 6 | UserID int `json:"userId"` 7 | ID int `json:"id"` 8 | Title string `json:"title"` 9 | Body string `json:"body"` 10 | User *User `json:"user,omitempty"` 11 | } 12 | 13 | type Query struct { 14 | } 15 | 16 | type User struct { 17 | ID int `json:"id"` 18 | Name string `json:"name"` 19 | Username string `json:"username"` 20 | Email string `json:"email"` 21 | Posts []*Post `json:"posts"` 22 | } 23 | -------------------------------------------------------------------------------- /graphql/gqlgen/graph/resolver.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will not be regenerated automatically. 4 | // 5 | // It serves as dependency injection for your app, add any dependencies you require here. 6 | 7 | type Resolver struct{} 8 | -------------------------------------------------------------------------------- /graphql/gqlgen/graph/schema.graphqls: -------------------------------------------------------------------------------- 1 | # GraphQL schema example 2 | # 3 | # https://gqlgen.com/getting-started/ 4 | 5 | type User { 6 | id: Int! 7 | name: String! 8 | username: String! 9 | email: String! 10 | posts: [Post!]! 11 | } 12 | 13 | type Post { 14 | userId: Int! 15 | id: Int! 16 | title: String! 17 | body: String! 18 | user: User 19 | } 20 | 21 | type Query { 22 | users: [User!]! 23 | posts: [Post!]! 24 | greet: String! 25 | } 26 | 27 | -------------------------------------------------------------------------------- /graphql/gqlgen/graph/schema.resolvers.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | // Code generated by github.com/99designs/gqlgen version v0.17.49 6 | 7 | import ( 8 | "bufio" 9 | "context" 10 | "encoding/json" 11 | "fmt" 12 | "net/http" 13 | "net/url" 14 | "sync" 15 | "time" 16 | 17 | "example.com/gqlgen-users/graph/model" 18 | "github.com/99designs/gqlgen/graphql" 19 | "github.com/vikstrous/dataloadgen" 20 | ) 21 | 22 | func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) { 23 | resp, err := fetchFromJSONPlaceholder("/users") 24 | if err != nil { 25 | return nil, err 26 | } 27 | defer resp.Body.Close() 28 | 29 | var users []*model.User 30 | if err := decodeResponse(resp, &users); err != nil { 31 | return nil, err 32 | } 33 | 34 | return users, nil 35 | } 36 | 37 | func batchUsers(ctx context.Context, keys []string) (results []*model.User, errors []error) { 38 | var wg sync.WaitGroup 39 | var mu sync.Mutex 40 | wg.Add(len(keys)) 41 | 42 | for _, key := range keys { 43 | go func(key string) { 44 | defer wg.Done() 45 | resp, err := fetchFromJSONPlaceholder(fmt.Sprintf("/users/%s", key)) 46 | if err != nil { 47 | mu.Lock() 48 | errors = append(errors, err) 49 | mu.Unlock() 50 | return 51 | } 52 | defer resp.Body.Close() 53 | 54 | var user model.User 55 | if err := decodeResponse(resp, &user); err != nil { 56 | mu.Lock() 57 | errors = append(errors, err) 58 | mu.Unlock() 59 | return 60 | } 61 | 62 | mu.Lock() 63 | results = append(results, &user) 64 | mu.Unlock() 65 | }(key) 66 | } 67 | wg.Wait() 68 | return results, errors 69 | } 70 | 71 | func (r *queryResolver) Posts(ctx context.Context) ([]*model.Post, error) { 72 | resp, err := fetchFromJSONPlaceholder("/posts") 73 | if err != nil { 74 | return nil, err 75 | } 76 | defer resp.Body.Close() 77 | 78 | var posts []*model.Post 79 | if err := decodeResponse(resp, &posts); err != nil { 80 | return nil, err 81 | } 82 | 83 | fields := graphql.CollectAllFields(ctx) 84 | 85 | for _, field := range fields { 86 | if field == "user" { 87 | var userLoader = dataloadgen.NewLoader(batchUsers, dataloadgen.WithWait(time.Millisecond)) 88 | for _, post := range posts { 89 | func(post *model.Post) { 90 | user, err := userLoader.Load(ctx, fmt.Sprintf("%d", post.UserID)) 91 | if err != nil { 92 | return 93 | } 94 | post.User = user 95 | }(post) 96 | } 97 | break 98 | } 99 | } 100 | 101 | return posts, nil 102 | } 103 | 104 | func (r *queryResolver) Greet(ctx context.Context) (string, error) { 105 | return "Hello World!", nil 106 | } 107 | 108 | func (r *userResolver) Posts(ctx context.Context, obj *model.User) ([]*model.Post, error) { 109 | resp, err := fetchFromJSONPlaceholder(fmt.Sprintf("/users/%d/posts", obj.ID)) 110 | if err != nil { 111 | return nil, err 112 | } 113 | defer resp.Body.Close() 114 | 115 | var posts []*model.Post 116 | if err := decodeResponse(resp, &posts); err != nil { 117 | return nil, err 118 | } 119 | 120 | return posts, nil 121 | } 122 | 123 | // Query returns QueryResolver implementation. 124 | func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } 125 | 126 | // User returns UserResolver implementation. 127 | func (r *Resolver) User() UserResolver { return &userResolver{r} } 128 | 129 | type queryResolver struct{ *Resolver } 130 | type userResolver struct{ *Resolver } 131 | 132 | var proxyURL = &url.URL{ 133 | Scheme: "http", // or "https" if your proxy is using HTTPS 134 | Host: "127.0.0.1:3000", 135 | } 136 | var client = &http.Client{ 137 | Transport: &http.Transport{ 138 | Proxy: http.ProxyURL(proxyURL), 139 | MaxIdleConns: 1000, // Set the total max idle connections 140 | MaxIdleConnsPerHost: 200, // Set the max idle connections per host 141 | IdleConnTimeout: 30 * time.Second, 142 | }, 143 | } 144 | 145 | func fetchFromJSONPlaceholder(endpoint string) (*http.Response, error) { 146 | req, err := http.NewRequest("GET", "http://jsonplaceholder.typicode.com"+endpoint, nil) 147 | if err != nil { 148 | return nil, err 149 | } 150 | return client.Do(req) 151 | } 152 | func decodeResponse(resp *http.Response, v interface{}) error { 153 | // Use a buffered reader to optimize JSON decoding 154 | reader := bufio.NewReader(resp.Body) 155 | return json.NewDecoder(reader).Decode(v) 156 | } 157 | -------------------------------------------------------------------------------- /graphql/gqlgen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "os" 7 | 8 | "example.com/gqlgen-users/graph" 9 | "github.com/99designs/gqlgen/graphql/handler" 10 | "github.com/99designs/gqlgen/graphql/playground" 11 | ) 12 | 13 | const defaultPort = "8000" 14 | 15 | func main() { 16 | port := os.Getenv("PORT") 17 | if port == "" { 18 | port = defaultPort 19 | } 20 | 21 | srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}})) 22 | 23 | http.Handle("/graphiql", playground.Handler("GraphQL playground", "/graphql")) 24 | http.Handle("/graphql", srv) 25 | 26 | log.Printf("connect to http://localhost:%s/graphiql for GraphQL playground", port) 27 | log.Fatal(http.ListenAndServe(":"+port, nil)) 28 | } 29 | -------------------------------------------------------------------------------- /graphql/gqlgen/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pwd 3 | cd graphql/gqlgen 4 | pwd 5 | ./main -------------------------------------------------------------------------------- /graphql/gqlgen/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # For gqlgen: 4 | cd graphql/gqlgen 5 | go build -o main main.go -------------------------------------------------------------------------------- /graphql/gqlgen/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | import ( 7 | _ "github.com/99designs/gqlgen" 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /graphql/graphql_jit/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql_jit", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "graphql_jit", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@graphql-tools/schema": "^10.0.4", 13 | "axios": "^1.7.2", 14 | "dataloader": "^2.2.2", 15 | "express": "^4.19.2", 16 | "graphql": "^15.9.0", 17 | "graphql-jit": "^0.8.6" 18 | } 19 | }, 20 | "node_modules/@fastify/merge-json-schemas": { 21 | "version": "0.1.1", 22 | "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", 23 | "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", 24 | "dependencies": { 25 | "fast-deep-equal": "^3.1.3" 26 | } 27 | }, 28 | "node_modules/@graphql-tools/merge": { 29 | "version": "9.0.17", 30 | "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.17.tgz", 31 | "integrity": "sha512-3K4g8KKbIqfdmK0L5+VtZsqwAeElPkvT5ejiH+KEhn2wyKNCi4HYHxpQk8xbu+dSwLlm9Lhet1hylpo/mWCkuQ==", 32 | "license": "MIT", 33 | "dependencies": { 34 | "@graphql-tools/utils": "^10.7.2", 35 | "tslib": "^2.4.0" 36 | }, 37 | "engines": { 38 | "node": ">=16.0.0" 39 | }, 40 | "peerDependencies": { 41 | "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 42 | } 43 | }, 44 | "node_modules/@graphql-tools/schema": { 45 | "version": "10.0.16", 46 | "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.16.tgz", 47 | "integrity": "sha512-G2zgb8hNg9Sx6Z2FSXm57ToNcwMls9A9cUm+EsCrnGGDsryzN5cONYePUpSGj5NCFivVp3o1FT5dg19P/1qeqQ==", 48 | "license": "MIT", 49 | "dependencies": { 50 | "@graphql-tools/merge": "^9.0.17", 51 | "@graphql-tools/utils": "^10.7.2", 52 | "tslib": "^2.4.0", 53 | "value-or-promise": "^1.0.12" 54 | }, 55 | "engines": { 56 | "node": ">=16.0.0" 57 | }, 58 | "peerDependencies": { 59 | "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 60 | } 61 | }, 62 | "node_modules/@graphql-tools/utils": { 63 | "version": "10.7.2", 64 | "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.7.2.tgz", 65 | "integrity": "sha512-Wn85S+hfkzfVFpXVrQ0hjnePa3p28aB6IdAGCiD1SqBCSMDRzL+OFEtyAyb30nV9Mqflqs9lCqjqlR2puG857Q==", 66 | "license": "MIT", 67 | "dependencies": { 68 | "@graphql-typed-document-node/core": "^3.1.1", 69 | "cross-inspect": "1.0.1", 70 | "dset": "^3.1.4", 71 | "tslib": "^2.4.0" 72 | }, 73 | "engines": { 74 | "node": ">=16.0.0" 75 | }, 76 | "peerDependencies": { 77 | "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 78 | } 79 | }, 80 | "node_modules/@graphql-typed-document-node/core": { 81 | "version": "3.2.0", 82 | "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", 83 | "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", 84 | "peerDependencies": { 85 | "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" 86 | } 87 | }, 88 | "node_modules/accepts": { 89 | "version": "1.3.8", 90 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 91 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 92 | "dependencies": { 93 | "mime-types": "~2.1.34", 94 | "negotiator": "0.6.3" 95 | }, 96 | "engines": { 97 | "node": ">= 0.6" 98 | } 99 | }, 100 | "node_modules/ajv": { 101 | "version": "8.16.0", 102 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", 103 | "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", 104 | "dependencies": { 105 | "fast-deep-equal": "^3.1.3", 106 | "json-schema-traverse": "^1.0.0", 107 | "require-from-string": "^2.0.2", 108 | "uri-js": "^4.4.1" 109 | }, 110 | "funding": { 111 | "type": "github", 112 | "url": "https://github.com/sponsors/epoberezkin" 113 | } 114 | }, 115 | "node_modules/ajv-formats": { 116 | "version": "3.0.1", 117 | "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", 118 | "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", 119 | "dependencies": { 120 | "ajv": "^8.0.0" 121 | }, 122 | "peerDependencies": { 123 | "ajv": "^8.0.0" 124 | }, 125 | "peerDependenciesMeta": { 126 | "ajv": { 127 | "optional": true 128 | } 129 | } 130 | }, 131 | "node_modules/array-flatten": { 132 | "version": "1.1.1", 133 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 134 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 135 | }, 136 | "node_modules/asynckit": { 137 | "version": "0.4.0", 138 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 139 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 140 | }, 141 | "node_modules/axios": { 142 | "version": "1.7.9", 143 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", 144 | "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", 145 | "license": "MIT", 146 | "dependencies": { 147 | "follow-redirects": "^1.15.6", 148 | "form-data": "^4.0.0", 149 | "proxy-from-env": "^1.1.0" 150 | } 151 | }, 152 | "node_modules/body-parser": { 153 | "version": "1.20.3", 154 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", 155 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", 156 | "license": "MIT", 157 | "dependencies": { 158 | "bytes": "3.1.2", 159 | "content-type": "~1.0.5", 160 | "debug": "2.6.9", 161 | "depd": "2.0.0", 162 | "destroy": "1.2.0", 163 | "http-errors": "2.0.0", 164 | "iconv-lite": "0.4.24", 165 | "on-finished": "2.4.1", 166 | "qs": "6.13.0", 167 | "raw-body": "2.5.2", 168 | "type-is": "~1.6.18", 169 | "unpipe": "1.0.0" 170 | }, 171 | "engines": { 172 | "node": ">= 0.8", 173 | "npm": "1.2.8000 || >= 1.4.16" 174 | } 175 | }, 176 | "node_modules/bytes": { 177 | "version": "3.1.2", 178 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 179 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 180 | "engines": { 181 | "node": ">= 0.8" 182 | } 183 | }, 184 | "node_modules/call-bind": { 185 | "version": "1.0.7", 186 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 187 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 188 | "dependencies": { 189 | "es-define-property": "^1.0.0", 190 | "es-errors": "^1.3.0", 191 | "function-bind": "^1.1.2", 192 | "get-intrinsic": "^1.2.4", 193 | "set-function-length": "^1.2.1" 194 | }, 195 | "engines": { 196 | "node": ">= 0.4" 197 | }, 198 | "funding": { 199 | "url": "https://github.com/sponsors/ljharb" 200 | } 201 | }, 202 | "node_modules/combined-stream": { 203 | "version": "1.0.8", 204 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 205 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 206 | "dependencies": { 207 | "delayed-stream": "~1.0.0" 208 | }, 209 | "engines": { 210 | "node": ">= 0.8" 211 | } 212 | }, 213 | "node_modules/content-disposition": { 214 | "version": "0.5.4", 215 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 216 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 217 | "dependencies": { 218 | "safe-buffer": "5.2.1" 219 | }, 220 | "engines": { 221 | "node": ">= 0.6" 222 | } 223 | }, 224 | "node_modules/content-type": { 225 | "version": "1.0.5", 226 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 227 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 228 | "engines": { 229 | "node": ">= 0.6" 230 | } 231 | }, 232 | "node_modules/cookie": { 233 | "version": "0.7.1", 234 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", 235 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", 236 | "license": "MIT", 237 | "engines": { 238 | "node": ">= 0.6" 239 | } 240 | }, 241 | "node_modules/cookie-signature": { 242 | "version": "1.0.6", 243 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 244 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 245 | }, 246 | "node_modules/cross-inspect": { 247 | "version": "1.0.1", 248 | "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", 249 | "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", 250 | "license": "MIT", 251 | "dependencies": { 252 | "tslib": "^2.4.0" 253 | }, 254 | "engines": { 255 | "node": ">=16.0.0" 256 | } 257 | }, 258 | "node_modules/dataloader": { 259 | "version": "2.2.3", 260 | "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", 261 | "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", 262 | "license": "MIT" 263 | }, 264 | "node_modules/debug": { 265 | "version": "2.6.9", 266 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 267 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 268 | "dependencies": { 269 | "ms": "2.0.0" 270 | } 271 | }, 272 | "node_modules/define-data-property": { 273 | "version": "1.1.4", 274 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 275 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 276 | "dependencies": { 277 | "es-define-property": "^1.0.0", 278 | "es-errors": "^1.3.0", 279 | "gopd": "^1.0.1" 280 | }, 281 | "engines": { 282 | "node": ">= 0.4" 283 | }, 284 | "funding": { 285 | "url": "https://github.com/sponsors/ljharb" 286 | } 287 | }, 288 | "node_modules/delayed-stream": { 289 | "version": "1.0.0", 290 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 291 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 292 | "engines": { 293 | "node": ">=0.4.0" 294 | } 295 | }, 296 | "node_modules/depd": { 297 | "version": "2.0.0", 298 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 299 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 300 | "engines": { 301 | "node": ">= 0.8" 302 | } 303 | }, 304 | "node_modules/destroy": { 305 | "version": "1.2.0", 306 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 307 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 308 | "engines": { 309 | "node": ">= 0.8", 310 | "npm": "1.2.8000 || >= 1.4.16" 311 | } 312 | }, 313 | "node_modules/dset": { 314 | "version": "3.1.4", 315 | "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", 316 | "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", 317 | "license": "MIT", 318 | "engines": { 319 | "node": ">=4" 320 | } 321 | }, 322 | "node_modules/ee-first": { 323 | "version": "1.1.1", 324 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 325 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 326 | }, 327 | "node_modules/encodeurl": { 328 | "version": "2.0.0", 329 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 330 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 331 | "license": "MIT", 332 | "engines": { 333 | "node": ">= 0.8" 334 | } 335 | }, 336 | "node_modules/es-define-property": { 337 | "version": "1.0.0", 338 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 339 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 340 | "dependencies": { 341 | "get-intrinsic": "^1.2.4" 342 | }, 343 | "engines": { 344 | "node": ">= 0.4" 345 | } 346 | }, 347 | "node_modules/es-errors": { 348 | "version": "1.3.0", 349 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 350 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 351 | "engines": { 352 | "node": ">= 0.4" 353 | } 354 | }, 355 | "node_modules/escape-html": { 356 | "version": "1.0.3", 357 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 358 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 359 | }, 360 | "node_modules/etag": { 361 | "version": "1.8.1", 362 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 363 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 364 | "engines": { 365 | "node": ">= 0.6" 366 | } 367 | }, 368 | "node_modules/express": { 369 | "version": "4.21.2", 370 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", 371 | "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", 372 | "license": "MIT", 373 | "dependencies": { 374 | "accepts": "~1.3.8", 375 | "array-flatten": "1.1.1", 376 | "body-parser": "1.20.3", 377 | "content-disposition": "0.5.4", 378 | "content-type": "~1.0.4", 379 | "cookie": "0.7.1", 380 | "cookie-signature": "1.0.6", 381 | "debug": "2.6.9", 382 | "depd": "2.0.0", 383 | "encodeurl": "~2.0.0", 384 | "escape-html": "~1.0.3", 385 | "etag": "~1.8.1", 386 | "finalhandler": "1.3.1", 387 | "fresh": "0.5.2", 388 | "http-errors": "2.0.0", 389 | "merge-descriptors": "1.0.3", 390 | "methods": "~1.1.2", 391 | "on-finished": "2.4.1", 392 | "parseurl": "~1.3.3", 393 | "path-to-regexp": "0.1.12", 394 | "proxy-addr": "~2.0.7", 395 | "qs": "6.13.0", 396 | "range-parser": "~1.2.1", 397 | "safe-buffer": "5.2.1", 398 | "send": "0.19.0", 399 | "serve-static": "1.16.2", 400 | "setprototypeof": "1.2.0", 401 | "statuses": "2.0.1", 402 | "type-is": "~1.6.18", 403 | "utils-merge": "1.0.1", 404 | "vary": "~1.1.2" 405 | }, 406 | "engines": { 407 | "node": ">= 0.10.0" 408 | }, 409 | "funding": { 410 | "type": "opencollective", 411 | "url": "https://opencollective.com/express" 412 | } 413 | }, 414 | "node_modules/fast-deep-equal": { 415 | "version": "3.1.3", 416 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 417 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 418 | }, 419 | "node_modules/fast-json-stringify": { 420 | "version": "5.16.1", 421 | "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", 422 | "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", 423 | "dependencies": { 424 | "@fastify/merge-json-schemas": "^0.1.0", 425 | "ajv": "^8.10.0", 426 | "ajv-formats": "^3.0.1", 427 | "fast-deep-equal": "^3.1.3", 428 | "fast-uri": "^2.1.0", 429 | "json-schema-ref-resolver": "^1.0.1", 430 | "rfdc": "^1.2.0" 431 | } 432 | }, 433 | "node_modules/fast-uri": { 434 | "version": "2.4.0", 435 | "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", 436 | "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==" 437 | }, 438 | "node_modules/finalhandler": { 439 | "version": "1.3.1", 440 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", 441 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", 442 | "license": "MIT", 443 | "dependencies": { 444 | "debug": "2.6.9", 445 | "encodeurl": "~2.0.0", 446 | "escape-html": "~1.0.3", 447 | "on-finished": "2.4.1", 448 | "parseurl": "~1.3.3", 449 | "statuses": "2.0.1", 450 | "unpipe": "~1.0.0" 451 | }, 452 | "engines": { 453 | "node": ">= 0.8" 454 | } 455 | }, 456 | "node_modules/follow-redirects": { 457 | "version": "1.15.6", 458 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", 459 | "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", 460 | "funding": [ 461 | { 462 | "type": "individual", 463 | "url": "https://github.com/sponsors/RubenVerborgh" 464 | } 465 | ], 466 | "engines": { 467 | "node": ">=4.0" 468 | }, 469 | "peerDependenciesMeta": { 470 | "debug": { 471 | "optional": true 472 | } 473 | } 474 | }, 475 | "node_modules/form-data": { 476 | "version": "4.0.0", 477 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 478 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 479 | "dependencies": { 480 | "asynckit": "^0.4.0", 481 | "combined-stream": "^1.0.8", 482 | "mime-types": "^2.1.12" 483 | }, 484 | "engines": { 485 | "node": ">= 6" 486 | } 487 | }, 488 | "node_modules/forwarded": { 489 | "version": "0.2.0", 490 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 491 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 492 | "engines": { 493 | "node": ">= 0.6" 494 | } 495 | }, 496 | "node_modules/fresh": { 497 | "version": "0.5.2", 498 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 499 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 500 | "engines": { 501 | "node": ">= 0.6" 502 | } 503 | }, 504 | "node_modules/function-bind": { 505 | "version": "1.1.2", 506 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 507 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 508 | "funding": { 509 | "url": "https://github.com/sponsors/ljharb" 510 | } 511 | }, 512 | "node_modules/generate-function": { 513 | "version": "2.3.1", 514 | "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", 515 | "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", 516 | "dependencies": { 517 | "is-property": "^1.0.2" 518 | } 519 | }, 520 | "node_modules/get-intrinsic": { 521 | "version": "1.2.4", 522 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 523 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 524 | "dependencies": { 525 | "es-errors": "^1.3.0", 526 | "function-bind": "^1.1.2", 527 | "has-proto": "^1.0.1", 528 | "has-symbols": "^1.0.3", 529 | "hasown": "^2.0.0" 530 | }, 531 | "engines": { 532 | "node": ">= 0.4" 533 | }, 534 | "funding": { 535 | "url": "https://github.com/sponsors/ljharb" 536 | } 537 | }, 538 | "node_modules/gopd": { 539 | "version": "1.0.1", 540 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 541 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 542 | "dependencies": { 543 | "get-intrinsic": "^1.1.3" 544 | }, 545 | "funding": { 546 | "url": "https://github.com/sponsors/ljharb" 547 | } 548 | }, 549 | "node_modules/graphql": { 550 | "version": "15.10.1", 551 | "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.10.1.tgz", 552 | "integrity": "sha512-BL/Xd/T9baO6NFzoMpiMD7YUZ62R6viR5tp/MULVEnbYJXZA//kRNW7J0j1w/wXArgL0sCxhDfK5dczSKn3+cg==", 553 | "license": "MIT", 554 | "engines": { 555 | "node": ">= 10.x" 556 | } 557 | }, 558 | "node_modules/graphql-jit": { 559 | "version": "0.8.7", 560 | "resolved": "https://registry.npmjs.org/graphql-jit/-/graphql-jit-0.8.7.tgz", 561 | "integrity": "sha512-KGzCrsxQPfEiXOUIJCexWKiWF6ycjO89kAO6SdO8OWRGwYXbG0hsLuTnbFfMq0gj7d7/ib/Gh7jtst7FHZEEjw==", 562 | "license": "MIT", 563 | "dependencies": { 564 | "@graphql-typed-document-node/core": "^3.2.0", 565 | "fast-json-stringify": "^5.16.1", 566 | "generate-function": "^2.3.1", 567 | "lodash.memoize": "^4.1.2", 568 | "lodash.merge": "4.6.2", 569 | "lodash.mergewith": "4.6.2" 570 | }, 571 | "peerDependencies": { 572 | "graphql": ">=15" 573 | } 574 | }, 575 | "node_modules/has-property-descriptors": { 576 | "version": "1.0.2", 577 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 578 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 579 | "dependencies": { 580 | "es-define-property": "^1.0.0" 581 | }, 582 | "funding": { 583 | "url": "https://github.com/sponsors/ljharb" 584 | } 585 | }, 586 | "node_modules/has-proto": { 587 | "version": "1.0.3", 588 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 589 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 590 | "engines": { 591 | "node": ">= 0.4" 592 | }, 593 | "funding": { 594 | "url": "https://github.com/sponsors/ljharb" 595 | } 596 | }, 597 | "node_modules/has-symbols": { 598 | "version": "1.0.3", 599 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 600 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 601 | "engines": { 602 | "node": ">= 0.4" 603 | }, 604 | "funding": { 605 | "url": "https://github.com/sponsors/ljharb" 606 | } 607 | }, 608 | "node_modules/hasown": { 609 | "version": "2.0.2", 610 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 611 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 612 | "dependencies": { 613 | "function-bind": "^1.1.2" 614 | }, 615 | "engines": { 616 | "node": ">= 0.4" 617 | } 618 | }, 619 | "node_modules/http-errors": { 620 | "version": "2.0.0", 621 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 622 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 623 | "dependencies": { 624 | "depd": "2.0.0", 625 | "inherits": "2.0.4", 626 | "setprototypeof": "1.2.0", 627 | "statuses": "2.0.1", 628 | "toidentifier": "1.0.1" 629 | }, 630 | "engines": { 631 | "node": ">= 0.8" 632 | } 633 | }, 634 | "node_modules/iconv-lite": { 635 | "version": "0.4.24", 636 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 637 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 638 | "dependencies": { 639 | "safer-buffer": ">= 2.1.2 < 3" 640 | }, 641 | "engines": { 642 | "node": ">=0.10.0" 643 | } 644 | }, 645 | "node_modules/inherits": { 646 | "version": "2.0.4", 647 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 648 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 649 | }, 650 | "node_modules/ipaddr.js": { 651 | "version": "1.9.1", 652 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 653 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 654 | "engines": { 655 | "node": ">= 0.10" 656 | } 657 | }, 658 | "node_modules/is-property": { 659 | "version": "1.0.2", 660 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", 661 | "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" 662 | }, 663 | "node_modules/json-schema-ref-resolver": { 664 | "version": "1.0.1", 665 | "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", 666 | "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", 667 | "dependencies": { 668 | "fast-deep-equal": "^3.1.3" 669 | } 670 | }, 671 | "node_modules/json-schema-traverse": { 672 | "version": "1.0.0", 673 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 674 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" 675 | }, 676 | "node_modules/lodash.memoize": { 677 | "version": "4.1.2", 678 | "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", 679 | "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" 680 | }, 681 | "node_modules/lodash.merge": { 682 | "version": "4.6.2", 683 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 684 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" 685 | }, 686 | "node_modules/lodash.mergewith": { 687 | "version": "4.6.2", 688 | "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", 689 | "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" 690 | }, 691 | "node_modules/media-typer": { 692 | "version": "0.3.0", 693 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 694 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 695 | "engines": { 696 | "node": ">= 0.6" 697 | } 698 | }, 699 | "node_modules/merge-descriptors": { 700 | "version": "1.0.3", 701 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", 702 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", 703 | "license": "MIT", 704 | "funding": { 705 | "url": "https://github.com/sponsors/sindresorhus" 706 | } 707 | }, 708 | "node_modules/methods": { 709 | "version": "1.1.2", 710 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 711 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 712 | "engines": { 713 | "node": ">= 0.6" 714 | } 715 | }, 716 | "node_modules/mime": { 717 | "version": "1.6.0", 718 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 719 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 720 | "bin": { 721 | "mime": "cli.js" 722 | }, 723 | "engines": { 724 | "node": ">=4" 725 | } 726 | }, 727 | "node_modules/mime-db": { 728 | "version": "1.52.0", 729 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 730 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 731 | "engines": { 732 | "node": ">= 0.6" 733 | } 734 | }, 735 | "node_modules/mime-types": { 736 | "version": "2.1.35", 737 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 738 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 739 | "dependencies": { 740 | "mime-db": "1.52.0" 741 | }, 742 | "engines": { 743 | "node": ">= 0.6" 744 | } 745 | }, 746 | "node_modules/ms": { 747 | "version": "2.0.0", 748 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 749 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 750 | }, 751 | "node_modules/negotiator": { 752 | "version": "0.6.3", 753 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 754 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 755 | "engines": { 756 | "node": ">= 0.6" 757 | } 758 | }, 759 | "node_modules/object-inspect": { 760 | "version": "1.13.2", 761 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", 762 | "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", 763 | "engines": { 764 | "node": ">= 0.4" 765 | }, 766 | "funding": { 767 | "url": "https://github.com/sponsors/ljharb" 768 | } 769 | }, 770 | "node_modules/on-finished": { 771 | "version": "2.4.1", 772 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 773 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 774 | "dependencies": { 775 | "ee-first": "1.1.1" 776 | }, 777 | "engines": { 778 | "node": ">= 0.8" 779 | } 780 | }, 781 | "node_modules/parseurl": { 782 | "version": "1.3.3", 783 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 784 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 785 | "engines": { 786 | "node": ">= 0.8" 787 | } 788 | }, 789 | "node_modules/path-to-regexp": { 790 | "version": "0.1.12", 791 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", 792 | "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", 793 | "license": "MIT" 794 | }, 795 | "node_modules/proxy-addr": { 796 | "version": "2.0.7", 797 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 798 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 799 | "dependencies": { 800 | "forwarded": "0.2.0", 801 | "ipaddr.js": "1.9.1" 802 | }, 803 | "engines": { 804 | "node": ">= 0.10" 805 | } 806 | }, 807 | "node_modules/proxy-from-env": { 808 | "version": "1.1.0", 809 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 810 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 811 | }, 812 | "node_modules/punycode": { 813 | "version": "2.3.1", 814 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 815 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 816 | "engines": { 817 | "node": ">=6" 818 | } 819 | }, 820 | "node_modules/qs": { 821 | "version": "6.13.0", 822 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", 823 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", 824 | "license": "BSD-3-Clause", 825 | "dependencies": { 826 | "side-channel": "^1.0.6" 827 | }, 828 | "engines": { 829 | "node": ">=0.6" 830 | }, 831 | "funding": { 832 | "url": "https://github.com/sponsors/ljharb" 833 | } 834 | }, 835 | "node_modules/range-parser": { 836 | "version": "1.2.1", 837 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 838 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 839 | "engines": { 840 | "node": ">= 0.6" 841 | } 842 | }, 843 | "node_modules/raw-body": { 844 | "version": "2.5.2", 845 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 846 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 847 | "dependencies": { 848 | "bytes": "3.1.2", 849 | "http-errors": "2.0.0", 850 | "iconv-lite": "0.4.24", 851 | "unpipe": "1.0.0" 852 | }, 853 | "engines": { 854 | "node": ">= 0.8" 855 | } 856 | }, 857 | "node_modules/require-from-string": { 858 | "version": "2.0.2", 859 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 860 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 861 | "engines": { 862 | "node": ">=0.10.0" 863 | } 864 | }, 865 | "node_modules/rfdc": { 866 | "version": "1.4.1", 867 | "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", 868 | "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" 869 | }, 870 | "node_modules/safe-buffer": { 871 | "version": "5.2.1", 872 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 873 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 874 | "funding": [ 875 | { 876 | "type": "github", 877 | "url": "https://github.com/sponsors/feross" 878 | }, 879 | { 880 | "type": "patreon", 881 | "url": "https://www.patreon.com/feross" 882 | }, 883 | { 884 | "type": "consulting", 885 | "url": "https://feross.org/support" 886 | } 887 | ] 888 | }, 889 | "node_modules/safer-buffer": { 890 | "version": "2.1.2", 891 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 892 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 893 | }, 894 | "node_modules/send": { 895 | "version": "0.19.0", 896 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", 897 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", 898 | "license": "MIT", 899 | "dependencies": { 900 | "debug": "2.6.9", 901 | "depd": "2.0.0", 902 | "destroy": "1.2.0", 903 | "encodeurl": "~1.0.2", 904 | "escape-html": "~1.0.3", 905 | "etag": "~1.8.1", 906 | "fresh": "0.5.2", 907 | "http-errors": "2.0.0", 908 | "mime": "1.6.0", 909 | "ms": "2.1.3", 910 | "on-finished": "2.4.1", 911 | "range-parser": "~1.2.1", 912 | "statuses": "2.0.1" 913 | }, 914 | "engines": { 915 | "node": ">= 0.8.0" 916 | } 917 | }, 918 | "node_modules/send/node_modules/encodeurl": { 919 | "version": "1.0.2", 920 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 921 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 922 | "license": "MIT", 923 | "engines": { 924 | "node": ">= 0.8" 925 | } 926 | }, 927 | "node_modules/send/node_modules/ms": { 928 | "version": "2.1.3", 929 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 930 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 931 | "license": "MIT" 932 | }, 933 | "node_modules/serve-static": { 934 | "version": "1.16.2", 935 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", 936 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", 937 | "license": "MIT", 938 | "dependencies": { 939 | "encodeurl": "~2.0.0", 940 | "escape-html": "~1.0.3", 941 | "parseurl": "~1.3.3", 942 | "send": "0.19.0" 943 | }, 944 | "engines": { 945 | "node": ">= 0.8.0" 946 | } 947 | }, 948 | "node_modules/set-function-length": { 949 | "version": "1.2.2", 950 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 951 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 952 | "dependencies": { 953 | "define-data-property": "^1.1.4", 954 | "es-errors": "^1.3.0", 955 | "function-bind": "^1.1.2", 956 | "get-intrinsic": "^1.2.4", 957 | "gopd": "^1.0.1", 958 | "has-property-descriptors": "^1.0.2" 959 | }, 960 | "engines": { 961 | "node": ">= 0.4" 962 | } 963 | }, 964 | "node_modules/setprototypeof": { 965 | "version": "1.2.0", 966 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 967 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 968 | }, 969 | "node_modules/side-channel": { 970 | "version": "1.0.6", 971 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 972 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 973 | "dependencies": { 974 | "call-bind": "^1.0.7", 975 | "es-errors": "^1.3.0", 976 | "get-intrinsic": "^1.2.4", 977 | "object-inspect": "^1.13.1" 978 | }, 979 | "engines": { 980 | "node": ">= 0.4" 981 | }, 982 | "funding": { 983 | "url": "https://github.com/sponsors/ljharb" 984 | } 985 | }, 986 | "node_modules/statuses": { 987 | "version": "2.0.1", 988 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 989 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 990 | "engines": { 991 | "node": ">= 0.8" 992 | } 993 | }, 994 | "node_modules/toidentifier": { 995 | "version": "1.0.1", 996 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 997 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 998 | "engines": { 999 | "node": ">=0.6" 1000 | } 1001 | }, 1002 | "node_modules/tslib": { 1003 | "version": "2.6.3", 1004 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", 1005 | "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" 1006 | }, 1007 | "node_modules/type-is": { 1008 | "version": "1.6.18", 1009 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1010 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1011 | "dependencies": { 1012 | "media-typer": "0.3.0", 1013 | "mime-types": "~2.1.24" 1014 | }, 1015 | "engines": { 1016 | "node": ">= 0.6" 1017 | } 1018 | }, 1019 | "node_modules/unpipe": { 1020 | "version": "1.0.0", 1021 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1022 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1023 | "engines": { 1024 | "node": ">= 0.8" 1025 | } 1026 | }, 1027 | "node_modules/uri-js": { 1028 | "version": "4.4.1", 1029 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1030 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1031 | "dependencies": { 1032 | "punycode": "^2.1.0" 1033 | } 1034 | }, 1035 | "node_modules/utils-merge": { 1036 | "version": "1.0.1", 1037 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1038 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1039 | "engines": { 1040 | "node": ">= 0.4.0" 1041 | } 1042 | }, 1043 | "node_modules/value-or-promise": { 1044 | "version": "1.0.12", 1045 | "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", 1046 | "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", 1047 | "engines": { 1048 | "node": ">=12" 1049 | } 1050 | }, 1051 | "node_modules/vary": { 1052 | "version": "1.1.2", 1053 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1054 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1055 | "engines": { 1056 | "node": ">= 0.8" 1057 | } 1058 | } 1059 | } 1060 | } 1061 | -------------------------------------------------------------------------------- /graphql/graphql_jit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql_jit", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "ISC", 10 | "description": "", 11 | "dependencies": { 12 | "@graphql-tools/schema": "^10.0.4", 13 | "axios": "^1.7.2", 14 | "dataloader": "^2.2.2", 15 | "express": "^4.19.2", 16 | "graphql": "^15.9.0", 17 | "graphql-jit": "^0.8.6" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /graphql/graphql_jit/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pwd 3 | cd graphql/graphql_jit 4 | node server.js 5 | -------------------------------------------------------------------------------- /graphql/graphql_jit/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { parse } = require('graphql'); 3 | const { compileQuery, isCompiledQuery } = require('graphql-jit'); 4 | const bodyParser = require('body-parser'); 5 | const axios = require('axios'); 6 | const DataLoader = require('dataloader'); 7 | const { Agent } = require('http'); 8 | const { makeExecutableSchema } = require('@graphql-tools/schema'); 9 | 10 | const httpAgent = new Agent({ keepAlive: true }); 11 | const axiosInstance = axios.create({ 12 | httpAgent, 13 | proxy: { 14 | protocol: 'http', 15 | host: '127.0.0.1', 16 | port: 3000, 17 | } 18 | }); 19 | 20 | const typeDefs = ` 21 | type Post { 22 | userId: Int 23 | id: Int 24 | title: String 25 | body: String 26 | user: User 27 | } 28 | 29 | type User { 30 | id: Int 31 | name: String 32 | username: String 33 | email: String 34 | phone: String 35 | website: String 36 | } 37 | 38 | type Query { 39 | greet: String 40 | posts: [Post] 41 | } 42 | `; 43 | 44 | const resolvers = { 45 | Query: { 46 | greet: () => 'Hello World!', 47 | posts: async () => { 48 | try { 49 | const response = await axiosInstance.get('http://jsonplaceholder.typicode.com/posts'); 50 | return response.data; 51 | } catch (error) { 52 | throw new Error('Failed to fetch posts'); 53 | } 54 | }, 55 | }, 56 | }; 57 | 58 | const schema = makeExecutableSchema({ typeDefs, resolvers }); 59 | 60 | async function batchUsers(userIds) { 61 | const requests = userIds.map(async (id) => { 62 | const response = await axiosInstance.get(`http://jsonplaceholder.typicode.com/users/${id}`); 63 | return response.data; 64 | }); 65 | return await Promise.all(requests); 66 | } 67 | 68 | const app = express(); 69 | 70 | app.use(bodyParser.json()); 71 | 72 | // In-memory store for compiled queries 73 | const queryCache = new Map(); 74 | 75 | app.use('/graphql', async (req, res) => { 76 | const query = req.body.query || req.query.query; 77 | if (!query) { 78 | res.status(400).send('Query not provided'); 79 | return; 80 | } 81 | 82 | try { 83 | let compiledQuery; 84 | if (queryCache.has(query)) { 85 | compiledQuery = queryCache.get(query); 86 | } else { 87 | const document = parse(query); 88 | compiledQuery = compileQuery(schema, document); 89 | if (!isCompiledQuery(compiledQuery)) { 90 | throw new Error('Error compiling query'); 91 | } 92 | queryCache.set(query, compiledQuery); 93 | } 94 | 95 | const userLoader = new DataLoader(batchUsers, { 96 | batchScheduleFn: callback => setTimeout(callback, 1), 97 | }); 98 | 99 | const contextValue = { userLoader }; 100 | 101 | const result = await compiledQuery.query( 102 | undefined, 103 | contextValue, 104 | req.body.variables, 105 | req.body.operationName 106 | ); 107 | 108 | res.json(result); 109 | } catch (error) { 110 | res.status(500).send(error.message); 111 | } 112 | }); 113 | 114 | app.listen(8000, () => { 115 | console.log('Running a GraphQL API server at http://localhost:8000/graphql'); 116 | }); 117 | -------------------------------------------------------------------------------- /graphql/graphql_jit/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # For graphql_jit 4 | cd graphql/graphql_jit 5 | npm install 6 | cd ../../ -------------------------------------------------------------------------------- /graphql/hasura/config.yaml: -------------------------------------------------------------------------------- 1 | version: 3 2 | endpoint: http://localhost:8080 3 | metadata_directory: metadata 4 | -------------------------------------------------------------------------------- /graphql/hasura/handler.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const axios = require("axios"); 3 | const DataLoader = require("dataloader"); 4 | const { Agent } = require("http"); 5 | 6 | // Create a new axios instance with connection pooling. 7 | const httpAgent = new Agent({ keepAlive: true }); 8 | const axiosInstance = axios.create({ 9 | httpAgent, 10 | }); 11 | 12 | const app = express(); 13 | const port = 4000; 14 | 15 | app.use(express.json()); 16 | 17 | async function batchUsers(usersIds) { 18 | const requests = usersIds.map(async (id) => { 19 | const response = await axiosInstance.get( 20 | `http://jsonplaceholder.typicode.com/users/${id}`, 21 | { 22 | proxy: { 23 | protocol: "http", 24 | host: "127.0.0.1", 25 | port: 3000, 26 | }, 27 | }, 28 | ); 29 | return response.data; 30 | }); 31 | return await Promise.all(requests); 32 | } 33 | 34 | // Route to greet 35 | app.post('/greet', (req, res) => { 36 | res.json('Hello World!'); 37 | }); 38 | 39 | // Route to fetch posts 40 | app.post('/posts', async (req, res) => { 41 | try { 42 | const response = await axiosInstance.get('http://jsonplaceholder.typicode.com/posts', { 43 | proxy: { 44 | protocol: "http", 45 | host: "127.0.0.1", 46 | port: 3000, 47 | }, 48 | }); 49 | let posts = response.data; 50 | 51 | if (req.body.request_query.includes("user")) { 52 | const userLoader = new DataLoader(batchUsers, { 53 | batchScheduleFn: (callback) => setTimeout(callback, 1), 54 | }); 55 | posts = await Promise.all(posts.map(async (post) => { 56 | const user = await userLoader.load(post.userId); 57 | return { 58 | ...post, 59 | user, 60 | }; 61 | })) 62 | } 63 | 64 | res.json(posts); 65 | } catch (error) { 66 | res.json(error); 67 | } 68 | }); 69 | 70 | app.listen(port, () => { 71 | console.log(`Handler server running on port ${port}`); 72 | }); 73 | -------------------------------------------------------------------------------- /graphql/hasura/kill.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Stop and remove PostgreSQL container 4 | docker stop postgres 5 | docker rm postgres 6 | 7 | # Stop and remove Hasura GraphQL Engine container 8 | docker stop graphql-engine 9 | docker rm graphql-engine 10 | 11 | # Stop and remove handler container 12 | docker stop handler 13 | docker rm handler 14 | -------------------------------------------------------------------------------- /graphql/hasura/metadata/actions.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | greet: String! 3 | posts: [Post!]! 4 | } 5 | 6 | type Post { 7 | id: ID! 8 | userId: ID! 9 | title: String! 10 | body: String! 11 | user: User 12 | } 13 | 14 | type User { 15 | id: ID! 16 | name: String! 17 | email: String! 18 | phone: String! 19 | website: String! 20 | } 21 | -------------------------------------------------------------------------------- /graphql/hasura/metadata/actions.yaml: -------------------------------------------------------------------------------- 1 | actions: 2 | - name: greet 3 | definition: 4 | kind: synchronous 5 | handler: '{{HASURA_GRAPHQL_ACTION_HANDLER_URL}}/greet' 6 | - name: posts 7 | definition: 8 | kind: synchronous 9 | handler: '{{HASURA_GRAPHQL_ACTION_HANDLER_URL}}/posts' 10 | -------------------------------------------------------------------------------- /graphql/hasura/metadata/databases/databases.yaml: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /graphql/hasura/metadata/version.yaml: -------------------------------------------------------------------------------- 1 | version: 3 2 | -------------------------------------------------------------------------------- /graphql/hasura/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasura", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.8", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 10 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 11 | "requires": { 12 | "mime-types": "~2.1.34", 13 | "negotiator": "0.6.3" 14 | } 15 | }, 16 | "ansi-styles": { 17 | "version": "3.2.1", 18 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 19 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 20 | "requires": { 21 | "color-convert": "^1.9.0" 22 | } 23 | }, 24 | "array-flatten": { 25 | "version": "1.1.1", 26 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 27 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 28 | }, 29 | "asynckit": { 30 | "version": "0.4.0", 31 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 32 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 33 | }, 34 | "axios": { 35 | "version": "1.7.9", 36 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", 37 | "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", 38 | "requires": { 39 | "follow-redirects": "^1.15.6", 40 | "form-data": "^4.0.0", 41 | "proxy-from-env": "^1.1.0" 42 | } 43 | }, 44 | "body-parser": { 45 | "version": "1.20.3", 46 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", 47 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", 48 | "requires": { 49 | "bytes": "3.1.2", 50 | "content-type": "~1.0.5", 51 | "debug": "2.6.9", 52 | "depd": "2.0.0", 53 | "destroy": "1.2.0", 54 | "http-errors": "2.0.0", 55 | "iconv-lite": "0.4.24", 56 | "on-finished": "2.4.1", 57 | "qs": "6.13.0", 58 | "raw-body": "2.5.2", 59 | "type-is": "~1.6.18", 60 | "unpipe": "1.0.0" 61 | } 62 | }, 63 | "bytes": { 64 | "version": "3.1.2", 65 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 66 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 67 | }, 68 | "call-bind": { 69 | "version": "1.0.8", 70 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", 71 | "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", 72 | "requires": { 73 | "call-bind-apply-helpers": "^1.0.0", 74 | "es-define-property": "^1.0.0", 75 | "get-intrinsic": "^1.2.4", 76 | "set-function-length": "^1.2.2" 77 | } 78 | }, 79 | "call-bind-apply-helpers": { 80 | "version": "1.0.0", 81 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.0.tgz", 82 | "integrity": "sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==", 83 | "requires": { 84 | "es-errors": "^1.3.0", 85 | "function-bind": "^1.1.2" 86 | } 87 | }, 88 | "chalk": { 89 | "version": "2.4.2", 90 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 91 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 92 | "requires": { 93 | "ansi-styles": "^3.2.1", 94 | "escape-string-regexp": "^1.0.5", 95 | "supports-color": "^5.3.0" 96 | } 97 | }, 98 | "color-convert": { 99 | "version": "1.9.3", 100 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 101 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 102 | "requires": { 103 | "color-name": "1.1.3" 104 | } 105 | }, 106 | "color-name": { 107 | "version": "1.1.3", 108 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 109 | "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" 110 | }, 111 | "combined-stream": { 112 | "version": "1.0.8", 113 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 114 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 115 | "requires": { 116 | "delayed-stream": "~1.0.0" 117 | } 118 | }, 119 | "content-disposition": { 120 | "version": "0.5.4", 121 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 122 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 123 | "requires": { 124 | "safe-buffer": "5.2.1" 125 | } 126 | }, 127 | "content-type": { 128 | "version": "1.0.5", 129 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 130 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" 131 | }, 132 | "cookie": { 133 | "version": "0.7.1", 134 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", 135 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" 136 | }, 137 | "cookie-signature": { 138 | "version": "1.0.6", 139 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 140 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 141 | }, 142 | "dataloader": { 143 | "version": "2.2.3", 144 | "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", 145 | "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==" 146 | }, 147 | "debug": { 148 | "version": "2.6.9", 149 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 150 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 151 | "requires": { 152 | "ms": "2.0.0" 153 | } 154 | }, 155 | "define-data-property": { 156 | "version": "1.1.4", 157 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 158 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 159 | "requires": { 160 | "es-define-property": "^1.0.0", 161 | "es-errors": "^1.3.0", 162 | "gopd": "^1.0.1" 163 | } 164 | }, 165 | "delayed-stream": { 166 | "version": "1.0.0", 167 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 168 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 169 | }, 170 | "depd": { 171 | "version": "2.0.0", 172 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 173 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 174 | }, 175 | "destroy": { 176 | "version": "1.2.0", 177 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 178 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" 179 | }, 180 | "dunder-proto": { 181 | "version": "1.0.0", 182 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", 183 | "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", 184 | "requires": { 185 | "call-bind-apply-helpers": "^1.0.0", 186 | "es-errors": "^1.3.0", 187 | "gopd": "^1.2.0" 188 | } 189 | }, 190 | "ee-first": { 191 | "version": "1.1.1", 192 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 193 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 194 | }, 195 | "encodeurl": { 196 | "version": "2.0.0", 197 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 198 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" 199 | }, 200 | "es-define-property": { 201 | "version": "1.0.1", 202 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 203 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" 204 | }, 205 | "es-errors": { 206 | "version": "1.3.0", 207 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 208 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" 209 | }, 210 | "escape-html": { 211 | "version": "1.0.3", 212 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 213 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 214 | }, 215 | "escape-string-regexp": { 216 | "version": "1.0.5", 217 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 218 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" 219 | }, 220 | "etag": { 221 | "version": "1.8.1", 222 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 223 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" 224 | }, 225 | "express": { 226 | "version": "4.21.2", 227 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", 228 | "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", 229 | "requires": { 230 | "accepts": "~1.3.8", 231 | "array-flatten": "1.1.1", 232 | "body-parser": "1.20.3", 233 | "content-disposition": "0.5.4", 234 | "content-type": "~1.0.4", 235 | "cookie": "0.7.1", 236 | "cookie-signature": "1.0.6", 237 | "debug": "2.6.9", 238 | "depd": "2.0.0", 239 | "encodeurl": "~2.0.0", 240 | "escape-html": "~1.0.3", 241 | "etag": "~1.8.1", 242 | "finalhandler": "1.3.1", 243 | "fresh": "0.5.2", 244 | "http-errors": "2.0.0", 245 | "merge-descriptors": "1.0.3", 246 | "methods": "~1.1.2", 247 | "on-finished": "2.4.1", 248 | "parseurl": "~1.3.3", 249 | "path-to-regexp": "0.1.12", 250 | "proxy-addr": "~2.0.7", 251 | "qs": "6.13.0", 252 | "range-parser": "~1.2.1", 253 | "safe-buffer": "5.2.1", 254 | "send": "0.19.0", 255 | "serve-static": "1.16.2", 256 | "setprototypeof": "1.2.0", 257 | "statuses": "2.0.1", 258 | "type-is": "~1.6.18", 259 | "utils-merge": "1.0.1", 260 | "vary": "~1.1.2" 261 | } 262 | }, 263 | "finalhandler": { 264 | "version": "1.3.1", 265 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", 266 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", 267 | "requires": { 268 | "debug": "2.6.9", 269 | "encodeurl": "~2.0.0", 270 | "escape-html": "~1.0.3", 271 | "on-finished": "2.4.1", 272 | "parseurl": "~1.3.3", 273 | "statuses": "2.0.1", 274 | "unpipe": "~1.0.0" 275 | } 276 | }, 277 | "follow-redirects": { 278 | "version": "1.15.6", 279 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", 280 | "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" 281 | }, 282 | "form-data": { 283 | "version": "4.0.1", 284 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", 285 | "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", 286 | "requires": { 287 | "asynckit": "^0.4.0", 288 | "combined-stream": "^1.0.8", 289 | "mime-types": "^2.1.12" 290 | } 291 | }, 292 | "forwarded": { 293 | "version": "0.2.0", 294 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 295 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 296 | }, 297 | "fresh": { 298 | "version": "0.5.2", 299 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 300 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 301 | }, 302 | "function-bind": { 303 | "version": "1.1.2", 304 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 305 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" 306 | }, 307 | "get-intrinsic": { 308 | "version": "1.2.5", 309 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", 310 | "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", 311 | "requires": { 312 | "call-bind-apply-helpers": "^1.0.0", 313 | "dunder-proto": "^1.0.0", 314 | "es-define-property": "^1.0.1", 315 | "es-errors": "^1.3.0", 316 | "function-bind": "^1.1.2", 317 | "gopd": "^1.2.0", 318 | "has-symbols": "^1.1.0", 319 | "hasown": "^2.0.2" 320 | } 321 | }, 322 | "gopd": { 323 | "version": "1.2.0", 324 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 325 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" 326 | }, 327 | "has-flag": { 328 | "version": "3.0.0", 329 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 330 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" 331 | }, 332 | "has-property-descriptors": { 333 | "version": "1.0.2", 334 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 335 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 336 | "requires": { 337 | "es-define-property": "^1.0.0" 338 | } 339 | }, 340 | "has-symbols": { 341 | "version": "1.1.0", 342 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 343 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" 344 | }, 345 | "hasown": { 346 | "version": "2.0.2", 347 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 348 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 349 | "requires": { 350 | "function-bind": "^1.1.2" 351 | } 352 | }, 353 | "hasura-cli": { 354 | "version": "2.36.1", 355 | "resolved": "https://registry.npmjs.org/hasura-cli/-/hasura-cli-2.36.1.tgz", 356 | "integrity": "sha512-h8MHlkkgjlnmFsjzJ+FYDecIB0zswtdvFszXLPsSsyOuCuIIGejAx3rSPFVIxyiZVXohbsJcgjJBe7r91BV3Wg==", 357 | "requires": { 358 | "axios": "^0.21.1", 359 | "chalk": "^2.4.2" 360 | }, 361 | "dependencies": { 362 | "axios": { 363 | "version": "0.21.4", 364 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", 365 | "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", 366 | "requires": { 367 | "follow-redirects": "^1.14.0" 368 | } 369 | } 370 | } 371 | }, 372 | "http": { 373 | "version": "0.0.1-security", 374 | "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", 375 | "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" 376 | }, 377 | "http-errors": { 378 | "version": "2.0.0", 379 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 380 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 381 | "requires": { 382 | "depd": "2.0.0", 383 | "inherits": "2.0.4", 384 | "setprototypeof": "1.2.0", 385 | "statuses": "2.0.1", 386 | "toidentifier": "1.0.1" 387 | } 388 | }, 389 | "iconv-lite": { 390 | "version": "0.4.24", 391 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 392 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 393 | "requires": { 394 | "safer-buffer": ">= 2.1.2 < 3" 395 | } 396 | }, 397 | "inherits": { 398 | "version": "2.0.4", 399 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 400 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 401 | }, 402 | "ipaddr.js": { 403 | "version": "1.9.1", 404 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 405 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 406 | }, 407 | "media-typer": { 408 | "version": "0.3.0", 409 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 410 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" 411 | }, 412 | "merge-descriptors": { 413 | "version": "1.0.3", 414 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", 415 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" 416 | }, 417 | "methods": { 418 | "version": "1.1.2", 419 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 420 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" 421 | }, 422 | "mime": { 423 | "version": "1.6.0", 424 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 425 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 426 | }, 427 | "mime-db": { 428 | "version": "1.52.0", 429 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 430 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 431 | }, 432 | "mime-types": { 433 | "version": "2.1.35", 434 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 435 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 436 | "requires": { 437 | "mime-db": "1.52.0" 438 | } 439 | }, 440 | "ms": { 441 | "version": "2.0.0", 442 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 443 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 444 | }, 445 | "negotiator": { 446 | "version": "0.6.3", 447 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 448 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 449 | }, 450 | "object-inspect": { 451 | "version": "1.13.3", 452 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", 453 | "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==" 454 | }, 455 | "on-finished": { 456 | "version": "2.4.1", 457 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 458 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 459 | "requires": { 460 | "ee-first": "1.1.1" 461 | } 462 | }, 463 | "parseurl": { 464 | "version": "1.3.3", 465 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 466 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 467 | }, 468 | "path-to-regexp": { 469 | "version": "0.1.12", 470 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", 471 | "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" 472 | }, 473 | "proxy-addr": { 474 | "version": "2.0.7", 475 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 476 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 477 | "requires": { 478 | "forwarded": "0.2.0", 479 | "ipaddr.js": "1.9.1" 480 | } 481 | }, 482 | "proxy-from-env": { 483 | "version": "1.1.0", 484 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 485 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 486 | }, 487 | "qs": { 488 | "version": "6.13.0", 489 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", 490 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", 491 | "requires": { 492 | "side-channel": "^1.0.6" 493 | } 494 | }, 495 | "range-parser": { 496 | "version": "1.2.1", 497 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 498 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 499 | }, 500 | "raw-body": { 501 | "version": "2.5.2", 502 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 503 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 504 | "requires": { 505 | "bytes": "3.1.2", 506 | "http-errors": "2.0.0", 507 | "iconv-lite": "0.4.24", 508 | "unpipe": "1.0.0" 509 | } 510 | }, 511 | "safe-buffer": { 512 | "version": "5.2.1", 513 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 514 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 515 | }, 516 | "safer-buffer": { 517 | "version": "2.1.2", 518 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 519 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 520 | }, 521 | "send": { 522 | "version": "0.19.0", 523 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", 524 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", 525 | "requires": { 526 | "debug": "2.6.9", 527 | "depd": "2.0.0", 528 | "destroy": "1.2.0", 529 | "encodeurl": "~1.0.2", 530 | "escape-html": "~1.0.3", 531 | "etag": "~1.8.1", 532 | "fresh": "0.5.2", 533 | "http-errors": "2.0.0", 534 | "mime": "1.6.0", 535 | "ms": "2.1.3", 536 | "on-finished": "2.4.1", 537 | "range-parser": "~1.2.1", 538 | "statuses": "2.0.1" 539 | }, 540 | "dependencies": { 541 | "encodeurl": { 542 | "version": "1.0.2", 543 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 544 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" 545 | }, 546 | "ms": { 547 | "version": "2.1.3", 548 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 549 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 550 | } 551 | } 552 | }, 553 | "serve-static": { 554 | "version": "1.16.2", 555 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", 556 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", 557 | "requires": { 558 | "encodeurl": "~2.0.0", 559 | "escape-html": "~1.0.3", 560 | "parseurl": "~1.3.3", 561 | "send": "0.19.0" 562 | } 563 | }, 564 | "set-function-length": { 565 | "version": "1.2.2", 566 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 567 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 568 | "requires": { 569 | "define-data-property": "^1.1.4", 570 | "es-errors": "^1.3.0", 571 | "function-bind": "^1.1.2", 572 | "get-intrinsic": "^1.2.4", 573 | "gopd": "^1.0.1", 574 | "has-property-descriptors": "^1.0.2" 575 | } 576 | }, 577 | "setprototypeof": { 578 | "version": "1.2.0", 579 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 580 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 581 | }, 582 | "side-channel": { 583 | "version": "1.0.6", 584 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 585 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 586 | "requires": { 587 | "call-bind": "^1.0.7", 588 | "es-errors": "^1.3.0", 589 | "get-intrinsic": "^1.2.4", 590 | "object-inspect": "^1.13.1" 591 | } 592 | }, 593 | "statuses": { 594 | "version": "2.0.1", 595 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 596 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 597 | }, 598 | "supports-color": { 599 | "version": "5.5.0", 600 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 601 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 602 | "requires": { 603 | "has-flag": "^3.0.0" 604 | } 605 | }, 606 | "toidentifier": { 607 | "version": "1.0.1", 608 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 609 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 610 | }, 611 | "type-is": { 612 | "version": "1.6.18", 613 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 614 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 615 | "requires": { 616 | "media-typer": "0.3.0", 617 | "mime-types": "~2.1.24" 618 | } 619 | }, 620 | "unpipe": { 621 | "version": "1.0.0", 622 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 623 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" 624 | }, 625 | "utils-merge": { 626 | "version": "1.0.1", 627 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 628 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" 629 | }, 630 | "vary": { 631 | "version": "1.1.2", 632 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 633 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" 634 | } 635 | } 636 | } 637 | -------------------------------------------------------------------------------- /graphql/hasura/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasura", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "start": "PORT=4000 pm2 start handler.js -i max", 7 | "stop": "pm2 stop handler" 8 | }, 9 | "dependencies": { 10 | "axios": "^1.7.2", 11 | "dataloader": "^2.2.2", 12 | "express": "^4.19.2", 13 | "hasura-cli": "2.36.1", 14 | "http": "^0.0.1-security" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /graphql/hasura/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Database credentials 4 | DB_NAME="db" 5 | DB_USER="user" 6 | DB_PASSWORD="password" 7 | DB_PORT="5432" 8 | 9 | # Start PostgreSQL container 10 | docker run -d --name postgres \ 11 | -e POSTGRES_USER=$DB_USER \ 12 | -e POSTGRES_PASSWORD=$DB_PASSWORD \ 13 | -e POSTGRES_DB=$DB_NAME \ 14 | -p $DB_PORT:5432 \ 15 | postgres:13 16 | 17 | DB_HOST=127.0.0.1 18 | 19 | # Wait for PostgreSQL to be ready 20 | echo "Waiting for PostgreSQL to be ready..." 21 | until docker exec postgres pg_isready -U $DB_USER -d $DB_NAME -h $DB_HOST; do 22 | sleep 1 23 | done 24 | echo "PostgreSQL is ready!" 25 | 26 | docker run -d --name handler \ 27 | --network host \ 28 | --mount type=bind,source="/home/runner/work/graphql-benchmarks/graphql-benchmarks/graphql/hasura",target=/app \ 29 | node:14 bash -c "cd /app && npm install -g pm2 && npm install && npm start && pm2 logs" 30 | 31 | HANDLER_URL=127.0.0.1 32 | HANDLER_URL="http://$HANDLER_URL:4000" 33 | 34 | # Start Hasura GraphQL Engine container 35 | docker run -d --name graphql-engine \ 36 | -e HASURA_GRAPHQL_ACTION_HANDLER_URL=$HANDLER_URL \ 37 | -e HASURA_GRAPHQL_DATABASE_URL=postgres://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_NAME \ 38 | -e HASURA_GRAPHQL_ENABLE_CONSOLE=false \ 39 | -e HASURA_GRAPHQL_ENABLED_LOG_TYPES=startup,http-log,webhook-log,websocket-log,query-log \ 40 | -e HASURA_GRAPHQL_EXPERIMENTAL_FEATURES=naming_convention \ 41 | -e HASURA_GRAPHQL_DEFAULT_NAMING_CONVENTION=graphql-default \ 42 | --network host \ 43 | hasura/graphql-engine:v2.40.0 44 | 45 | # Apply Hasura metadata 46 | cd ./graphql/hasura 47 | npx hasura metadata apply 48 | cd ../.. 49 | -------------------------------------------------------------------------------- /graphql/hasura/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # For hasura 4 | cd graphql/hasura 5 | npm install 6 | cd ../../ 7 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | /target/ 39 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.springframework.boot' version '3.4.2' 4 | id 'io.spring.dependency-management' version '1.1.7' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | 10 | java { 11 | sourceCompatibility = '17' 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | } 17 | 18 | dependencyManagement { 19 | imports { 20 | mavenBom "com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:9.2.2" 21 | mavenBom "io.projectreactor:reactor-bom:2023.0.14" 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation 'com.netflix.graphql.dgs:graphql-dgs-spring-boot-starter' 27 | implementation 'org.springframework.boot:spring-boot-starter-web' 28 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 29 | implementation 'io.projectreactor.netty:reactor-netty-core' 30 | implementation 'io.projectreactor.netty:reactor-netty-http' 31 | implementation 'org.apache.httpcomponents.core5:httpcore5:5.3.3' 32 | implementation 'org.apache.httpcomponents.client5:httpclient5:5.4.1' 33 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 34 | } 35 | 36 | tasks.named('test') { 37 | useJUnitPlatform() 38 | } 39 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailcallhq/graphql-benchmarks/dbb7ae58d608a127918e0bbdbb2165634417fe7b/graphql/netflix_dgs/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /graphql/netflix_dgs/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | org.gradle.wrapper.GradleWrapperMain \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd graphql/netflix_dgs 3 | ./gradlew bootRun -------------------------------------------------------------------------------- /graphql/netflix_dgs/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'netflixdgs' 2 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # For netflix dgs 4 | cd graphql/netflix_dgs 5 | ./gradlew build 6 | cd ../../ -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/java/com/example/netflixdgs/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.netflixdgs; 2 | 3 | 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; 13 | import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; 14 | import org.apache.hc.client5.http.impl.classic.HttpClients; 15 | import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; 16 | import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner; 17 | import org.apache.hc.core5.http.HttpHost; 18 | import org.apache.hc.core5.http.HttpResponse; 19 | import org.apache.hc.core5.http.protocol.HttpContext; 20 | import org.apache.hc.core5.util.TimeValue; 21 | 22 | @Configuration 23 | public class AppConfig { 24 | 25 | @Bean 26 | public RestTemplate restTemplate() { 27 | final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); 28 | // the maximum number of connections. See https://github.com/tailcallhq/graphql-benchmarks/blob/main/wrk/bench.sh 29 | connectionManager.setMaxTotal(100); 30 | connectionManager.setDefaultMaxPerRoute(100); 31 | 32 | final ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() { 33 | public TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context) { 34 | // Keep alive for 60 seconds 35 | return TimeValue.of(60, TimeUnit.SECONDS); 36 | } 37 | }; 38 | 39 | final HttpHost proxy = new HttpHost("127.0.0.1", 3000); 40 | final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); 41 | final CloseableHttpClient httpClient = HttpClients.custom() 42 | .setRoutePlanner(routePlanner) 43 | .setConnectionManager(connectionManager) 44 | .setConnectionManagerShared(true) 45 | .setKeepAliveStrategy(keepAliveStrategy) 46 | .build(); 47 | final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); 48 | return new RestTemplate(factory); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/java/com/example/netflixdgs/GreetDataFetcher.java: -------------------------------------------------------------------------------- 1 | package com.example.netflixdgs; 2 | 3 | import com.netflix.graphql.dgs.DgsComponent; 4 | import com.netflix.graphql.dgs.DgsQuery; 5 | import org.springframework.stereotype.Component; 6 | 7 | @DgsComponent 8 | public class GreetDataFetcher { 9 | 10 | @DgsQuery 11 | public String greet() { 12 | return "Hello World!"; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/java/com/example/netflixdgs/NetflixdgsApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.netflixdgs; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class NetflixdgsApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(NetflixdgsApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/java/com/example/netflixdgs/Post.java: -------------------------------------------------------------------------------- 1 | package com.example.netflixdgs; 2 | 3 | public class Post { 4 | private Integer id; 5 | private Integer userId; 6 | private String title; 7 | private String body; 8 | private User user; 9 | 10 | public Integer getUserId() { 11 | return userId; 12 | } 13 | 14 | public String getBody() { 15 | return body; 16 | } 17 | 18 | public User getUser() { 19 | return user; 20 | } 21 | 22 | public Integer getId() { 23 | return id; 24 | } 25 | public String getTitle() { 26 | return title; 27 | } 28 | 29 | public void setUser(User user) { 30 | this.user = user; 31 | } 32 | 33 | public Post() { 34 | super(); 35 | } 36 | 37 | public Post(Integer id, Integer userId, String title, String body, User user) { 38 | this.id = id; 39 | this.userId = userId; 40 | this.title = title; 41 | this.body = body; 42 | this.user = user; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/java/com/example/netflixdgs/PostsDataFetcher.java: -------------------------------------------------------------------------------- 1 | package com.example.netflixdgs; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.stream.Collectors; 6 | 7 | import com.netflix.graphql.dgs.DgsComponent; 8 | import com.netflix.graphql.dgs.DgsQuery; 9 | import org.dataloader.DataLoader; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.web.client.RestTemplate; 12 | 13 | import org.springframework.core.ParameterizedTypeReference; 14 | import org.springframework.http.ResponseEntity; 15 | import com.netflix.graphql.dgs.context.DgsContext; 16 | import graphql.schema.DataFetchingEnvironment; 17 | 18 | @DgsComponent 19 | public class PostsDataFetcher { 20 | private final RestTemplate restTemplate; 21 | private static final ParameterizedTypeReference> typeReference = new ParameterizedTypeReference>() {}; 22 | 23 | @Autowired 24 | public PostsDataFetcher(RestTemplate restTemplate) { 25 | this.restTemplate = restTemplate; 26 | } 27 | 28 | @DgsQuery 29 | public CompletableFuture> posts(DataFetchingEnvironment dfe) { 30 | String apiUrl = "http://jsonplaceholder.typicode.com/posts"; 31 | 32 | ResponseEntity> responseEntity = restTemplate.exchange( 33 | apiUrl, 34 | org.springframework.http.HttpMethod.GET, 35 | null, 36 | typeReference 37 | ); 38 | 39 | List posts = responseEntity.getBody(); 40 | 41 | if (!dfe.getSelectionSet().contains("user")) { 42 | return CompletableFuture.completedFuture(posts); 43 | } 44 | 45 | DataLoader userDataLoader = dfe.getDataLoader("users"); 46 | 47 | List> futures = posts.stream() 48 | .map(post -> { 49 | return userDataLoader.load(post.getUserId()).thenAccept(user -> post.setUser(user)); 50 | }) 51 | .collect(Collectors.toList()); 52 | 53 | return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) 54 | .thenApply(v -> posts); 55 | } 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/java/com/example/netflixdgs/User.java: -------------------------------------------------------------------------------- 1 | package com.example.netflixdgs; 2 | 3 | public class User { 4 | private Integer id; 5 | private String name; 6 | private String username; 7 | private String email; 8 | private String phone; 9 | private String website; 10 | 11 | public User(Integer id, String name, String username, String email, String phone, String website) { 12 | this.id = id; 13 | this.name = name; 14 | this.username = username; 15 | this.email = email; 16 | this.phone = phone; 17 | this.website = website; 18 | } 19 | public Integer getId() { 20 | return id; 21 | } 22 | public String getName() { 23 | return name; 24 | } 25 | public String getUsername() { 26 | return username; 27 | } 28 | public String getEmail() { 29 | return email; 30 | } 31 | public String getPhone() { 32 | return phone; 33 | } 34 | public String getWebsite() { 35 | return website; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/java/com/example/netflixdgs/UserDataLoader.java: -------------------------------------------------------------------------------- 1 | package com.example.netflixdgs; 2 | 3 | import com.netflix.graphql.dgs.DgsDataLoader; 4 | import org.dataloader.BatchLoader; 5 | import org.dataloader.DataLoader; 6 | import org.dataloader.DataLoaderFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.client.RestTemplate; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.CompletableFuture; 12 | import java.util.stream.Collectors; 13 | 14 | @DgsDataLoader(name = "users") 15 | public class UserDataLoader implements BatchLoader { 16 | private final RestTemplate restTemplate; 17 | 18 | @Autowired 19 | public UserDataLoader(RestTemplate restTemplate) { 20 | this.restTemplate = restTemplate; 21 | } 22 | 23 | @Override 24 | public CompletableFuture> load(List userIds) { 25 | return CompletableFuture.supplyAsync(() -> userIds.stream() 26 | .map(userId -> restTemplate.getForObject("http://jsonplaceholder.typicode.com/users/" + userId, User.class)) 27 | .collect(Collectors.toList()) 28 | ); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8000 2 | http.proxyHost=localhost 3 | http.proxyPort=3000 4 | dgs.graphql.dataloader.schedule-duration=1ms 5 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/main/resources/schema/schema.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | id: Int! 3 | name: String! 4 | username: String! 5 | email: String! 6 | phone: String 7 | website: String 8 | } 9 | 10 | type Post { 11 | id: Int! 12 | userId: Int! 13 | title: String! 14 | body: String! 15 | user: User 16 | } 17 | 18 | type Query { 19 | posts: [Post] 20 | greet: String! 21 | } 22 | -------------------------------------------------------------------------------- /graphql/netflix_dgs/src/test/java/com/example/netflixdgs/NetflixdgsApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.netflixdgs; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class NetflixdgsApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /graphql/tailcall/benchmark.graphql: -------------------------------------------------------------------------------- 1 | schema 2 | @server(port: 8000) 3 | @upstream( 4 | poolMaxIdlePerHost: 200 5 | tcpKeepAlive: 60 6 | proxy: { url: "http://127.0.0.1:3000" } 7 | ) { 8 | query: Query 9 | } 10 | 11 | type Query { 12 | posts: [Post] 13 | @http(url: "http://jsonplaceholder.typicode.com/posts", dedupe: true) 14 | greet: String! @expr(body: "Hello World!") 15 | } 16 | 17 | type User { 18 | id: Int! 19 | name: String! 20 | username: String! 21 | email: String! 22 | phone: String 23 | website: String 24 | } 25 | 26 | type Post { 27 | id: Int! 28 | userId: Int! 29 | title: String! 30 | body: String! 31 | user: User @http(url: "http://jsonplaceholder.typicode.com/users/{{value.userId}}", dedupe: true) 32 | } 33 | -------------------------------------------------------------------------------- /graphql/tailcall/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pwd 3 | cd graphql/tailcall 4 | npm install 5 | -------------------------------------------------------------------------------- /graphql/tailcall/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailcall", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "tailcall", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@tailcallhq/tailcall": "0.136.0" 11 | } 12 | }, 13 | "node_modules/@scarf/scarf": { 14 | "version": "1.3.0", 15 | "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.3.0.tgz", 16 | "integrity": "sha512-lHKK8M5CTcpFj2hZDB3wIjb0KAbEOgDmiJGDv1WBRfQgRm/a8/XMEkG/N1iM01xgbUDsPQwi42D+dFo1XPAKew==", 17 | "hasInstallScript": true 18 | }, 19 | "node_modules/@tailcallhq/core-darwin-arm64": { 20 | "version": "0.136.0", 21 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-darwin-arm64/-/core-darwin-arm64-0.136.0.tgz", 22 | "integrity": "sha512-i/8KhCHggkP8k4xu0GtCzOLTuDhoS4flpMX07CTbaM1mq8KDv0Nq4dKuHYvWbJ2Aa6jYLFiKOtn37aq1Fk92Qg==", 23 | "cpu": [ 24 | "arm64" 25 | ], 26 | "license": "Apache-2.0", 27 | "optional": true, 28 | "os": [ 29 | "darwin" 30 | ], 31 | "bin": { 32 | "tailcall": "bin/tailcall" 33 | } 34 | }, 35 | "node_modules/@tailcallhq/core-darwin-x64": { 36 | "version": "0.136.0", 37 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-darwin-x64/-/core-darwin-x64-0.136.0.tgz", 38 | "integrity": "sha512-BA4nAYZVKWzo9ZGIh0eTMzd6SbcA1FrCaeyDANOe6rEfSPpQ6zUhSAVJSVBLMHj2RCObRpKoadQKw4hHxTHHuQ==", 39 | "cpu": [ 40 | "x64" 41 | ], 42 | "license": "Apache-2.0", 43 | "optional": true, 44 | "os": [ 45 | "darwin" 46 | ], 47 | "bin": { 48 | "tailcall": "bin/tailcall" 49 | } 50 | }, 51 | "node_modules/@tailcallhq/core-linux-arm64-gnu": { 52 | "version": "0.136.0", 53 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-linux-arm64-gnu/-/core-linux-arm64-gnu-0.136.0.tgz", 54 | "integrity": "sha512-FPu1PUUMJ09F4wenK0+7+fs1gXL5J3ubfhg0QpJHbP6KiuFnEqBk0EtnTg3z8LuXSRqUYbZovTCmXIz6lCuG1w==", 55 | "cpu": [ 56 | "arm64" 57 | ], 58 | "license": "Apache-2.0", 59 | "optional": true, 60 | "os": [ 61 | "linux" 62 | ], 63 | "bin": { 64 | "tailcall": "bin/tailcall" 65 | } 66 | }, 67 | "node_modules/@tailcallhq/core-linux-arm64-musl": { 68 | "version": "0.136.0", 69 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-linux-arm64-musl/-/core-linux-arm64-musl-0.136.0.tgz", 70 | "integrity": "sha512-UsjLAHj7y+grDEo2r9KnVlHxS5Yx+ks4dHwROiY8ZB0vUTka0a1UEhWOlQgCWEqbobhwhKQsPe651ysssF2SVA==", 71 | "cpu": [ 72 | "arm64" 73 | ], 74 | "license": "Apache-2.0", 75 | "optional": true, 76 | "os": [ 77 | "linux" 78 | ], 79 | "bin": { 80 | "tailcall": "bin/tailcall" 81 | } 82 | }, 83 | "node_modules/@tailcallhq/core-linux-ia32-gnu": { 84 | "version": "0.136.0", 85 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-linux-ia32-gnu/-/core-linux-ia32-gnu-0.136.0.tgz", 86 | "integrity": "sha512-859ITxxrcLXX9HJeo4NiefJGnBEqW1Qug6Fdp4icJwGnMyfZBRhpDRVl2hUUAX1ZgZiTRtN8IT5eQcD+6pHtXA==", 87 | "cpu": [ 88 | "ia32" 89 | ], 90 | "license": "Apache-2.0", 91 | "optional": true, 92 | "os": [ 93 | "linux" 94 | ], 95 | "bin": { 96 | "tailcall": "bin/tailcall" 97 | } 98 | }, 99 | "node_modules/@tailcallhq/core-linux-x64-gnu": { 100 | "version": "0.136.0", 101 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-linux-x64-gnu/-/core-linux-x64-gnu-0.136.0.tgz", 102 | "integrity": "sha512-SH9oXAhXJw6xIn3HOaw3RFAMbdDlPpTbswtUlfY320ipR0TWANGhBEf1M4Ce+Bcyl0WR4c8ZZ6WN/zgPGuI1Xw==", 103 | "cpu": [ 104 | "x64" 105 | ], 106 | "license": "Apache-2.0", 107 | "optional": true, 108 | "os": [ 109 | "linux" 110 | ], 111 | "bin": { 112 | "tailcall": "bin/tailcall" 113 | } 114 | }, 115 | "node_modules/@tailcallhq/core-linux-x64-musl": { 116 | "version": "0.136.0", 117 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-linux-x64-musl/-/core-linux-x64-musl-0.136.0.tgz", 118 | "integrity": "sha512-gwaTlXU/8b+rVHVcDKOoQnZbtKLZ5Jx25NkDPQJgnkwtV4lEzFZubg4XwJQaAhnC1NI6Aykll9KzsRupXuXTmQ==", 119 | "cpu": [ 120 | "x64" 121 | ], 122 | "license": "Apache-2.0", 123 | "optional": true, 124 | "os": [ 125 | "linux" 126 | ], 127 | "bin": { 128 | "tailcall": "bin/tailcall" 129 | } 130 | }, 131 | "node_modules/@tailcallhq/core-win32-arm64-msvc": { 132 | "version": "0.136.0", 133 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-win32-arm64-msvc/-/core-win32-arm64-msvc-0.136.0.tgz", 134 | "integrity": "sha512-4U857J0Nat4ReiOkqRXUFYj8fOVETgmmm0h8tGltkEaj/uvpZlg5xkyJ6b+T3AJliM1b3y1iLxBapUziUBqdXA==", 135 | "cpu": [ 136 | "arm64" 137 | ], 138 | "license": "Apache-2.0", 139 | "optional": true, 140 | "os": [ 141 | "win32" 142 | ], 143 | "bin": { 144 | "tailcall": "bin/tailcall.exe" 145 | } 146 | }, 147 | "node_modules/@tailcallhq/core-win32-ia32-msvc": { 148 | "version": "0.136.0", 149 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-win32-ia32-msvc/-/core-win32-ia32-msvc-0.136.0.tgz", 150 | "integrity": "sha512-SXS9GXMDP6L/3GTPbn5UVzKFyePXOqbvP+zf2WfFiap3yrmxWVgit59+buXMeKymSarFjM/dQbvajNiUkKmg3Q==", 151 | "cpu": [ 152 | "ia32" 153 | ], 154 | "license": "Apache-2.0", 155 | "optional": true, 156 | "os": [ 157 | "win32" 158 | ], 159 | "bin": { 160 | "tailcall": "bin/tailcall.exe" 161 | } 162 | }, 163 | "node_modules/@tailcallhq/core-win32-x64-msvc": { 164 | "version": "0.136.0", 165 | "resolved": "https://registry.npmjs.org/@tailcallhq/core-win32-x64-msvc/-/core-win32-x64-msvc-0.136.0.tgz", 166 | "integrity": "sha512-bQKcHaSNHGe9ZT68wok8N2xYTbO7ib+9btp42TQk8oPhl5FBWvWrEdojpjYkVOMq+gR7qO0sxe8PaM5lCotU9A==", 167 | "cpu": [ 168 | "x64" 169 | ], 170 | "license": "Apache-2.0", 171 | "optional": true, 172 | "os": [ 173 | "win32" 174 | ], 175 | "bin": { 176 | "tailcall": "bin/tailcall.exe" 177 | } 178 | }, 179 | "node_modules/@tailcallhq/tailcall": { 180 | "version": "0.136.0", 181 | "resolved": "https://registry.npmjs.org/@tailcallhq/tailcall/-/tailcall-0.136.0.tgz", 182 | "integrity": "sha512-tA05b+hMm/97zA87m6ntQr+b1r2Jm2DpHtA3RatQptkBnTfZA/UTEmmNlbR6rUSITd9CIaLFMo/+UejwOEKtgg==", 183 | "hasInstallScript": true, 184 | "license": "Apache-2.0", 185 | "dependencies": { 186 | "@scarf/scarf": "^1.3.0", 187 | "detect-libc": "^2.0.2" 188 | }, 189 | "optionalDependencies": { 190 | "@tailcallhq/core-darwin-arm64": "v0.136.0", 191 | "@tailcallhq/core-darwin-x64": "v0.136.0", 192 | "@tailcallhq/core-linux-arm64-gnu": "v0.136.0", 193 | "@tailcallhq/core-linux-arm64-musl": "v0.136.0", 194 | "@tailcallhq/core-linux-ia32-gnu": "v0.136.0", 195 | "@tailcallhq/core-linux-x64-gnu": "v0.136.0", 196 | "@tailcallhq/core-linux-x64-musl": "v0.136.0", 197 | "@tailcallhq/core-win32-arm64-msvc": "v0.136.0", 198 | "@tailcallhq/core-win32-ia32-msvc": "v0.136.0", 199 | "@tailcallhq/core-win32-x64-msvc": "v0.136.0" 200 | } 201 | }, 202 | "node_modules/detect-libc": { 203 | "version": "2.0.3", 204 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", 205 | "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", 206 | "engines": { 207 | "node": ">=8" 208 | } 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /graphql/tailcall/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailcall", 3 | "description": "", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": { 11 | "@tailcallhq/tailcall": "0.136.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /graphql/tailcall/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Base directory 4 | 5 | cd graphql/tailcall 6 | 7 | current_dir=$(pwd) 8 | echo "Current directory: $current_dir" 9 | 10 | base_dir="./node_modules" 11 | 12 | # Pick the tailcall executable 13 | for core_dir in $(find "$base_dir" -type d -name "core-*"); do 14 | tailcall_executable="${core_dir}/bin/tailcall" 15 | 16 | # Check if the tailcall executable exists 17 | if [[ -x "$tailcall_executable" ]]; then 18 | echo "Executing $tailcall_executable" 19 | 20 | # Run the executable with the specified arguments 21 | TAILCALL_LOG_LEVEL=error TC_TRACKER=false "$tailcall_executable" start $current_dir/benchmark.graphql 22 | exit 0 23 | fi 24 | done 25 | 26 | echo "tailcall executable not found." 27 | exit 1 -------------------------------------------------------------------------------- /graphql/tailcall/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # For tailcall: 4 | cd graphql/tailcall 5 | npm install 6 | cd ../../ -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | pid /tmp/nginx.pid; 2 | worker_processes auto; 3 | 4 | events { 5 | worker_connections 512; 6 | } 7 | 8 | error_log /tmp/error.log error; 9 | 10 | http { 11 | proxy_cache_path /tmp/nginx-cache levels=1:2 keys_zone=http_cache:10m max_size=1g inactive=60m use_temp_path=on; 12 | access_log /dev/null; 13 | client_body_temp_path /tmp/nginx-client-body; 14 | proxy_temp_path /tmp/nginx-proxy; 15 | fastcgi_temp_path /tmp/nginx-fastcgi; 16 | uwsgi_temp_path /tmp/nginx-uwsgi; 17 | scgi_temp_path /tmp/nginx-scgi; 18 | 19 | server { 20 | resolver 8.8.8.8 valid=100s ipv6=off; 21 | listen 3000; 22 | location / { 23 | proxy_set_header Host $proxy_host; 24 | proxy_ssl_verify off; 25 | proxy_ssl_server_name on; 26 | proxy_pass http://$host; 27 | proxy_cache http_cache; 28 | proxy_cache_valid 200 302 60m; 29 | proxy_cache_valid 404 1m; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /nginx/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ulimit -n 1000000 3 | 4 | # Get the present working directory 5 | current_dir=$(pwd) 6 | 7 | # Start nginx using the configuration file from the current directory 8 | nginx -c "$current_dir/nginx/nginx.conf" 9 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base", ":automergeMinor"], 4 | "ignoreDeps": ["hasura-cli"] 5 | } 6 | -------------------------------------------------------------------------------- /results.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | | Query | Server | Requests/sec | Latency (ms) | Relative | 4 | |-------:|--------:|--------------:|--------------:|---------:| 5 | | 1 | `{ posts { id userId title user { id name email }}}` | 6 | || [Tailcall] | `22,805.00` | `4.30` | `214.34x` | 7 | || [async-graphql] | `1,080.88` | `91.86` | `10.16x` | 8 | || [GraphQL JIT] | `1,038.83` | `95.78` | `9.76x` | 9 | || [Caliban] | `902.18` | `110.94` | `8.48x` | 10 | || [Gqlgen] | `400.68` | `246.33` | `3.77x` | 11 | || [Netflix DGS] | `191.44` | `506.58` | `1.80x` | 12 | || [Apollo GraphQL] | `133.94` | `687.14` | `1.26x` | 13 | || [Hasura] | `106.40` | `824.18` | `1.00x` | 14 | | 2 | `{ posts { title }}` | 15 | || [Tailcall] | `36,300.30` | `2.69` | `82.05x` | 16 | || [Caliban] | `5,554.80` | `18.07` | `12.55x` | 17 | || [async-graphql] | `5,296.11` | `18.90` | `11.97x` | 18 | || [Gqlgen] | `1,112.44` | `98.53` | `2.51x` | 19 | || [GraphQL JIT] | `1,066.55` | `93.57` | `2.41x` | 20 | || [Apollo GraphQL] | `896.38` | `112.08` | `2.03x` | 21 | || [Netflix DGS] | `818.73` | `149.69` | `1.85x` | 22 | || [Hasura] | `442.44` | `226.86` | `1.00x` | 23 | | 3 | `{ greet }` | 24 | || [Caliban] | `47,657.70` | `2.04` | `30.11x` | 25 | || [Tailcall] | `46,811.10` | `2.09` | `29.57x` | 26 | || [Gqlgen] | `25,725.90` | `4.91` | `16.25x` | 27 | || [async-graphql] | `25,616.50` | `3.92` | `16.18x` | 28 | || [GraphQL JIT] | `4,434.19` | `22.51` | `2.80x` | 29 | || [Netflix DGS] | `4,119.79` | `27.30` | `2.60x` | 30 | || [Apollo GraphQL] | `4,071.10` | `27.92` | `2.57x` | 31 | || [Hasura] | `1,583.00` | `64.75` | `1.00x` | 32 | 33 | 34 | -------------------------------------------------------------------------------- /run_analyze_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Update and install gnuplot 4 | sudo apt-get update && sudo apt-get install -y gnuplot 5 | 6 | # Remove existing results file 7 | rm -f results.md 8 | 9 | services=("apollo" "caliban" "netflixdgs" "gqlgen" "tailcall" "async_graphql" "hasura" "graphql_jit") 10 | 11 | # Loop through each benchmark (1, 2, 3) 12 | for bench in 1 2 3; do 13 | echo "Processing files for bench${bench}:" 14 | 15 | # Construct the command for each benchmark 16 | cmd="bash analyze.sh" 17 | 18 | # Loop through each service 19 | for service in "${services[@]}"; do 20 | # Convert service name to match file naming convention 21 | case $service in 22 | "apollo") file_service="apollo_server" ;; 23 | "netflixdgs") file_service="netflix_dgs" ;; 24 | *) file_service=$service ;; 25 | esac 26 | 27 | # Add files for current service to the command 28 | for run in 1 2 3; do 29 | cmd+=" bench${bench}_result${run}_graphql_${file_service}_run.sh.txt" 30 | done 31 | done 32 | 33 | # Execute the command 34 | echo "Executing: $cmd" 35 | eval $cmd 36 | done -------------------------------------------------------------------------------- /run_benchmarks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Start services and run benchmarks 4 | function killServerOnPort() { 5 | local port="$1" 6 | local pid=$(lsof -t -i:"$port") 7 | 8 | if [ -n "$pid" ]; then 9 | kill "$pid" 10 | echo "Killed process running on port $port" 11 | else 12 | echo "No process found running on port $port" 13 | fi 14 | } 15 | 16 | bench1Results=() 17 | bench2Results=() 18 | bench3Results=() 19 | killServerOnPort 3000 20 | sh nginx/run.sh 21 | 22 | function runBenchmark() { 23 | killServerOnPort 8000 24 | sleep 5 25 | local serviceScript="$1" 26 | local benchmarks=(1 2 3) 27 | 28 | if [[ "$serviceScript" == *"hasura"* ]]; then 29 | bash "$serviceScript" # Run synchronously without background process 30 | else 31 | bash "$serviceScript" & # Run in daemon mode 32 | fi 33 | 34 | sleep 15 # Give some time for the service to start up 35 | 36 | local graphqlEndpoint="http://localhost:8000/graphql" 37 | if [[ "$serviceScript" == *"hasura"* ]]; then 38 | graphqlEndpoint=http://127.0.0.1:8080/v1/graphql 39 | fi 40 | 41 | for bench in "${benchmarks[@]}"; do 42 | local benchmarkScript="wrk/bench.sh" 43 | 44 | # Replace / with _ 45 | local sanitizedServiceScriptName=$(echo "$serviceScript" | tr '/' '_') 46 | 47 | local resultFiles=("result1_${sanitizedServiceScriptName}.txt" "result2_${sanitizedServiceScriptName}.txt" "result3_${sanitizedServiceScriptName}.txt") 48 | 49 | bash "test_query${bench}.sh" "$graphqlEndpoint" 50 | 51 | # Warmup run 52 | bash "$benchmarkScript" "$graphqlEndpoint" "$bench" >/dev/null 53 | sleep 1 # Give some time for apps to finish in-flight requests from warmup 54 | bash "$benchmarkScript" "$graphqlEndpoint" "$bench" >/dev/null 55 | sleep 1 56 | bash "$benchmarkScript" "$graphqlEndpoint" "$bench" >/dev/null 57 | sleep 1 58 | 59 | # 3 benchmark runs 60 | for resultFile in "${resultFiles[@]}"; do 61 | echo "Running benchmark $bench for $serviceScript" 62 | bash "$benchmarkScript" "$graphqlEndpoint" "$bench" >"bench${bench}_${resultFile}" 63 | if [ "$bench" == "1" ]; then 64 | bench1Results+=("bench1_${resultFile}") 65 | elif [ "$bench" == "2" ]; then 66 | bench2Results+=("bench2_${resultFile}") 67 | elif [ "$bench" == "3" ]; then 68 | bench3Results+=("bench3_${resultFile}") 69 | fi 70 | done 71 | done 72 | } 73 | 74 | rm "results.md" 75 | 76 | # Main script 77 | if [ $# -eq 0 ]; then 78 | echo "Usage: $0 " 79 | echo "Available services: apollo_server, caliban, netflix_dgs, gqlgen, tailcall, async_graphql, hasura, graphql_jit" 80 | exit 1 81 | fi 82 | 83 | service="$1" 84 | valid_services=("apollo_server" "caliban" "netflix_dgs" "gqlgen" "tailcall" "async_graphql" "hasura" "graphql_jit") 85 | 86 | if [[ ! " ${valid_services[@]} " =~ " ${service} " ]]; then 87 | echo "Invalid service name. Available services: ${valid_services[*]}" 88 | exit 1 89 | fi 90 | 91 | 92 | 93 | runBenchmark "graphql/${service}/run.sh" 94 | 95 | if [ "$service" == "apollo_server" ]; then 96 | cd graphql/apollo_server/ 97 | npm stop 98 | cd ../../ 99 | elif [ "$service" == "hasura" ]; then 100 | bash "graphql/hasura/kill.sh" 101 | fi -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # For gqlgen: 4 | cd graphql/gqlgen 5 | go build -o main main.go 6 | cd ../../ 7 | # For apollo server: 8 | cd graphql/apollo_server 9 | npm i 10 | cd ../../ 11 | 12 | # For netflix dgs 13 | cd graphql/netflix_dgs 14 | ./gradlew build 15 | cd ../../ 16 | 17 | # For tailcall: 18 | cd graphql/tailcall 19 | npm install 20 | cd ../../ 21 | 22 | # For caliban 23 | cd graphql/caliban 24 | ./sbt assembly 25 | cd ../../ 26 | 27 | # For async-graphql 28 | ./graphql/async_graphql/build.sh 29 | 30 | # For hasura 31 | cd graphql/hasura 32 | npm install 33 | cd ../../ 34 | 35 | # For graphql_jit 36 | cd graphql/graphql_jit 37 | npm install 38 | cd ../../ 39 | -------------------------------------------------------------------------------- /test_query1.sh: -------------------------------------------------------------------------------- 1 | graphqlEndpoint="${1:-http://localhost:8000/graphql}" 2 | curl -i -X POST -d '{"query": "{posts{id,userId, title, user{id, name, email}}}"}' $graphqlEndpoint -H "Content-Type: application/json" 3 | -------------------------------------------------------------------------------- /test_query2.sh: -------------------------------------------------------------------------------- 1 | graphqlEndpoint="${1:-http://localhost:8000/graphql}" 2 | curl -i -X POST -d '{"query": "{posts{title}}"}' $graphqlEndpoint -H "Content-Type: application/json" 3 | -------------------------------------------------------------------------------- /test_query3.sh: -------------------------------------------------------------------------------- 1 | graphqlEndpoint="${1:-http://localhost:8000/graphql}" 2 | curl -i -X POST -d '{"query": "{greet}"}' $graphqlEndpoint -H "Content-Type: application/json" 3 | -------------------------------------------------------------------------------- /wrk/bench.sh: -------------------------------------------------------------------------------- 1 | graphqlEndpoint="${1:-http://localhost:8000/graphql}" 2 | whichBench=$2 3 | 4 | if [ "$whichBench" == "2" ]; then 5 | wrk -d 30 -t 4 -c 100 -s $(pwd)/wrk/wrk2.lua "$graphqlEndpoint" 6 | else 7 | wrk -d 10 -t 4 -c 100 -s "$(pwd)/wrk/wrk${whichBench}.lua" "$graphqlEndpoint" 8 | fi 9 | -------------------------------------------------------------------------------- /wrk/wrk1.lua: -------------------------------------------------------------------------------- 1 | wrk.method = "POST" 2 | wrk.body = '{"operationName":null,"variables":{},"query":"{posts{id,userId,title,user{id,name,email}}}"}' 3 | wrk.headers["Connection"] = "keep-alive" 4 | wrk.headers["User-Agent"] = 5 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" 6 | wrk.headers["Content-Type"] = "application/json" 7 | -------------------------------------------------------------------------------- /wrk/wrk2.lua: -------------------------------------------------------------------------------- 1 | wrk.method = "POST" 2 | wrk.body = '{"operationName":null,"variables":{},"query":"{posts{title}}"}' 3 | wrk.headers["Connection"] = "keep-alive" 4 | wrk.headers["User-Agent"] = 5 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" 6 | wrk.headers["Content-Type"] = "application/json" 7 | -------------------------------------------------------------------------------- /wrk/wrk3.lua: -------------------------------------------------------------------------------- 1 | wrk.method = "POST" 2 | wrk.body = '{"operationName":null,"variables":{},"query":"{greet}"}' 3 | wrk.headers["Connection"] = "keep-alive" 4 | wrk.headers["User-Agent"] = 5 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" 6 | wrk.headers["Content-Type"] = "application/json" 7 | --------------------------------------------------------------------------------