diff --git a/next.config.js b/next.config.js index 56da077fae..3a99e7137e 100644 --- a/next.config.js +++ b/next.config.js @@ -37,7 +37,7 @@ const ALLOWED_SVG_REGEX = new RegExp(`${sep}icons${sep}.+\\.svg$`) const config = { output: undefined, // reactStrictMode: true, provoke duplicated codemirror editors - webpack(config) { + webpack(config, { isServer, dev }) { // #region MDX const mdxRule = config.module.rules.find(rule => rule.test?.test?.(".mdx")) if (mdxRule) { @@ -62,6 +62,19 @@ const config = { fileLoaderRule.exclude = /\.svg$/i config.module.rules.push( + { + test: /\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg|txt)$/i, + resourceQuery: /resource/, + type: "asset/resource", + generator: { + filename: "static/media/[name].[hash][ext]", + publicPath: "/_next/", + // Server build outputs to .next/server/, so go up to reach .next/static/ + outputPath: [isServer && "../", !dev && "../"] + .filter(Boolean) + .join(""), + }, + }, // All .svg from /icons/ and with ?svgr are going to be processed by @svgr/webpack { test: ALLOWED_SVG_REGEX, @@ -103,7 +116,7 @@ const config = { test: /\.svg$/i, exclude: ALLOWED_SVG_REGEX, resourceQuery: { - not: [...fileLoaderRule.resourceQuery.not, /svgr/], + not: [...fileLoaderRule.resourceQuery.not, /svgr|resource/], }, }, ) diff --git a/package.json b/package.json index d3b9d07d11..1d3a6faff6 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/nesting": "0.0.0-insiders.565cd3e", "@tailwindcss/typography": "^0.5.15", + "arktype": "2.1.28", "autoprefixer": "^10.4.20", "calendar-link": "^2.10.0", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0df41ae641..ba6e6906f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,9 @@ importers: '@tailwindcss/typography': specifier: ^0.5.15 version: 0.5.19(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.1)) + arktype: + specifier: 2.1.28 + version: 2.1.28 autoprefixer: specifier: ^10.4.20 version: 10.4.22(postcss@8.5.6) @@ -326,7 +329,7 @@ importers: scripts/sync-working-groups: dependencies: arktype: - specifier: ^2.1.27 + specifier: 2.1.28 version: 2.1.28 packages: diff --git a/scripts/get-github-info/github-stats.json b/scripts/get-github-info/github-stats.json index 9f6b835af8..dae0f6bf92 100644 --- a/scripts/get-github-info/github-stats.json +++ b/scripts/get-github-info/github-stats.json @@ -1,15 +1,15 @@ { "altair-graphql/altair": { "hasCommitsInLast3Months": false, - "stars": 5365, + "stars": 5372, "formattedStars": "5k", "license": "MIT License", - "lastRelease": "2025-10-28T22:43:22Z", - "formattedLastRelease": "1 month ago" + "lastRelease": "2025-12-12T08:48:15Z", + "formattedLastRelease": "12 hours ago" }, "apache/apisix": { "hasCommitsInLast3Months": false, - "stars": 15907, + "stars": 15968, "formattedStars": "16k", "license": "Apache License 2.0", "lastRelease": "2025-10-16T07:54:57Z", @@ -17,27 +17,27 @@ }, "apollographql/apollo-studio-community": { "hasCommitsInLast3Months": false, - "stars": 261, - "formattedStars": "261", + "stars": 260, + "formattedStars": "260", "license": "Unknown", "lastRelease": "", "formattedLastRelease": "" }, "ChilliCream/hotchocolate": { "hasCommitsInLast3Months": false, - "stars": 5637, + "stars": 5650, "formattedStars": "6k", "license": "MIT License", - "lastRelease": "2025-11-26T11:12:44Z", - "formattedLastRelease": "3 days ago" + "lastRelease": "2025-12-10T12:55:40Z", + "formattedLastRelease": "2 days ago" }, "dgraph-io/dgraph": { "hasCommitsInLast3Months": false, - "stars": 21367, + "stars": 21392, "formattedStars": "21k", "license": "Apache License 2.0", - "lastRelease": "2025-10-07T20:50:36Z", - "formattedLastRelease": "1 month ago" + "lastRelease": "2025-12-12T00:45:30Z", + "formattedLastRelease": "20 hours ago" }, "yahoo/elide": { "hasCommitsInLast3Months": false, @@ -45,7 +45,7 @@ "formattedStars": "1k", "license": "Other", "lastRelease": "2025-09-01T03:57:54Z", - "formattedLastRelease": "2 months ago" + "formattedLastRelease": "3 months ago" }, "graphapi-io/resources": { "hasCommitsInLast3Months": false, @@ -57,7 +57,7 @@ }, "hasura/graphql-engine": { "hasCommitsInLast3Months": false, - "stars": 31827, + "stars": 31835, "formattedStars": "32k", "license": "Apache License 2.0", "lastRelease": "2025-10-14T15:20:38Z", @@ -65,23 +65,23 @@ }, "graphql-hive/platform": { "hasCommitsInLast3Months": false, - "stars": 471, - "formattedStars": "471", + "stars": 472, + "formattedStars": "472", "license": "MIT License", - "lastRelease": "2025-11-25T15:15:47Z", - "formattedLastRelease": "3 days ago" + "lastRelease": "2025-12-11T15:59:16Z", + "formattedLastRelease": "1 day ago" }, "Kong/insomnia": { "hasCommitsInLast3Months": false, - "stars": 37611, + "stars": 37667, "formattedStars": "38k", "license": "Apache License 2.0", - "lastRelease": "2025-11-21T17:29:45Z", - "formattedLastRelease": "1 week ago" + "lastRelease": "2025-12-12T19:24:57Z", + "formattedLastRelease": "1 hour ago" }, "postmanlabs/postman-app-support": { "hasCommitsInLast3Months": false, - "stars": 5979, + "stars": 5982, "formattedStars": "6k", "license": "Unknown", "lastRelease": "", @@ -97,87 +97,39 @@ }, "TykTechnologies/tyk": { "hasCommitsInLast3Months": false, - "stars": 10515, + "stars": 10532, "formattedStars": "11k", "license": "Other", "lastRelease": "2025-11-28T16:32:34Z", - "formattedLastRelease": "19 hours ago" + "formattedLastRelease": "2 weeks ago" }, "twinlogix/typetta": { "hasCommitsInLast3Months": false, - "stars": 115, - "formattedStars": "115", + "stars": 116, + "formattedStars": "116", "license": "Apache License 2.0", "lastRelease": "2023-10-16T07:50:50Z", "formattedLastRelease": "2 years ago" }, "webiny/webiny-js": { "hasCommitsInLast3Months": false, - "stars": 7894, + "stars": 7900, "formattedStars": "8k", "license": "Other", - "lastRelease": "2025-09-16T08:29:00Z", - "formattedLastRelease": "2 months ago" + "lastRelease": "2025-12-09T11:36:01Z", + "formattedLastRelease": "3 days ago" }, "ballerina-platform/module-ballerina-graphql": { "hasCommitsInLast3Months": false, "stars": 138, "formattedStars": "138", "license": "Apache License 2.0", - "lastRelease": "2025-11-06T10:54:08Z", - "formattedLastRelease": "3 weeks ago" - }, - "oliyh/re-graph": { - "hasCommitsInLast3Months": false, - "stars": 464, - "formattedStars": "464", - "license": "Unknown", - "lastRelease": "2022-07-20T09:24:02Z", - "formattedLastRelease": "3 years ago" - }, - "microsoft/cppgraphqlgen": { - "hasCommitsInLast3Months": false, - "stars": 343, - "formattedStars": "343", - "license": "MIT License", - "lastRelease": "2024-12-10T17:25:31Z", - "formattedLastRelease": "11 months ago" - }, - "graphql/libgraphqlparser": { - "hasCommitsInLast3Months": false, - "stars": 1102, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "2017-10-16T21:47:42Z", - "formattedLastRelease": "8 years ago" - }, - "alumbra/alumbra": { - "hasCommitsInLast3Months": false, - "stars": 148, - "formattedStars": "148", - "license": "MIT License", - "lastRelease": "2017-06-12T12:14:25Z", - "formattedLastRelease": "8 years ago" - }, - "tendant/graphql-clj": { - "hasCommitsInLast3Months": false, - "stars": 285, - "formattedStars": "285", - "license": "Eclipse Public License 1.0", - "lastRelease": "", - "formattedLastRelease": "" - }, - "walmartlabs/lacinia": { - "hasCommitsInLast3Months": false, - "stars": 1843, - "formattedStars": "2k", - "license": "Other", - "lastRelease": "", - "formattedLastRelease": "" + "lastRelease": "2025-12-08T12:39:34Z", + "formattedLastRelease": "4 days ago" }, "graphql-dotnet/graphql-client": { "hasCommitsInLast3Months": false, - "stars": 644, + "stars": 645, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2024-05-21T07:06:30Z", @@ -197,7 +149,7 @@ "formattedStars": "8", "license": "MIT License", "lastRelease": "2025-11-14T07:39:17Z", - "formattedLastRelease": "2 weeks ago" + "formattedLastRelease": "4 weeks ago" }, "sahb1239/SAHB.GraphQLClient": { "hasCommitsInLast3Months": false, @@ -217,19 +169,19 @@ }, "EntityGraphQL/EntityGraphQL": { "hasCommitsInLast3Months": false, - "stars": 449, - "formattedStars": "449", + "stars": 450, + "formattedStars": "450", "license": "MIT License", - "lastRelease": "2025-09-16T00:35:14Z", - "formattedLastRelease": "2 months ago" + "lastRelease": "2025-12-01T22:09:40Z", + "formattedLastRelease": "1 week ago" }, "graphql-dotnet/graphql-dotnet": { "hasCommitsInLast3Months": false, - "stars": 5975, + "stars": 5976, "formattedStars": "6k", "license": "MIT License", "lastRelease": "2025-11-17T17:57:35Z", - "formattedLastRelease": "1 week ago" + "formattedLastRelease": "3 weeks ago" }, "chkimes/graphql-net": { "hasCommitsInLast3Months": false, @@ -247,37 +199,37 @@ "lastRelease": "", "formattedLastRelease": "" }, - "burner/graphqld": { + "microsoft/cppgraphqlgen": { "hasCommitsInLast3Months": false, - "stars": 35, - "formattedStars": "35", - "license": "GNU Lesser General Public License v3.0", - "lastRelease": "2024-05-14T13:42:29Z", + "stars": 343, + "formattedStars": "343", + "license": "MIT License", + "lastRelease": "2024-12-10T17:25:31Z", "formattedLastRelease": "1 year ago" }, - "annkissam/common_graphql_client": { + "graphql/libgraphqlparser": { "hasCommitsInLast3Months": false, - "stars": 42, - "formattedStars": "42", + "stars": 1102, + "formattedStars": "1k", "license": "MIT License", - "lastRelease": "2020-05-05T16:48:50Z", - "formattedLastRelease": "5 years ago" + "lastRelease": "2017-10-16T21:47:42Z", + "formattedLastRelease": "8 years ago" }, - "uesteibar/neuron": { + "burner/graphqld": { "hasCommitsInLast3Months": false, - "stars": 333, - "formattedStars": "333", - "license": "Other", - "lastRelease": "", - "formattedLastRelease": "" + "stars": 35, + "formattedStars": "35", + "license": "GNU Lesser General Public License v3.0", + "lastRelease": "2024-05-14T13:42:29Z", + "formattedLastRelease": "1 year ago" }, "absinthe-graphql/absinthe": { "hasCommitsInLast3Months": false, - "stars": 4374, + "stars": 4379, "formattedStars": "4k", "license": "Other", "lastRelease": "2025-11-21T15:08:24Z", - "formattedLastRelease": "1 week ago" + "formattedLastRelease": "3 weeks ago" }, "graphql-elixir/graphql": { "hasCommitsInLast3Months": false, @@ -295,6 +247,14 @@ "lastRelease": "", "formattedLastRelease": "" }, + "oliyh/re-graph": { + "hasCommitsInLast3Months": false, + "stars": 464, + "formattedStars": "464", + "license": "Unknown", + "lastRelease": "2022-07-20T09:24:02Z", + "formattedLastRelease": "3 years ago" + }, "jlouis/graphql-erlang": { "hasCommitsInLast3Months": false, "stars": 314, @@ -303,9 +263,33 @@ "lastRelease": "2018-06-22T12:35:43Z", "formattedLastRelease": "7 years ago" }, + "alumbra/alumbra": { + "hasCommitsInLast3Months": false, + "stars": 148, + "formattedStars": "148", + "license": "MIT License", + "lastRelease": "2017-06-12T12:14:25Z", + "formattedLastRelease": "8 years ago" + }, + "tendant/graphql-clj": { + "hasCommitsInLast3Months": false, + "stars": 285, + "formattedStars": "285", + "license": "Eclipse Public License 1.0", + "lastRelease": "", + "formattedLastRelease": "" + }, + "walmartlabs/lacinia": { + "hasCommitsInLast3Months": false, + "stars": 1843, + "formattedStars": "2k", + "license": "Other", + "lastRelease": "", + "formattedLastRelease": "" + }, "gql-dart/ferry": { "hasCommitsInLast3Months": false, - "stars": 628, + "stars": 630, "formattedStars": "1k", "license": "MIT License", "lastRelease": "", @@ -319,9 +303,33 @@ "lastRelease": "2025-10-21T16:42:55Z", "formattedLastRelease": "1 month ago" }, + "annkissam/common_graphql_client": { + "hasCommitsInLast3Months": false, + "stars": 42, + "formattedStars": "42", + "license": "MIT License", + "lastRelease": "2020-05-05T16:48:50Z", + "formattedLastRelease": "5 years ago" + }, + "uesteibar/neuron": { + "hasCommitsInLast3Months": false, + "stars": 333, + "formattedStars": "333", + "license": "Other", + "lastRelease": "", + "formattedLastRelease": "" + }, + "dosco/graphjin": { + "hasCommitsInLast3Months": false, + "stars": 3014, + "formattedStars": "3k", + "license": "Apache License 2.0", + "lastRelease": "2025-11-05T07:51:12Z", + "formattedLastRelease": "1 month ago" + }, "Khan/genqlient": { "hasCommitsInLast3Months": false, - "stars": 1266, + "stars": 1274, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2025-05-18T19:09:08Z", @@ -329,15 +337,15 @@ }, "hasura/go-graphql-client": { "hasCommitsInLast3Months": false, - "stars": 459, - "formattedStars": "459", + "stars": 461, + "formattedStars": "461", "license": "MIT License", "lastRelease": "2025-11-05T06:45:53Z", - "formattedLastRelease": "3 weeks ago" + "formattedLastRelease": "1 month ago" }, "shurcooL/graphql": { "hasCommitsInLast3Months": false, - "stars": 728, + "stars": 727, "formattedStars": "1k", "license": "MIT License", "lastRelease": "", @@ -353,11 +361,11 @@ }, "99designs/gqlgen": { "hasCommitsInLast3Months": false, - "stars": 10593, + "stars": 10607, "formattedStars": "11k", "license": "MIT License", "lastRelease": "2025-11-24T16:56:20Z", - "formattedLastRelease": "4 days ago" + "formattedLastRelease": "2 weeks ago" }, "andrewwphillips/eggql": { "hasCommitsInLast3Months": false, @@ -377,15 +385,15 @@ }, "graph-gophers/graphql-go": { "hasCommitsInLast3Months": false, - "stars": 4741, + "stars": 4743, "formattedStars": "5k", "license": "BSD 2-Clause \"Simplified\" License", "lastRelease": "2025-09-09T11:37:07Z", - "formattedLastRelease": "2 months ago" + "formattedLastRelease": "3 months ago" }, "graphql-go/graphql": { "hasCommitsInLast3Months": false, - "stars": 10132, + "stars": 10135, "formattedStars": "10k", "license": "MIT License", "lastRelease": "2023-04-10T18:20:23Z", @@ -409,43 +417,43 @@ }, "wundergraph/graphql-go-tools": { "hasCommitsInLast3Months": false, - "stars": 790, + "stars": 814, "formattedStars": "1k", "license": "MIT License", - "lastRelease": "2025-11-21T21:18:20Z", - "formattedLastRelease": "1 week ago" + "lastRelease": "2025-12-10T11:25:46Z", + "formattedLastRelease": "2 days ago" }, - "dosco/graphjin": { + "morpheusgraphql/morpheus-graphql": { "hasCommitsInLast3Months": false, - "stars": 3011, - "formattedStars": "3k", - "license": "Apache License 2.0", - "lastRelease": "2025-11-05T07:51:12Z", - "formattedLastRelease": "3 weeks ago" + "stars": 418, + "formattedStars": "418", + "license": "MIT License", + "lastRelease": "2024-06-10T08:34:35Z", + "formattedLastRelease": "1 year ago" }, - "grails/gorm-graphql": { + "apollographql/apollo-kotlin": { "hasCommitsInLast3Months": false, - "stars": 81, - "formattedStars": "81", - "license": "Unknown", - "lastRelease": "2023-12-08T10:48:05Z", - "formattedLastRelease": "1 year ago" + "stars": 3930, + "formattedStars": "4k", + "license": "MIT License", + "lastRelease": "2025-11-13T17:33:51Z", + "formattedLastRelease": "4 weeks ago" }, - "grooviter/gql": { + "ExpediaGroup/graphql-kotlin": { "hasCommitsInLast3Months": false, - "stars": 49, - "formattedStars": "49", + "stars": 1796, + "formattedStars": "2k", "license": "Apache License 2.0", - "lastRelease": "2024-11-05T10:13:23Z", - "formattedLastRelease": "1 year ago" + "lastRelease": "2025-06-16T17:02:18Z", + "formattedLastRelease": "5 months ago" }, - "morpheusgraphql/morpheus-graphql": { + "americanexpress/nodes": { "hasCommitsInLast3Months": false, - "stars": 418, - "formattedStars": "418", - "license": "MIT License", - "lastRelease": "2024-06-10T08:34:35Z", - "formattedLastRelease": "1 year ago" + "stars": 307, + "formattedStars": "307", + "license": "Apache License 2.0", + "lastRelease": "2019-07-13T22:47:01Z", + "formattedLastRelease": "6 years ago" }, "jasonsychau/graphql-w-persistent": { "hasCommitsInLast3Months": false, @@ -463,47 +471,135 @@ "lastRelease": "2021-01-11T11:19:38Z", "formattedLastRelease": "4 years ago" }, - "apollographql/apollo-client": { + "grails/gorm-graphql": { "hasCommitsInLast3Months": false, - "stars": 19673, - "formattedStars": "20k", - "license": "MIT License", - "lastRelease": "2025-11-19T01:20:25Z", - "formattedLastRelease": "1 week ago" + "stars": 81, + "formattedStars": "81", + "license": "Unknown", + "lastRelease": "2023-12-08T10:48:05Z", + "formattedLastRelease": "2 years ago" }, - "aws-amplify/amplify-js": { + "grooviter/gql": { "hasCommitsInLast3Months": false, - "stars": 9567, - "formattedStars": "10k", + "stars": 49, + "formattedStars": "49", "license": "Apache License 2.0", - "lastRelease": "2025-11-06T13:36:19Z", - "formattedLastRelease": "3 weeks ago" + "lastRelease": "2024-11-05T10:13:23Z", + "formattedLastRelease": "1 year ago" }, - "Houfeng/gq-loader": { + "graphql-java-generator/graphql-gradle-plugin-project": { "hasCommitsInLast3Months": false, - "stars": 59, - "formattedStars": "59", - "license": "Unknown", + "stars": 57, + "formattedStars": "57", + "license": "MIT License", "lastRelease": "", "formattedLastRelease": "" }, - "gqty-dev/gqty": { + "graphql-calculator/graphql-calculator": { "hasCommitsInLast3Months": false, - "stars": 1030, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "2025-10-26T19:29:38Z", - "formattedLastRelease": "1 month ago" + "stars": 112, + "formattedStars": "112", + "license": "Apache License 2.0", + "lastRelease": "2021-09-03T01:56:25Z", + "formattedLastRelease": "4 years ago" }, - "grafoojs/grafoo": { + "graphql-java-kickstart/graphql-spring-boot": { "hasCommitsInLast3Months": false, - "stars": 274, - "formattedStars": "274", + "stars": 1513, + "formattedStars": "2k", "license": "MIT License", - "lastRelease": "2018-06-20T15:21:00Z", - "formattedLastRelease": "7 years ago" + "lastRelease": "2023-12-07T11:07:47Z", + "formattedLastRelease": "2 years ago" }, - "badbatch/graphql-box": { + "graphql-java/graphql-java": { + "hasCommitsInLast3Months": false, + "stars": 6227, + "formattedStars": "6k", + "license": "MIT License", + "lastRelease": "2025-11-10T01:21:35Z", + "formattedLastRelease": "1 month ago" + }, + "babyfish-ct/jimmer": { + "hasCommitsInLast3Months": false, + "stars": 1581, + "formattedStars": "2k", + "license": "Apache License 2.0", + "lastRelease": "2025-11-26T13:05:27Z", + "formattedLastRelease": "2 weeks ago" + }, + "aPureBase/KGraphQL": { + "hasCommitsInLast3Months": false, + "stars": 308, + "formattedStars": "308", + "license": "MIT License", + "lastRelease": "2023-01-27T10:09:55Z", + "formattedLastRelease": "2 years ago" + }, + "eclipse/microprofile-graphql": { + "hasCommitsInLast3Months": false, + "stars": 101, + "formattedStars": "101", + "license": "Apache License 2.0", + "lastRelease": "2022-03-21T18:26:51Z", + "formattedLastRelease": "3 years ago" + }, + "netflix/dgs-framework": { + "hasCommitsInLast3Months": false, + "stars": 3281, + "formattedStars": "3k", + "license": "Apache License 2.0", + "lastRelease": "2025-12-05T21:45:25Z", + "formattedLastRelease": "6 days ago" + }, + "spring-projects/spring-graphql": { + "hasCommitsInLast3Months": false, + "stars": 1580, + "formattedStars": "2k", + "license": "Apache License 2.0", + "lastRelease": "2025-11-18T10:05:26Z", + "formattedLastRelease": "3 weeks ago" + }, + "apollographql/apollo-client": { + "hasCommitsInLast3Months": false, + "stars": 19681, + "formattedStars": "20k", + "license": "MIT License", + "lastRelease": "2025-12-10T08:08:29Z", + "formattedLastRelease": "2 days ago" + }, + "aws-amplify/amplify-js": { + "hasCommitsInLast3Months": false, + "stars": 9570, + "formattedStars": "10k", + "license": "Apache License 2.0", + "lastRelease": "2025-12-10T14:00:23Z", + "formattedLastRelease": "2 days ago" + }, + "Houfeng/gq-loader": { + "hasCommitsInLast3Months": false, + "stars": 59, + "formattedStars": "59", + "license": "Unknown", + "lastRelease": "", + "formattedLastRelease": "" + }, + "gqty-dev/gqty": { + "hasCommitsInLast3Months": false, + "stars": 1031, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "2025-10-26T19:29:38Z", + "formattedLastRelease": "1 month ago" + }, + "grafoojs/grafoo": { + "hasCommitsInLast3Months": false, + "stars": 274, + "formattedStars": "274", + "license": "MIT License", + "lastRelease": "2018-06-20T15:21:00Z", + "formattedLastRelease": "7 years ago" + }, + "badbatch/graphql-box": { "hasCommitsInLast3Months": false, "stars": 27, "formattedStars": "27", @@ -513,32 +609,32 @@ }, "nearform/graphql-hooks": { "hasCommitsInLast3Months": false, - "stars": 1889, + "stars": 1888, "formattedStars": "2k", "license": "Other", "lastRelease": "2025-01-08T18:45:52Z", - "formattedLastRelease": "10 months ago" + "formattedLastRelease": "11 months ago" }, "graphql/graphql-http": { "hasCommitsInLast3Months": false, - "stars": 357, - "formattedStars": "357", + "stars": 359, + "formattedStars": "359", "license": "MIT License", "lastRelease": "2025-01-17T14:16:52Z", "formattedLastRelease": "10 months ago" }, "jasonkuhrt/graphql-request": { "hasCommitsInLast3Months": false, - "stars": 6081, + "stars": 6086, "formattedStars": "6k", "license": "MIT License", - "lastRelease": "2025-11-25T16:55:56Z", - "formattedLastRelease": "3 days ago" + "lastRelease": "2025-12-12T15:51:04Z", + "formattedLastRelease": "4 hours ago" }, "enisdenjo/graphql-sse": { "hasCommitsInLast3Months": false, - "stars": 435, - "formattedStars": "435", + "stars": 438, + "formattedStars": "438", "license": "MIT License", "lastRelease": "2025-10-22T16:19:40Z", "formattedLastRelease": "1 month ago" @@ -553,7 +649,7 @@ }, "enisdenjo/graphql-ws": { "hasCommitsInLast3Months": false, - "stars": 1843, + "stars": 1846, "formattedStars": "2k", "license": "MIT License", "lastRelease": "2025-07-14T12:15:37Z", @@ -585,59 +681,43 @@ }, "facebook/relay": { "hasCommitsInLast3Months": false, - "stars": 18893, + "stars": 18900, "formattedStars": "19k", "license": "MIT License", "lastRelease": "2025-08-06T23:45:00Z", - "formattedLastRelease": "3 months ago" + "formattedLastRelease": "4 months ago" }, "FormidableLabs/urql": { "hasCommitsInLast3Months": false, - "stars": 8900, + "stars": 8905, "formattedStars": "9k", "license": "MIT License", "lastRelease": "2025-08-29T08:06:41Z", "formattedLastRelease": "3 months ago" }, - "apollographql/apollo-server": { - "hasCommitsInLast3Months": false, - "stars": 13926, - "formattedStars": "14k", - "license": "MIT License", - "lastRelease": "2025-11-21T23:19:03Z", - "formattedLastRelease": "1 week ago" - }, - "graphql/graphql-js": { + "neomatrixcode/Diana.jl": { "hasCommitsInLast3Months": false, - "stars": 20279, - "formattedStars": "20k", + "stars": 117, + "formattedStars": "117", "license": "MIT License", - "lastRelease": "2025-11-01T14:18:53Z", - "formattedLastRelease": "3 weeks ago" + "lastRelease": "2022-08-16T03:22:22Z", + "formattedLastRelease": "3 years ago" }, - "dotansimha/graphql-yoga": { + "DeloitteDigitalAPAC/GraphQLClient.jl": { "hasCommitsInLast3Months": false, - "stars": 8455, - "formattedStars": "8k", - "license": "MIT License", - "lastRelease": "2025-11-28T11:05:21Z", - "formattedLastRelease": "1 day ago" + "stars": 47, + "formattedStars": "47", + "license": "Other", + "lastRelease": "2022-10-26T16:48:16Z", + "formattedLastRelease": "3 years ago" }, - "mercurius-js/mercurius": { + "andreas/ocaml-graphql-server": { "hasCommitsInLast3Months": false, - "stars": 2462, - "formattedStars": "2k", + "stars": 621, + "formattedStars": "1k", "license": "MIT License", - "lastRelease": "2025-11-13T14:12:18Z", - "formattedLastRelease": "2 weeks ago" - }, - "getcronit/pylon": { - "hasCommitsInLast3Months": false, - "stars": 345, - "formattedStars": "345", - "license": "Apache License 2.0", - "lastRelease": "2025-10-01T08:35:15Z", - "formattedLastRelease": "1 month ago" + "lastRelease": "2022-07-08T16:26:45Z", + "formattedLastRelease": "3 years ago" }, "networkimprov/brangr": { "hasCommitsInLast3Months": false, @@ -649,19 +729,19 @@ }, "hayes/giraphql": { "hasCommitsInLast3Months": false, - "stars": 2553, + "stars": 2561, "formattedStars": "3k", "license": "ISC License", - "lastRelease": "2025-11-10T01:29:18Z", - "formattedLastRelease": "2 weeks ago" + "lastRelease": "2025-12-10T22:07:12Z", + "formattedLastRelease": "1 day ago" }, "graphql/graphiql": { "hasCommitsInLast3Months": false, - "stars": 16715, + "stars": 16730, "formattedStars": "17k", "license": "MIT License", - "lastRelease": "2025-11-01T22:30:04Z", - "formattedLastRelease": "3 weeks ago" + "lastRelease": "2025-11-30T09:04:01Z", + "formattedLastRelease": "1 week ago" }, "Urigo/graphql-cli": { "hasCommitsInLast3Months": false, @@ -673,11 +753,11 @@ }, "dotansimha/graphql-code-generator": { "hasCommitsInLast3Months": false, - "stars": 11179, + "stars": 11183, "formattedStars": "11k", "license": "MIT License", "lastRelease": "2025-11-29T04:07:19Z", - "formattedLastRelease": "7 hours ago" + "formattedLastRelease": "1 week ago" }, "kamilkisiela/graphql-config": { "hasCommitsInLast3Months": false, @@ -689,7 +769,7 @@ }, "dimaMachina/graphql-eslint/": { "hasCommitsInLast3Months": false, - "stars": 831, + "stars": 832, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2025-03-26T14:11:23Z", @@ -700,8 +780,8 @@ "stars": 1725, "formattedStars": "2k", "license": "MIT License", - "lastRelease": "2025-11-15T02:42:13Z", - "formattedLastRelease": "2 weeks ago" + "lastRelease": "2025-12-10T22:16:19Z", + "formattedLastRelease": "1 day ago" }, "graphql/graphql-language-service": { "hasCommitsInLast3Months": false, @@ -713,18 +793,18 @@ }, "n1ru4l/graphql-live-query": { "hasCommitsInLast3Months": false, - "stars": 440, - "formattedStars": "440", + "stars": 441, + "formattedStars": "441", "license": "MIT License", "lastRelease": "2022-07-29T09:27:53Z", "formattedLastRelease": "3 years ago" }, "Urigo/graphql-mesh": { "hasCommitsInLast3Months": false, - "stars": 3463, + "stars": 3465, "formattedStars": "3k", "license": "MIT License", - "lastRelease": "2025-11-19T12:12:00Z", + "lastRelease": "2025-12-04T22:32:32Z", "formattedLastRelease": "1 week ago" }, "maticzav/graphql-middleware": { @@ -745,7 +825,7 @@ }, "Urigo/graphql-scalars": { "hasCommitsInLast3Months": false, - "stars": 1927, + "stars": 1929, "formattedStars": "2k", "license": "MIT License", "lastRelease": "2025-10-14T23:00:24Z", @@ -753,7 +833,7 @@ }, "maticzav/graphql-shield": { "hasCommitsInLast3Months": false, - "stars": 3574, + "stars": 3573, "formattedStars": "4k", "license": "MIT License", "lastRelease": "2022-11-22T19:08:37Z", @@ -761,11 +841,11 @@ }, "ardatan/graphql-tools": { "hasCommitsInLast3Months": false, - "stars": 5418, + "stars": 5416, "formattedStars": "5k", "license": "MIT License", "lastRelease": "2025-11-28T10:05:14Z", - "formattedLastRelease": "1 day ago" + "formattedLastRelease": "2 weeks ago" }, "anvilco/graphql-introspection-tools": { "hasCommitsInLast3Months": false, @@ -777,7 +857,7 @@ }, "graphile/postgraphile": { "hasCommitsInLast3Months": false, - "stars": 12858, + "stars": 12866, "formattedStars": "13k", "license": "Other", "lastRelease": "2023-10-05T16:27:00Z", @@ -785,7 +865,7 @@ }, "Urigo/SOFA": { "hasCommitsInLast3Months": false, - "stars": 1111, + "stars": 1112, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2024-12-16T10:06:41Z", @@ -793,59 +873,131 @@ }, "anvilco/spectaql": { "hasCommitsInLast3Months": false, - "stars": 1205, + "stars": 1207, "formattedStars": "1k", "license": "MIT License", "lastRelease": "", "formattedLastRelease": "" }, - "neomatrixcode/Diana.jl": { + "apollographql/apollo-server": { "hasCommitsInLast3Months": false, - "stars": 117, - "formattedStars": "117", + "stars": 13930, + "formattedStars": "14k", "license": "MIT License", - "lastRelease": "2022-08-16T03:22:22Z", - "formattedLastRelease": "3 years ago" + "lastRelease": "2025-11-21T23:19:03Z", + "formattedLastRelease": "2 weeks ago" }, - "DeloitteDigitalAPAC/GraphQLClient.jl": { + "graphql/graphql-js": { "hasCommitsInLast3Months": false, - "stars": 47, - "formattedStars": "47", - "license": "Other", - "lastRelease": "2022-10-26T16:48:16Z", - "formattedLastRelease": "3 years ago" + "stars": 20290, + "formattedStars": "20k", + "license": "MIT License", + "lastRelease": "2025-11-01T14:18:53Z", + "formattedLastRelease": "1 month ago" }, - "andreas/ocaml-graphql-server": { + "dotansimha/graphql-yoga": { "hasCommitsInLast3Months": false, - "stars": 621, - "formattedStars": "1k", + "stars": 8460, + "formattedStars": "8k", "license": "MIT License", - "lastRelease": "2022-07-08T16:26:45Z", - "formattedLastRelease": "3 years ago" + "lastRelease": "2025-12-02T00:13:11Z", + "formattedLastRelease": "1 week ago" + }, + "mercurius-js/mercurius": { + "hasCommitsInLast3Months": false, + "stars": 2464, + "formattedStars": "2k", + "license": "MIT License", + "lastRelease": "2025-11-13T14:12:18Z", + "formattedLastRelease": "4 weeks ago" + }, + "getcronit/pylon": { + "hasCommitsInLast3Months": false, + "stars": 348, + "formattedStars": "348", + "license": "Apache License 2.0", + "lastRelease": "2025-10-01T08:35:15Z", + "formattedLastRelease": "2 months ago" }, "graphql-perl/graphql-perl": { "hasCommitsInLast3Months": false, - "stars": 73, - "formattedStars": "73", + "stars": 72, + "formattedStars": "72", + "license": "Unknown", + "lastRelease": "", + "formattedLastRelease": "" + }, + "mirumee/ariadne-codegen": { + "hasCommitsInLast3Months": false, + "stars": 373, + "formattedStars": "373", + "license": "BSD 3-Clause \"New\" or \"Revised\" License", + "lastRelease": "2025-12-06T19:39:02Z", + "formattedLastRelease": "6 days ago" + }, + "graphql-python/gql": { + "hasCommitsInLast3Months": false, + "stars": 1654, + "formattedStars": "2k", + "license": "MIT License", + "lastRelease": "2025-09-05T14:22:54Z", + "formattedLastRelease": "3 months ago" + }, + "denisart/graphql-query": { + "hasCommitsInLast3Months": false, + "stars": 66, + "formattedStars": "66", + "license": "MIT License", + "lastRelease": "2024-07-31T10:54:53Z", + "formattedLastRelease": "1 year ago" + }, + "prisma-labs/python-graphql-client": { + "hasCommitsInLast3Months": false, + "stars": 156, + "formattedStars": "156", + "license": "MIT License", + "lastRelease": "", + "formattedLastRelease": "" + }, + "dsal3389/ql": { + "hasCommitsInLast3Months": false, + "stars": 9, + "formattedStars": "9", "license": "Unknown", + "lastRelease": "2025-02-04T17:36:51Z", + "formattedLastRelease": "10 months ago" + }, + "qlient-org/python-qlient": { + "hasCommitsInLast3Months": false, + "stars": 46, + "formattedStars": "46", + "license": "MIT License", + "lastRelease": "2022-07-29T16:10:08Z", + "formattedLastRelease": "3 years ago" + }, + "profusion/sgqlc": { + "hasCommitsInLast3Months": false, + "stars": 547, + "formattedStars": "1k", + "license": "ISC License", "lastRelease": "", "formattedLastRelease": "" }, "api-platform/api-platform": { "hasCommitsInLast3Months": false, - "stars": 9054, + "stars": 9066, "formattedStars": "9k", "license": "MIT License", "lastRelease": "2025-03-11T16:15:41Z", - "formattedLastRelease": "8 months ago" + "formattedLastRelease": "9 months ago" }, "GatoGraphQL/GatoGraphQL": { "hasCommitsInLast3Months": false, - "stars": 376, - "formattedStars": "376", + "stars": 377, + "formattedStars": "377", "license": "GNU General Public License v2.0", "lastRelease": "2025-11-26T08:29:30Z", - "formattedLastRelease": "3 days ago" + "formattedLastRelease": "2 weeks ago" }, "infinityloop-dev/graphpinator": { "hasCommitsInLast3Months": false, @@ -861,15 +1013,15 @@ "formattedStars": "16", "license": "MIT License", "lastRelease": "2025-10-11T09:19:14Z", - "formattedLastRelease": "1 month ago" + "formattedLastRelease": "2 months ago" }, "webonyx/graphql-php": { "hasCommitsInLast3Months": false, - "stars": 4702, + "stars": 4706, "formattedStars": "5k", "license": "MIT License", - "lastRelease": "2025-11-20T11:51:16Z", - "formattedLastRelease": "1 week ago" + "lastRelease": "2025-12-09T07:31:19Z", + "formattedLastRelease": "3 days ago" }, "ivome/graphql-relay-php": { "hasCommitsInLast3Months": false, @@ -885,7 +1037,7 @@ "formattedStars": "1k", "license": "MIT License", "lastRelease": "2025-10-31T08:00:22Z", - "formattedLastRelease": "4 weeks ago" + "formattedLastRelease": "1 month ago" }, "thecodingmachine/graphqlite": { "hasCommitsInLast3Months": false, @@ -893,15 +1045,15 @@ "formattedStars": "1k", "license": "MIT License", "lastRelease": "2025-09-04T16:39:26Z", - "formattedLastRelease": "2 months ago" + "formattedLastRelease": "3 months ago" }, "nuwave/lighthouse": { "hasCommitsInLast3Months": false, - "stars": 3465, + "stars": 3471, "formattedStars": "3k", "license": "MIT License", - "lastRelease": "2025-09-11T08:07:50Z", - "formattedLastRelease": "2 months ago" + "lastRelease": "2025-12-05T08:19:37Z", + "formattedLastRelease": "1 week ago" }, "railt/railt": { "hasCommitsInLast3Months": false, @@ -929,71 +1081,63 @@ }, "wp-graphql/wp-graphql": { "hasCommitsInLast3Months": false, - "stars": 3759, + "stars": 3758, "formattedStars": "4k", "license": "GNU General Public License v3.0", "lastRelease": "2025-11-24T22:39:55Z", - "formattedLastRelease": "4 days ago" + "formattedLastRelease": "2 weeks ago" }, - "mirumee/ariadne-codegen": { + "ropensci/ghql": { "hasCommitsInLast3Months": false, - "stars": 368, - "formattedStars": "368", - "license": "BSD 3-Clause \"New\" or \"Revised\" License", - "lastRelease": "2025-10-13T06:38:02Z", - "formattedLastRelease": "1 month ago" + "stars": 149, + "formattedStars": "149", + "license": "Other", + "lastRelease": "2025-09-08T08:41:00Z", + "formattedLastRelease": "3 months ago" }, - "graphql-python/gql": { + "ghostdogpr/caliban": { "hasCommitsInLast3Months": false, - "stars": 1653, - "formattedStars": "2k", - "license": "MIT License", - "lastRelease": "2025-09-05T14:22:54Z", - "formattedLastRelease": "2 months ago" + "stars": 977, + "formattedStars": "1k", + "license": "Apache License 2.0", + "lastRelease": "2025-07-14T00:24:20Z", + "formattedLastRelease": "4 months ago" }, - "denisart/graphql-query": { + "ohler55/agoo": { "hasCommitsInLast3Months": false, - "stars": 66, - "formattedStars": "66", + "stars": 924, + "formattedStars": "1k", "license": "MIT License", - "lastRelease": "2024-07-31T10:54:53Z", - "formattedLastRelease": "1 year ago" + "lastRelease": "2025-09-24T22:20:23Z", + "formattedLastRelease": "2 months ago" }, - "prisma-labs/python-graphql-client": { + "rmosolgo/graphql-ruby": { "hasCommitsInLast3Months": false, - "stars": 156, - "formattedStars": "156", + "stars": 5427, + "formattedStars": "5k", "license": "MIT License", - "lastRelease": "", - "formattedLastRelease": "" - }, - "dsal3389/ql": { - "hasCommitsInLast3Months": false, - "stars": 9, - "formattedStars": "9", - "license": "Unknown", - "lastRelease": "2025-02-04T17:36:51Z", - "formattedLastRelease": "9 months ago" + "lastRelease": "2025-07-19T17:15:49Z", + "formattedLastRelease": "4 months ago" }, - "qlient-org/python-qlient": { + "virtualshield/rails-graphql": { "hasCommitsInLast3Months": false, - "stars": 46, - "formattedStars": "46", + "stars": 187, + "formattedStars": "187", "license": "MIT License", - "lastRelease": "2022-07-29T16:10:08Z", - "formattedLastRelease": "3 years ago" + "lastRelease": "2025-08-25T17:53:38Z", + "formattedLastRelease": "3 months ago" }, - "profusion/sgqlc": { + "sangria-graphql/sangria": { "hasCommitsInLast3Months": false, - "stars": 546, - "formattedStars": "1k", - "license": "ISC License", - "lastRelease": "", - "formattedLastRelease": "" + "stars": 1957, + "formattedStars": "2k", + "license": "Apache License 2.0", + "lastRelease": "2025-10-20T11:40:30Z", + "formattedLastRelease": "1 month ago" }, "mirumee/ariadne": { "hasCommitsInLast3Months": false, - "stars": 2309, + "stars": 2310, "formattedStars": "2k", "license": "BSD 3-Clause \"New\" or \"Revised\" License", "lastRelease": "2025-04-18T08:27:47Z", @@ -1017,7 +1161,7 @@ }, "graphql-python/graphene": { "hasCommitsInLast3Months": false, - "stars": 8239, + "stars": 8240, "formattedStars": "8k", "license": "MIT License", "lastRelease": "2024-11-09T20:43:58Z", @@ -1025,203 +1169,131 @@ }, "strawberry-graphql/strawberry": { "hasCommitsInLast3Months": false, - "stars": 4569, + "stars": 4571, "formattedStars": "5k", "license": "MIT License", - "lastRelease": "2025-11-22T13:00:06Z", - "formattedLastRelease": "6 days ago" + "lastRelease": "2025-12-12T11:49:36Z", + "formattedLastRelease": "9 hours ago" }, "tartiflette/tartiflette": { "hasCommitsInLast3Months": false, - "stars": 856, + "stars": 854, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2021-11-15T11:05:03Z", "formattedLastRelease": "4 years ago" }, - "apollographql/apollo-kotlin": { - "hasCommitsInLast3Months": false, - "stars": 3929, - "formattedStars": "4k", - "license": "MIT License", - "lastRelease": "2025-11-13T17:33:51Z", - "formattedLastRelease": "2 weeks ago" - }, - "ExpediaGroup/graphql-kotlin": { - "hasCommitsInLast3Months": false, - "stars": 1795, - "formattedStars": "2k", - "license": "Apache License 2.0", - "lastRelease": "2025-06-16T17:02:18Z", - "formattedLastRelease": "5 months ago" - }, - "americanexpress/nodes": { - "hasCommitsInLast3Months": false, - "stars": 307, - "formattedStars": "307", - "license": "Apache License 2.0", - "lastRelease": "2019-07-13T22:47:01Z", - "formattedLastRelease": "6 years ago" - }, - "graphql-calculator/graphql-calculator": { - "hasCommitsInLast3Months": false, - "stars": 112, - "formattedStars": "112", - "license": "Apache License 2.0", - "lastRelease": "2021-09-03T01:56:25Z", - "formattedLastRelease": "4 years ago" - }, - "graphql-java-kickstart/graphql-spring-boot": { - "hasCommitsInLast3Months": false, - "stars": 1513, - "formattedStars": "2k", - "license": "MIT License", - "lastRelease": "2023-12-07T11:07:47Z", - "formattedLastRelease": "1 year ago" - }, - "graphql-java/graphql-java": { - "hasCommitsInLast3Months": false, - "stars": 6226, - "formattedStars": "6k", - "license": "MIT License", - "lastRelease": "2025-11-10T01:21:35Z", - "formattedLastRelease": "2 weeks ago" - }, - "babyfish-ct/jimmer": { + "obmarg/cynic": { "hasCommitsInLast3Months": false, - "stars": 1565, - "formattedStars": "2k", - "license": "Apache License 2.0", - "lastRelease": "2025-11-26T13:05:27Z", - "formattedLastRelease": "2 days ago" + "stars": 443, + "formattedStars": "443", + "license": "Mozilla Public License 2.0", + "lastRelease": "2025-08-19T19:37:22Z", + "formattedLastRelease": "3 months ago" }, - "aPureBase/KGraphQL": { + "arthurkhlghatyan/gql-client-rs": { "hasCommitsInLast3Months": false, - "stars": 308, - "formattedStars": "308", + "stars": 51, + "formattedStars": "51", "license": "MIT License", - "lastRelease": "2023-01-27T10:09:55Z", - "formattedLastRelease": "2 years ago" + "lastRelease": "2025-06-07T14:31:10Z", + "formattedLastRelease": "6 months ago" }, - "eclipse/microprofile-graphql": { + "async-graphql/async-graphql": { "hasCommitsInLast3Months": false, - "stars": 101, - "formattedStars": "101", + "stars": 3606, + "formattedStars": "4k", "license": "Apache License 2.0", - "lastRelease": "2022-03-21T18:26:51Z", - "formattedLastRelease": "3 years ago" + "lastRelease": "", + "formattedLastRelease": "" }, - "netflix/dgs-framework": { + "graphql-rust/juniper": { "hasCommitsInLast3Months": false, - "stars": 3280, - "formattedStars": "3k", - "license": "Apache License 2.0", - "lastRelease": "2025-11-24T21:04:55Z", - "formattedLastRelease": "4 days ago" + "stars": 5920, + "formattedStars": "6k", + "license": "Other", + "lastRelease": "2025-09-08T23:23:40Z", + "formattedLastRelease": "3 months ago" }, - "spring-projects/spring-graphql": { + "apollographql/router": { "hasCommitsInLast3Months": false, - "stars": 1578, - "formattedStars": "2k", - "license": "Apache License 2.0", - "lastRelease": "2025-11-18T10:05:26Z", - "formattedLastRelease": "1 week ago" + "stars": 941, + "formattedStars": "1k", + "license": "Other", + "lastRelease": "2025-12-12T12:42:12Z", + "formattedLastRelease": "8 hours ago" }, - "graphql-java-generator/graphql-gradle-plugin-project": { + "eerimoq/gqt": { "hasCommitsInLast3Months": false, - "stars": 57, - "formattedStars": "57", + "stars": 470, + "formattedStars": "470", "license": "MIT License", "lastRelease": "", "formattedLastRelease": "" }, - "ropensci/ghql": { - "hasCommitsInLast3Months": false, - "stars": 149, - "formattedStars": "149", - "license": "Other", - "lastRelease": "2025-09-08T08:41:00Z", - "formattedLastRelease": "2 months ago" - }, - "ohler55/agoo": { + "Escape-Technologies/graphql-armor": { "hasCommitsInLast3Months": false, - "stars": 924, + "stars": 561, "formattedStars": "1k", "license": "MIT License", - "lastRelease": "2025-09-24T22:20:23Z", - "formattedLastRelease": "2 months ago" + "lastRelease": "2025-08-22T13:32:40Z", + "formattedLastRelease": "3 months ago" }, - "rmosolgo/graphql-ruby": { + "ldebruijn/graphql-protect": { "hasCommitsInLast3Months": false, - "stars": 5428, - "formattedStars": "5k", + "stars": 34, + "formattedStars": "34", "license": "MIT License", - "lastRelease": "2025-07-19T17:15:49Z", - "formattedLastRelease": "4 months ago" + "lastRelease": "2025-11-25T14:27:46Z", + "formattedLastRelease": "2 weeks ago" }, - "virtualshield/rails-graphql": { + "graphql-hive/gateway": { "hasCommitsInLast3Months": false, - "stars": 187, - "formattedStars": "187", + "stars": 69, + "formattedStars": "69", "license": "MIT License", - "lastRelease": "2025-08-25T17:53:38Z", - "formattedLastRelease": "3 months ago" + "lastRelease": "2025-12-10T19:27:44Z", + "formattedLastRelease": "2 days ago" }, - "obmarg/cynic": { + "microcks/microcks": { "hasCommitsInLast3Months": false, - "stars": 442, - "formattedStars": "442", - "license": "Mozilla Public License 2.0", - "lastRelease": "2025-08-19T19:37:22Z", - "formattedLastRelease": "3 months ago" + "stars": 1762, + "formattedStars": "2k", + "license": "Apache License 2.0", + "lastRelease": "2025-12-08T15:51:55Z", + "formattedLastRelease": "4 days ago" }, - "arthurkhlghatyan/gql-client-rs": { + "schemathesis/schemathesis": { "hasCommitsInLast3Months": false, - "stars": 51, - "formattedStars": "51", + "stars": 2891, + "formattedStars": "3k", "license": "MIT License", - "lastRelease": "2025-06-07T14:31:10Z", - "formattedLastRelease": "5 months ago" + "lastRelease": "2025-12-10T19:22:47Z", + "formattedLastRelease": "2 days ago" }, - "async-graphql/async-graphql": { + "glideapps/quicktype": { "hasCommitsInLast3Months": false, - "stars": 3594, - "formattedStars": "4k", + "stars": 13505, + "formattedStars": "14k", "license": "Apache License 2.0", "lastRelease": "", "formattedLastRelease": "" }, - "graphql-rust/juniper": { - "hasCommitsInLast3Months": false, - "stars": 5911, - "formattedStars": "6k", - "license": "Other", - "lastRelease": "2025-09-08T23:23:40Z", - "formattedLastRelease": "2 months ago" - }, - "ghostdogpr/caliban": { + "wundergraph/cosmo": { "hasCommitsInLast3Months": false, - "stars": 976, + "stars": 1126, "formattedStars": "1k", "license": "Apache License 2.0", - "lastRelease": "2025-07-14T00:24:20Z", - "formattedLastRelease": "4 months ago" - }, - "sangria-graphql/sangria": { - "hasCommitsInLast3Months": false, - "stars": 1957, - "formattedStars": "2k", - "license": "Apache License 2.0", - "lastRelease": "2025-10-20T11:40:30Z", - "formattedLastRelease": "1 month ago" + "lastRelease": "2025-12-10T12:36:49Z", + "formattedLastRelease": "2 days ago" }, "apollographql/apollo-ios": { "hasCommitsInLast3Months": false, - "stars": 4011, + "stars": 4018, "formattedStars": "4k", "license": "MIT License", - "lastRelease": "2025-11-05T23:30:57Z", - "formattedLastRelease": "3 weeks ago" + "lastRelease": "2025-12-03T20:34:39Z", + "formattedLastRelease": "1 week ago" }, "nerdsupremacist/Graphaello": { "hasCommitsInLast3Months": false, @@ -1249,7 +1321,7 @@ }, "GraphQLSwift/Graphiti": { "hasCommitsInLast3Months": false, - "stars": 553, + "stars": 554, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2025-08-21T19:30:20Z", @@ -1262,77 +1334,5 @@ "license": "MIT License", "lastRelease": "2021-05-17T12:51:10Z", "formattedLastRelease": "4 years ago" - }, - "apollographql/router": { - "hasCommitsInLast3Months": false, - "stars": 939, - "formattedStars": "1k", - "license": "Other", - "lastRelease": "2025-11-28T17:47:44Z", - "formattedLastRelease": "18 hours ago" - }, - "Escape-Technologies/graphql-armor": { - "hasCommitsInLast3Months": false, - "stars": 558, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "2025-08-22T13:32:40Z", - "formattedLastRelease": "3 months ago" - }, - "eerimoq/gqt": { - "hasCommitsInLast3Months": false, - "stars": 470, - "formattedStars": "470", - "license": "MIT License", - "lastRelease": "", - "formattedLastRelease": "" - }, - "ldebruijn/graphql-protect": { - "hasCommitsInLast3Months": false, - "stars": 34, - "formattedStars": "34", - "license": "MIT License", - "lastRelease": "2025-11-25T14:27:46Z", - "formattedLastRelease": "3 days ago" - }, - "graphql-hive/gateway": { - "hasCommitsInLast3Months": false, - "stars": 69, - "formattedStars": "69", - "license": "MIT License", - "lastRelease": "2025-11-24T15:40:17Z", - "formattedLastRelease": "4 days ago" - }, - "microcks/microcks": { - "hasCommitsInLast3Months": false, - "stars": 1754, - "formattedStars": "2k", - "license": "Apache License 2.0", - "lastRelease": "2025-10-25T15:08:00Z", - "formattedLastRelease": "1 month ago" - }, - "schemathesis/schemathesis": { - "hasCommitsInLast3Months": false, - "stars": 2871, - "formattedStars": "3k", - "license": "MIT License", - "lastRelease": "2025-11-28T16:13:29Z", - "formattedLastRelease": "19 hours ago" - }, - "glideapps/quicktype": { - "hasCommitsInLast3Months": false, - "stars": 13464, - "formattedStars": "13k", - "license": "Apache License 2.0", - "lastRelease": "", - "formattedLastRelease": "" - }, - "wundergraph/cosmo": { - "hasCommitsInLast3Months": false, - "stars": 1120, - "formattedStars": "1k", - "license": "Apache License 2.0", - "lastRelease": "2025-11-27T13:47:56Z", - "formattedLastRelease": "1 day ago" } } \ No newline at end of file diff --git a/scripts/get-github-info/last-success.isodate b/scripts/get-github-info/last-success.isodate index 817c2fe4de..5d68e2023c 100644 --- a/scripts/get-github-info/last-success.isodate +++ b/scripts/get-github-info/last-success.isodate @@ -1 +1 @@ -2025-11-29T12:04:08.519Z \ No newline at end of file +2025-12-12T20:51:22.323Z \ No newline at end of file diff --git a/scripts/sync-working-groups/package.json b/scripts/sync-working-groups/package.json index b470821c56..7659901d21 100644 --- a/scripts/sync-working-groups/package.json +++ b/scripts/sync-working-groups/package.json @@ -7,6 +7,6 @@ "start": "node ./sync-working-groups.ts" }, "dependencies": { - "arktype": "^2.1.27" + "arktype": "2.1.28" } } diff --git a/scripts/sync-working-groups/working-group-events.ndjson b/scripts/sync-working-groups/working-group-events.ndjson index 012d82306e..962f16860f 100644 --- a/scripts/sync-working-groups/working-group-events.ndjson +++ b/scripts/sync-working-groups/working-group-events.ndjson @@ -30,8 +30,12 @@ {"kind":"calendar#event","etag":"\"3524923696926750\"","id":"56uko3hh68be4q73tttdicg7l2_20251225T183000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=NTZ1a28zaGg2OGJlNHE3M3R0dGRpY2c3bDJfMjAyNTEyMjVUMTgzMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2025-10-16T15:10:58.000Z","updated":"2025-11-06T20:44:08.463Z","summary":"GraphQL AI Working Group","description":"Sign up and view agenda at https://github.com/graphql/ai-wg


Zoom password: aiwg","location":"https://zoom.us/j/92302442188","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-11T13:30:00-05:00","end":"2025-12-11T14:30:00-05:00","recurringEventId":"56uko3hh68be4q73tttdicg7l2","originalStartTime":{"dateTime":"2025-12-25T13:30:00-05:00","timeZone":"America/New_York"},"transparency":"transparent","iCalUID":"56uko3hh68be4q73tttdicg7l2@google.com","sequence":1,"eventType":"default"} {"kind":"calendar#event","etag":"\"3516415120288286\"","id":"h9erafl4rc1jjor9i6akokm5ec_20251218T160000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=aDllcmFmbDRyYzFqam9yOWk2YWtva201ZWNfMjAyNTEyMThUMTYwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2023-12-08T21:32:03.000Z","updated":"2025-09-18T14:59:20.144Z","summary":"GraphQL Governing Board Meeting","creator":{"email":"jburson@linuxfoundation.org"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-18T11:00:00-05:00","end":"2025-12-18T12:00:00-05:00","recurringEventId":"h9erafl4rc1jjor9i6akokm5ec","originalStartTime":{"dateTime":"2025-12-18T11:00:00-05:00","timeZone":"America/New_York"},"iCalUID":"h9erafl4rc1jjor9i6akokm5ec@google.com","sequence":3,"eventType":"default"} {"kind":"calendar#event","etag":"\"3462003372886000\"","id":"kkc5tt01ovrjv8fki1lo31g5hj_20251218T170000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=a2tjNXR0MDFvdnJqdjhma2kxbG8zMWc1aGpfMjAyNTEyMThUMTcwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2024-01-12T09:55:37.000Z","updated":"2024-11-07T17:48:06.443Z","summary":"Composite schemas WG - Weekly 3","description":"The weekly \"secondary\" meeting of the composite schemas WG: https://github.com/graphql/composite-schemas-wg

Meeting password is \"composite\"

Live notes are at https://docs.google.com/document/d/1hJO6U7daYvcNcQ3FBKnh3v4R256ers6M8IGyqRpY_kE/edit?usp=sharing","location":"https://zoom.us/j/91078840351","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-18T12:00:00-05:00","end":"2025-12-18T13:00:00-05:00","recurringEventId":"kkc5tt01ovrjv8fki1lo31g5hj","originalStartTime":{"dateTime":"2025-12-18T12:00:00-05:00","timeZone":"Europe/Berlin"},"iCalUID":"kkc5tt01ovrjv8fki1lo31g5hj@google.com","sequence":1,"eventType":"default"} -{"kind":"calendar#event","etag":"\"3500694996844990\"","id":"2ffd8o32sh77kd3mtccrtg887n_20251218T183000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=MmZmZDhvMzJzaDc3a2QzbXRjY3J0Zzg4N25fMjAyNTEyMThUMTgzMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2025-05-01T19:23:48.000Z","updated":"2025-06-19T15:38:18.422Z","summary":"GraphQL WG - Secondary (EU)","description":"Zoom password: graphqlwg","location":"https://zoom.us/j/593263740","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-18T13:30:00-05:00","end":"2025-12-18T15:00:00-05:00","recurringEventId":"2ffd8o32sh77kd3mtccrtg887n","originalStartTime":{"dateTime":"2025-12-18T13:30:00-05:00","timeZone":"America/Los_Angeles"},"iCalUID":"2ffd8o32sh77kd3mtccrtg887n@google.com","sequence":0,"eventType":"default"} +{"kind":"calendar#event","etag":"\"3529763944050462\"","id":"2ffd8o32sh77kd3mtccrtg887n_20251218T183000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=MmZmZDhvMzJzaDc3a2QzbXRjY3J0Zzg4N25fMjAyNTEyMThUMTgzMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2025-05-01T19:23:48.000Z","updated":"2025-12-04T20:59:32.025Z","summary":"GraphQL WG - Secondary (EU)","description":"Zoom password: graphqlwg","location":"https://zoom.us/j/593263740","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-18T13:30:00-05:00","end":"2025-12-18T15:00:00-05:00","recurringEventId":"2ffd8o32sh77kd3mtccrtg887n","originalStartTime":{"dateTime":"2025-12-18T13:30:00-05:00","timeZone":"America/Los_Angeles"},"iCalUID":"2ffd8o32sh77kd3mtccrtg887n@google.com","sequence":0,"eventType":"default"} {"kind":"calendar#event","etag":"\"3517067971709790\"","id":"f7cvs5ala9jtt147l3mik2mlvl_20251222T160000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=ZjdjdnM1YWxhOWp0dDE0N2wzbWlrMm1sdmxfMjAyNTEyMjJUMTYwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2024-01-29T15:14:17.000Z","updated":"2025-09-22T09:39:45.854Z","summary":"Conference & Community Committee Meeting - Fortnightly Recurring","description":"\nYou have been invited to a recurring meeting for GraphQL Foundation\n\nWeekly Sync and Coordination Meeting for Conference Committee participants. Notes Document: https://docs.google.com/document/d/19-alP5jywnXzgN_1zYLBTRWh-4CaXzGakEZdTBFwNAc/edit\n\nWays to join meeting:\n\n1. Join from PC, Mac, iPad, or Android\n\nhttps://zoom-lfx.platform.linuxfoundation.org/meeting/96286151238?password=ff267735-efbd-4be4-a89c-b927b596190a\n\n2. Join via audio\n\nOne tap mobile:\nUS: +12532158782,,96286151238# or +13462487799,,96286151238\n\nOr dial:\nUS: +1 253 215 8782 or +1 346 248 7799 or +1 669 900 6833 or +1 301 715 8592 or +1 312 626 6799 or +1 646 374 8656 or 877 369 0926 (Toll Free) or 855 880 1246 (Toll Free)\nCanada: +1 647 374 4685 or +1 647 558 0588 or +1 778 907 2071 or +1 204 272 7920 or +1 438 809 7799 or +1 587 328 1099 or 855 703 8985 (Toll Free)\n\nMeeting ID: 96286151238\n\nMeeting Passcode: 986182\n\n\nInternational numbers: https://zoom.us/u/alwnPIaVT\n","location":"https://zoom-lfx.platform.linuxfoundation.org/meeting/96286151238?password=ff267735-efbd-4be4-a89c-b927b596190a","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-22T11:00:00-05:00","end":"2025-12-22T12:00:00-05:00","recurringEventId":"f7cvs5ala9jtt147l3mik2mlvl","originalStartTime":{"dateTime":"2025-12-22T11:00:00-05:00","timeZone":"America/New_York"},"iCalUID":"f7cvs5ala9jtt147l3mik2mlvl@google.com","sequence":2,"guestsCanInviteOthers":false,"eventType":"default"} {"kind":"calendar#event","etag":"\"3524923598591262\"","id":"s9agipg1r702pfngano7pol2h5_20251225T170000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=czlhZ2lwZzFyNzAycGZuZ2Fubzdwb2wyaDVfMjAyNTEyMjVUMTcwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2024-01-12T09:56:07.000Z","updated":"2025-11-06T20:43:19.295Z","summary":"Composite schemas WG - Weekly 4","description":"The weekly "secondary" meeting of the composite schemas WG: https://github.com/graphql/composite-schemas-wg

Meeting password is "composite"

Live notes are at https://docs.google.com/document/d/1hJO6U7daYvcNcQ3FBKnh3v4R256ers6M8IGyqRpY_kE/edit?usp=sharing","location":"https://zoom.us/j/91078840351","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-25T12:00:00-05:00","end":"2025-12-25T13:00:00-05:00","recurringEventId":"s9agipg1r702pfngano7pol2h5","originalStartTime":{"dateTime":"2025-12-25T12:00:00-05:00","timeZone":"Europe/Berlin"},"iCalUID":"s9agipg1r702pfngano7pol2h5@google.com","sequence":1,"eventType":"default"} {"kind":"calendar#event","etag":"\"3524923550687710\"","id":"4igp67o2j2nkso49c1d6nbv040_20251225T180000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=NGlncDY3bzJqMm5rc280OWMxZDZuYnYwNDBfMjAyNTEyMjVUMTgwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2025-04-15T10:29:33.000Z","updated":"2025-11-06T20:42:55.343Z","summary":"GraphQL OTel WG","description":"Zoom password: otel
 
https://github.com/graphql/otel-wg","location":"https://zoom.us/j/93594710848?pwd=meEB8rd5g69r5DF8zFaL8VIWO2Il1v.1","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-25T13:00:00-05:00","end":"2025-12-25T14:00:00-05:00","recurringEventId":"4igp67o2j2nkso49c1d6nbv040","originalStartTime":{"dateTime":"2025-12-25T13:00:00-05:00","timeZone":"America/Los_Angeles"},"iCalUID":"4igp67o2j2nkso49c1d6nbv040@google.com","sequence":0,"eventType":"default"} {"kind":"calendar#event","etag":"\"3524923616454910\"","id":"pag44b4o3k87r90laj5vf5t67v_20251225T190000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=cGFnNDRiNG8zazg3cjkwbGFqNXZmNXQ2N3ZfMjAyNTEyMjVUMTkwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2023-12-04T10:48:14.000Z","updated":"2025-11-06T20:43:28.227Z","summary":"GraphQL-over-HTTP WG","description":"Zoom password: httpwg","location":"https://zoom.us/j/92781382543","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2025-12-25T14:00:00-05:00","end":"2025-12-25T15:00:00-05:00","recurringEventId":"pag44b4o3k87r90laj5vf5t67v","originalStartTime":{"dateTime":"2025-12-25T14:00:00-05:00","timeZone":"America/Los_Angeles"},"iCalUID":"pag44b4o3k87r90laj5vf5t67v@google.com","sequence":3,"eventType":"default"} +{"kind":"calendar#event","etag":"\"3530925154728702\"","id":"f7cvs5ala9jtt147l3mik2mlvl_20260105T160000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=ZjdjdnM1YWxhOWp0dDE0N2wzbWlrMm1sdmxfMjAyNjAxMDVUMTYwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2024-01-29T15:14:17.000Z","updated":"2025-12-11T14:16:17.364Z","summary":"Conference & Community Committee Meeting - Fortnightly Recurring","description":"\nYou have been invited to a recurring meeting for GraphQL Foundation\n\nWeekly Sync and Coordination Meeting for Conference Committee participants. Notes Document: https://docs.google.com/document/d/19-alP5jywnXzgN_1zYLBTRWh-4CaXzGakEZdTBFwNAc/edit\n\nWays to join meeting:\n\n1. Join from PC, Mac, iPad, or Android\n\nhttps://zoom-lfx.platform.linuxfoundation.org/meeting/96286151238?password=ff267735-efbd-4be4-a89c-b927b596190a\n\n2. Join via audio\n\nOne tap mobile:\nUS: +12532158782,,96286151238# or +13462487799,,96286151238\n\nOr dial:\nUS: +1 253 215 8782 or +1 346 248 7799 or +1 669 900 6833 or +1 301 715 8592 or +1 312 626 6799 or +1 646 374 8656 or 877 369 0926 (Toll Free) or 855 880 1246 (Toll Free)\nCanada: +1 647 374 4685 or +1 647 558 0588 or +1 778 907 2071 or +1 204 272 7920 or +1 438 809 7799 or +1 587 328 1099 or 855 703 8985 (Toll Free)\n\nMeeting ID: 96286151238\n\nMeeting Passcode: 986182\n\n\nInternational numbers: https://zoom.us/u/alwnPIaVT\n","location":"https://zoom-lfx.platform.linuxfoundation.org/meeting/96286151238?password=ff267735-efbd-4be4-a89c-b927b596190a","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2026-01-05T11:00:00-05:00","end":"2026-01-05T12:00:00-05:00","recurringEventId":"f7cvs5ala9jtt147l3mik2mlvl","originalStartTime":{"dateTime":"2026-01-05T11:00:00-05:00","timeZone":"America/New_York"},"iCalUID":"f7cvs5ala9jtt147l3mik2mlvl@google.com","sequence":2,"guestsCanInviteOthers":false,"eventType":"default"} +{"kind":"calendar#event","etag":"\"3526826931784574\"","id":"q3qul35gpekign7gc8cvr6bap1_20260108T140000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=cTNxdWwzNWdwZWtpZ243Z2M4Y3ZyNmJhcDFfMjAyNjAxMDhUMTQwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2023-12-08T21:20:13.000Z","updated":"2025-11-17T21:04:25.892Z","summary":"Marketing & Content Subcommittee Meeting","location":"https://zoom-lfx.platform.linuxfoundation.org/meeting/91228653788?password=0745533d-9a7a-42bb-8c72-3b823f679384","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2026-01-08T09:00:00-05:00","end":"2026-01-08T10:00:00-05:00","recurringEventId":"q3qul35gpekign7gc8cvr6bap1_R20251030T130000","originalStartTime":{"dateTime":"2026-01-08T09:00:00-05:00","timeZone":"America/New_York"},"iCalUID":"q3qul35gpekign7gc8cvr6bap1_R20251030T130000@google.com","sequence":1,"eventType":"default"} +{"kind":"calendar#event","etag":"\"3512629578532638\"","id":"1ae8m39lvqtigc4ao1p670g8il_20260108T160000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=MWFlOG0zOWx2cXRpZ2M0YW8xcDY3MGc4aWxfMjAyNjAxMDhUMTYwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2025-02-04T14:14:16.000Z","updated":"2025-08-27T17:13:09.266Z","summary":"GraphQL Community WG","description":"Meeting password: community

https://github.com/graphql/community-wg/tree/main/agendas

Please be aware that meetings are recorded and/or live-streamed.","location":"https://zoom.us/j/93104287544","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2026-01-08T11:00:00-05:00","end":"2026-01-08T12:00:00-05:00","recurringEventId":"1ae8m39lvqtigc4ao1p670g8il_R20250313T150000","originalStartTime":{"dateTime":"2026-01-08T11:00:00-05:00","timeZone":"America/Los_Angeles"},"iCalUID":"1ae8m39lvqtigc4ao1p670g8il_R20250313T150000@google.com","sequence":1,"eventType":"default"} +{"kind":"calendar#event","etag":"\"3462003293658000\"","id":"lvqspfdh491rrdmvl7k1mruqd8_20260108T170000Z","status":"confirmed","htmlLink":"https://www.google.com/calendar/event?eid=bHZxc3BmZGg0OTFycmRtdmw3azFtcnVxZDhfMjAyNjAxMDhUMTcwMDAwWiBsaW51eGZvdW5kYXRpb24ub3JnX2lrNzl0OXV1ajJwMzJpM3IyMDNkZ3Y1bW84QGc","created":"2024-01-12T09:56:35.000Z","updated":"2024-11-07T17:47:26.829Z","summary":"Composite schemas WG - Weekly 2","description":"The weekly "secondary" meeting of the composite schemas WG: https://github.com/graphql/composite-schemas-wg

Meeting password is "composite"

Live notes are at https://docs.google.com/document/d/1hJO6U7daYvcNcQ3FBKnh3v4R256ers6M8IGyqRpY_kE/edit?usp=sharing","location":"https://zoom.us/j/91078840351","creator":{"email":"benjie@graphile.com"},"organizer":{"email":"linuxfoundation.org_ik79t9uuj2p32i3r203dgv5mo8@group.calendar.google.com","displayName":"GraphQL Foundation - Public","self":true},"start":"2026-01-08T12:00:00-05:00","end":"2026-01-08T13:00:00-05:00","recurringEventId":"lvqspfdh491rrdmvl7k1mruqd8","originalStartTime":{"dateTime":"2026-01-08T12:00:00-05:00","timeZone":"Europe/Berlin"},"iCalUID":"lvqspfdh491rrdmvl7k1mruqd8@google.com","sequence":1,"eventType":"default"} diff --git a/src/_design-system/breadcrumbs.tsx b/src/_design-system/breadcrumbs.tsx index 4b42a7554a..a394874b38 100644 --- a/src/_design-system/breadcrumbs.tsx +++ b/src/_design-system/breadcrumbs.tsx @@ -26,9 +26,10 @@ export const Breadcrumbs = ({ const title = extractStringsFromReactNode(item.title) const className = clsx( - "text-neu-700 dark:text-neu-400 min-w-6 last:text-neu-800 dark:last:text-neu-800 leading-none", + "text-neu-700 dark:text-neu-400 min-w-6 last:text-neu-800 dark:last:text-neu-800 leading-none whitespace-pre", href && "gql-focus-visible ring-inset hover:text-neu-900 hover:underline underline-offset-2", + item.title.length > 8 ? "overflow-hidden truncate" : "shrink-0", ) return ( diff --git a/src/app/(main)/community/events/page.tsx b/src/app/(main)/community/events/page.tsx index efd83a1580..cf8ee46fcd 100644 --- a/src/app/(main)/community/events/page.tsx +++ b/src/app/(main)/community/events/page.tsx @@ -140,7 +140,7 @@ function Stripes() { + ) : null + } + /> + ) +} diff --git a/src/app/(main)/resources/resource-hub-card.tsx b/src/app/(main)/resources/resource-hub-card.tsx new file mode 100644 index 0000000000..dfa9948d51 --- /dev/null +++ b/src/app/(main)/resources/resource-hub-card.tsx @@ -0,0 +1,147 @@ +import Link from "next/link" +import type { ReactNode } from "react" +import { clsx } from "clsx" + +import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr" +import ClockIcon from "@/app/conf/_design-system/pixelarticons/clock.svg?svgr" +import { Tag } from "@/app/conf/_design-system/tag" +import { Topic } from "@/resources/types" +import { blogTagColors } from "@/components/blog-page/blog-tag-colors" + +export const tagColors: Record = { + ...blogTagColors, + backend: "#36C1A0", + "defies-categorization": "#894545", + "developer-experience": "#6fc9af", + "federation-and-composite-schemas": "#cbc749", + "graphql-clients": "#ca78fc", + "graphql-in-production": "#e4981f", + "graphql-security": "#CC6BB0", + "graphql-spec": "#6B73CC", + scaling: "#8D8D8D", + frontend: "violet", + documentation: "salmon", + "schema-evolution": "thistle", + security: "cornflowerblue", + "case-studies": "#B36B00", + "federation-and-distributed-systems": "#FF8F70", + federation: "#5C7CFA", + tools: "#0FA3B1", + "api-platform-and-gateways": "#F4B400", + "schema-design": "#7E57C2", + ai: "#FF5FA2", + monitoring: "#2D9CDB", + "blog-or-newsletter": "hsl(var(--color-pri-base))", + book: "#00C6AC", + guide: "#FF8800", +} + +interface ResourceHubCardProps { + href: string + title: string + author?: string + duration?: string + authorPlacement?: "body" | "footer" + tags?: string[] + className?: string + icon?: ReactNode +} + +export function ResourceHubCard({ + href, + title, + author, + duration, + authorPlacement = "footer", + tags, + className, + icon, +}: ResourceHubCardProps) { + return ( + +
+
+ {tags?.length ? ( +
+ {tags.map(tag => ( + + {formatTag(tag)} + + ))} +
+ ) : null} +
+ {authorPlacement === "body" && author ? ( + {author} + ) : null} +

100 + ? "typography-body-lg" + : "typography-h4 md:typography-h3", + )} + > + {title} +

+
+
+ {icon ? ( +
+ {icon} +
+ ) : null} +
+
+ {(authorPlacement === "footer" || !!duration) && ( +
+ {authorPlacement === "footer" && author ? ( + {author} + ) : null} + {duration ? ( + + + {duration} + + ) : null} +
+ )} +
+ +
+
+ + ) +} + +function formatTag(tag: string) { + if (tag === "blog-or-newsletter") return "Blogs & Newsletters" + if (tag === "book") return "Books" + if (tag === "guide") return "Individual posts" + + return tag.replaceAll("-", " ") +} diff --git a/src/app/(main)/resources/resources-hero.tsx b/src/app/(main)/resources/resources-hero.tsx new file mode 100644 index 0000000000..29ac61e94c --- /dev/null +++ b/src/app/(main)/resources/resources-hero.tsx @@ -0,0 +1,28 @@ +import { LearnHeroStripes } from "@/components/learn-aggregator/learn-hero-stripes" + +export function ResourcesHero({ + heading, + text, + children, +}: { + heading: string + text: string + children?: React.ReactNode +}) { + return ( +
+ +
+

{heading}

+

{text}

+ {children} +
+
+ ) +} diff --git a/src/app/(main)/resources/specification-section.tsx b/src/app/(main)/resources/specification-section.tsx new file mode 100644 index 0000000000..45fc32edfe --- /dev/null +++ b/src/app/(main)/resources/specification-section.tsx @@ -0,0 +1,54 @@ +import { Button } from "@/app/conf/_design-system/button" +import ArchiveIcon from "./assets/archive.svg?svgr" +import { StripesDecoration } from "@/app/conf/_design-system/stripes-decoration" +import { Eyebrow } from "@/_design-system/eyebrow" + +export function SpecificationSection() { + return ( +
+
+ + specification + +
+
+ +
+ +
+

+ Read the GraphQL Specification +

+

+ The specification defines the core structure of GraphQL. It's the + foundation for building consistent servers, clients, and tools. + Read the spec to better understand how GraphQL works. +

+ +
+
+
+
+ ) +} + +function Stripes() { + return ( +
+ +
+ ) +} diff --git a/src/app/(main)/resources/subtitles.ts b/src/app/(main)/resources/subtitles.ts new file mode 100644 index 0000000000..f546c51b99 --- /dev/null +++ b/src/app/(main)/resources/subtitles.ts @@ -0,0 +1,30 @@ +import { type Topic } from "@/resources/types" + +export const categoryNames: Record = { + frontend: "Frontend", + backend: "Backend", + federation: "Federation", + security: "Security", + ai: "AI", + monitoring: "Monitoring", + "api-platform-and-gateways": "API Platform and Gateways", + "developer-experience": "Developer Experience", + "schema-design": "Schema Design", + tools: "Tools", +} + +export const categorySubtitles: Record = { + frontend: "Learn how to integrate GraphQL on the frontend.", + backend: + "Build powerful GraphQL backends with the right tools, libraries and expert insights.", + federation: "Learn how to build and compose GraphQL graphs with federation.", + ai: "Explore how to use GraphQL for AI systems.", + security: "Learn how to secure your GraphQL APIs.", + monitoring: + "Stay ahead of performance issues by monitoring queries and watching error trends.", + "api-platform-and-gateways": + "Learn how to build and deploy API Gateways and Supergraphs.", + "developer-experience": "Learn how to improve your developer experience.", + "schema-design": "Learn how to design and maintain GraphQL schemas.", + tools: "Discover the best tools for GraphQL development.", +} diff --git a/src/app/(main)/resources/tools-libraries-section.tsx b/src/app/(main)/resources/tools-libraries-section.tsx new file mode 100644 index 0000000000..4c69c86096 --- /dev/null +++ b/src/app/(main)/resources/tools-libraries-section.tsx @@ -0,0 +1,34 @@ +import { Button } from "@/app/conf/_design-system/button" +import ToolsIcon from "./assets/tools.svg?svgr" +import { Eyebrow } from "@/_design-system/eyebrow" + +export function ToolsLibrariesSection() { + return ( +
+
+ + tools and libraries + + +
+
+

+ Build GraphQL with Tools & Libraries +

+

+ Explore solutions and docs for building with GraphQL — across + languages, frameworks, and platforms. +

+ +
+ +
+ +
+
+
+
+ ) +} diff --git a/src/app/(main)/resources/video-resources-section.tsx b/src/app/(main)/resources/video-resources-section.tsx new file mode 100644 index 0000000000..7816a8ff83 --- /dev/null +++ b/src/app/(main)/resources/video-resources-section.tsx @@ -0,0 +1,31 @@ +import { Button } from "@/app/conf/_design-system/button" +import { Eyebrow } from "@/_design-system/eyebrow" +import VideoPlayerIcon from "./assets/video-player.svg?svgr" + +export function VideoResourcesSection() { + return ( +
+ video resources library + +
+
+ +
+ +
+

+ Watch and learn GraphQL +

+

+ Build your skills with featured videos from GraphQL Conf, global + meetups, and expert engineers — keeping you up to date in a + fast-moving ecosystem. +

+ +
+
+
+ ) +} diff --git a/src/app/(main)/resources/video/page.tsx b/src/app/(main)/resources/video/page.tsx new file mode 100644 index 0000000000..b845357422 --- /dev/null +++ b/src/app/(main)/resources/video/page.tsx @@ -0,0 +1,95 @@ +import { NavbarFixed } from "@/components/navbar/navbar-fixed" +import { getResourcesByTag } from "@/resources/data" +import { ResourcesHero } from "../resources-hero" +import { VideoLibrary } from "./video-library" +import { KeepLearning } from "../keep-learning" +import { LookingForMore } from "@/components/looking-for-more" +import { Breadcrumbs } from "@/_design-system/breadcrumbs" + +export const metadata = { + title: "Video Resources Library", + description: + "Expand your expertise with curated videos to help you master GraphQL and stay up to date with its evolving ecosystem.", +} + +export default async function VideoResourcesPage() { + const resources = await getResourcesByTag("video") + const seen = new Set() + const unique = resources.filter(resource => { + const key = resource.title.trim().toLowerCase() + if (seen.has(key)) return false + seen.add(key) + return true + }) + unique.sort((a, b) => + a.title.localeCompare(b.title, "en", { sensitivity: "base" }), + ) + + return ( +
+ + + +
+ +
+ +
+
+

Browse GraphQL Videos

+

+ The video library includes talks from GraphQL Conf and archival + presentations by developers from Facebook and beyond, shared at + conferences and meetups worldwide. +

+
+ + + + + + +
+
+ ) +} diff --git a/src/app/(main)/resources/video/video-library.tsx b/src/app/(main)/resources/video/video-library.tsx new file mode 100644 index 0000000000..b646764128 --- /dev/null +++ b/src/app/(main)/resources/video/video-library.tsx @@ -0,0 +1,222 @@ +"use client" + +import { useMemo, useState } from "react" +import { clsx } from "clsx" + +import { Button } from "@/app/conf/_design-system/button" +import { Tag } from "@/app/conf/_design-system/tag" +import CaretDownIcon from "@/app/conf/_design-system/pixelarticons/caret-down.svg?svgr" +import { type ResourceMetadata, topics, type Topic } from "@/resources/types" + +import { ResourceHubCard, tagColors } from "../resource-hub-card" + +interface VideoLibraryProps { + resources: ResourceMetadata[] + className?: string +} + +type SortOrder = "az" | "za" + +export function VideoLibrary({ resources, className }: VideoLibraryProps) { + const [selectedTopics, setSelectedTopics] = useState([]) + const [sortOrder, setSortOrder] = useState("az") + + const topicOptions = useMemo(() => { + const allowed = new Set(topics) + const found = new Set() + resources.forEach(resource => { + resource.tags.forEach(tag => { + if (allowed.has(tag as Topic)) { + found.add(tag) + } + }) + }) + return Array.from(found).sort((a, b) => + a.localeCompare(b, "en", { sensitivity: "base" }), + ) + }, [resources]) + + const filtered = useMemo(() => { + const filteredByTopic = + selectedTopics.length === 0 + ? resources + : resources.filter(resource => + resource.tags.some(tag => selectedTopics.includes(tag)), + ) + + const sorted = [...filteredByTopic].sort((a, b) => + sortOrder === "az" + ? a.title.localeCompare(b.title, "en", { sensitivity: "base" }) + : b.title.localeCompare(a.title, "en", { sensitivity: "base" }), + ) + + return sorted + }, [resources, selectedTopics, sortOrder]) + + return ( +
+
+
+ + +
+ + Sort + +
+ + +
+
+
+
+ +
    + {filtered.slice(0, 6).map(resource => { + return ( +
  • + tag !== "video")} + duration={resource.duration} + /> +
  • + ) + })} +
+ {filtered.length > 6 && ( +
+ {/* we're using
for SEO and Cmd+F support */} + + + +
    + {filtered.slice(6).map(resource => { + return ( +
  • + tag !== "video")} + duration={resource.duration} + /> +
  • + ) + })} +
+
+ )} +
+ ) +} + +function TopicsFilter({ + label, + options, + resources, + value, + onChange, +}: { + label: string + options: string[] + resources: ResourceMetadata[] + value: string[] + onChange: (next: string[]) => void +}) { + const topicCounts = useMemo(() => { + const counts: Record = {} + resources.forEach(resource => { + resource.tags.forEach(tag => { + if (options.includes(tag)) { + counts[tag] = (counts[tag] || 0) + 1 + } + }) + }) + return counts + }, [resources, options]) + + const toggleTopic = (topic: string) => { + if (value.includes(topic)) { + onChange(value.filter(t => t !== topic)) + } else { + onChange([...value, topic]) + } + } + + const hasSelection = value.length > 0 + + return ( +
+

+ {label} +

+
    + {options.map((topic, i) => { + const isSelected = value.includes(topic) + const count = topicCounts[topic] || 0 + return ( +
  • + +
  • + ) + })} +
+
+ ) +} + +function arrowsMoveSideways(event: React.KeyboardEvent) { + if (event.key === "ArrowLeft" || event.key === "ArrowRight") { + const target = event.currentTarget as HTMLElement + const sibling = + event.key === "ArrowLeft" + ? target.parentElement?.previousElementSibling?.querySelector("button") + : target.parentElement?.nextElementSibling?.querySelector("button") + if (sibling instanceof HTMLElement) { + sibling.focus() + } + } +} diff --git a/src/app/conf/_design-system/pixelarticons/caret-down.svg b/src/app/conf/_design-system/pixelarticons/caret-down.svg index 428cdbbaaf..deb68afbea 100644 --- a/src/app/conf/_design-system/pixelarticons/caret-down.svg +++ b/src/app/conf/_design-system/pixelarticons/caret-down.svg @@ -3,12 +3,11 @@ width="25" height="24" viewBox="0 0 25 24" - fill="none" + fill="#A0A88A" > diff --git a/src/app/conf/_design-system/stripes-decoration.tsx b/src/app/conf/_design-system/stripes-decoration.tsx index 8d03825612..f895baeada 100644 --- a/src/app/conf/_design-system/stripes-decoration.tsx +++ b/src/app/conf/_design-system/stripes-decoration.tsx @@ -1,21 +1,27 @@ import clsx from "clsx" const maskEven = - "repeating-linear-gradient(to right, transparent, transparent var(--stripe-width), black var(--stripe-width), black calc(var(--stripe-width) * 2))" + "repeating-linear-gradient(var(--angle), transparent, transparent var(--stripe-width), black var(--stripe-width), black calc(var(--stripe-width) * 2))" const maskOdd = - "repeating-linear-gradient(to right, black, black var(--stripe-width), transparent var(--stripe-width), transparent calc(var(--stripe-width) * 2))" + "repeating-linear-gradient(var(--angle), black, black var(--stripe-width), transparent var(--stripe-width), transparent calc(var(--stripe-width) * 2))" export interface StripesDecorationProps { evenClassName?: string oddClassName?: string stripeWidth?: string + /** + * @default "90deg" to right, + * use "-90deg" to align with right side of the container + */ + angle?: string } export function StripesDecoration({ stripeWidth = "12px", evenClassName, oddClassName, + angle = "90deg", }: StripesDecorationProps) { return ( <> @@ -23,7 +29,10 @@ export function StripesDecoration({
= { spec: "#00C6AC", grants: "#84BD01", "in-the-news": "#3F3A3D", + "developer-experience": "#6fc9af", } diff --git a/src/components/blog-page/blog-tags.tsx b/src/components/blog-page/blog-tags.tsx index 6c1a509535..618aa8282f 100644 --- a/src/components/blog-page/blog-tags.tsx +++ b/src/components/blog-page/blog-tags.tsx @@ -35,7 +35,7 @@ export function BlogTags({ key={tag} // yes, the page lives under /tags, not /blog/tags href={`/tags/${tag}`} - className="-m-1 flex p-1 ring-inset ring-neu-400 transition-opacity duration-75 hover:ring focus:!outline-offset-0 dark:ring-neu-50 [:has(>:hover)>&:not(:hover)]:opacity-70" + className="gql-focus-visible -m-1 flex p-1 ring-inset ring-neu-400 transition-opacity duration-75 hover:ring focus:!outline-offset-0 dark:ring-neu-50 [:has(>:hover)>&:not(:hover)]:opacity-70" > {tagElement} diff --git a/src/components/blog-page/featured-blog-posts.tsx b/src/components/blog-page/featured-blog-posts.tsx index fc6c7f08cc..d017dfd39b 100644 --- a/src/components/blog-page/featured-blog-posts.tsx +++ b/src/components/blog-page/featured-blog-posts.tsx @@ -47,7 +47,7 @@ export function FeaturedBlogPosts({ byline={firstFeatured.frontMatter.byline} date={firstFeatured.frontMatter.date} /> - +
diff --git a/src/components/blog-page/index.tsx b/src/components/blog-page/index.tsx index 0d597d3b7e..4f11d56fe2 100644 --- a/src/components/blog-page/index.tsx +++ b/src/components/blog-page/index.tsx @@ -5,9 +5,10 @@ import { Tag } from "@/app/conf/_design-system/tag" import { arrowsMoveSideways } from "@/app/conf/_design-system/utils/arrows-move-sideways" import { StripesDecoration } from "@/app/conf/_design-system/stripes-decoration" +import { LookingForMore } from "@/components/looking-for-more" + import { blogTagColors } from "./blog-tag-colors" import { BlogCard } from "./blog-card" -import { LookingForMore } from "./looking-for-more" import { BlogMdxContent } from "./mdx-types" import { FeaturedBlogPosts } from "./featured-blog-posts" @@ -51,14 +52,14 @@ export function BlogPage({
-
+

{currentTag || "All Posts"}

Categories

-
    +
      {Object.entries(tags) .sort((a, b) => b[1] - a[1]) .map(([tag, count], i) => ( @@ -78,7 +79,7 @@ export function BlogPage({
-
+
{blogs.map( page => (!currentTag || page.frontMatter.tags.includes(currentTag)) && ( @@ -87,7 +88,13 @@ export function BlogPage({ )}
- +
) diff --git a/src/components/blog-page/looking-for-more.tsx b/src/components/blog-page/looking-for-more.tsx deleted file mode 100644 index 2441ade4e7..0000000000 --- a/src/components/blog-page/looking-for-more.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Anchor } from "@/app/conf/_design-system/anchor" - -import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr" - -export function LookingForMore() { - return ( -
-
-
-

Looking for more?

-

- Explore learning guides and best practices — or browse for tools, - libraries and other resources. -

-
-
- - Learn - - - - Resources - - -
-
-
- ) -} diff --git a/src/components/learn-aggregator/learn-hero-stripes.tsx b/src/components/learn-aggregator/learn-hero-stripes.tsx index 29d2a4e37f..b7692117a3 100644 --- a/src/components/learn-aggregator/learn-hero-stripes.tsx +++ b/src/components/learn-aggregator/learn-hero-stripes.tsx @@ -1,13 +1,24 @@ +import { clsx } from "clsx" import { StripesDecoration } from "@/app/conf/_design-system/stripes-decoration" import blurBean from "./learn-blur-bean.webp" -export function LearnHeroStripes() { +export function LearnHeroStripes({ + className, + style, + ...rest +}: { + className?: string + style?: React.CSSProperties +}) { return (
) { - return ( -
-
-
-

Looking for more?

-

- Learning is just the beginning. Discover tools and other resources — - or connect with the GraphQL community around the world. -

-
- -
-
- ) -} diff --git a/src/components/looking-for-more.tsx b/src/components/looking-for-more.tsx new file mode 100644 index 0000000000..db174b300d --- /dev/null +++ b/src/components/looking-for-more.tsx @@ -0,0 +1,46 @@ +import { clsx } from "clsx" +import { Anchor } from "@/app/conf/_design-system/anchor" +import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr" + +type LinkItem = { href: string; label: string } + +interface LookingForMoreProps extends React.HTMLAttributes { + description: string + links: [LinkItem, LinkItem] +} + +export function LookingForMore({ + description, + links, + ...props +}: LookingForMoreProps) { + return ( +
+
+
+

Looking for more?

+

{description}

+
+
+ + {links[0].label} + + + + {links[1].label} + + +
+
+
+ ) +} diff --git a/src/components/nav-links/arrow-nav-links.tsx b/src/components/nav-links/arrow-nav-links.tsx index 9e70684c1f..6563ad511b 100644 --- a/src/components/nav-links/arrow-nav-links.tsx +++ b/src/components/nav-links/arrow-nav-links.tsx @@ -36,7 +36,7 @@ export function ArrowNavLinks({ title={prev.title} className="gql-focus-visible typography-link flex max-w-[50%] items-center gap-2 border border-neu-200 pr-2 text-left text-base no-underline hover:bg-neu-50 hover:ring hover:ring-neu-100 dark:border-neu-100 dark:hover:bg-neu-50/50 dark:hover:ring-neu-50" > - + {prev.title} @@ -49,7 +49,7 @@ export function ArrowNavLinks({ className="gql-focus-visible typography-link ml-auto flex max-w-[50%] items-center gap-2 border border-neu-200 pl-2 text-left text-base no-underline hover:bg-neu-50 hover:ring hover:ring-neu-100 dark:border-neu-100 dark:hover:bg-neu-50/50 dark:hover:ring-neu-50" > {next.title} - + diff --git a/src/components/navbar/navbar.tsx b/src/components/navbar/navbar.tsx index d0e9d8fcbd..64b826850e 100644 --- a/src/components/navbar/navbar.tsx +++ b/src/components/navbar/navbar.tsx @@ -18,6 +18,8 @@ import { Flexsearch } from "../flexsearch" import { NavLink, navLinkClasses } from "./nav-link" import { useMenu } from "../use-menu" +import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr" + type Item = normalizePages.PageItem | normalizePages.MenuItem export interface NavBarProps { items: Item[] @@ -33,6 +35,7 @@ function NavbarMenu({ const routes = Object.fromEntries( (menu.children || []).map(route => [route.name, route]), ) + return ( - {Object.entries(menu.items || {}).map(([key, item]) => ( - , - state: NavigationMenu.Link.State, - ) => ( - - - {item.title} - - - )} - /> - ))} + {Object.entries(menu.items || {}).map(([key, item]) => { + if (typeof item === "string") item = { title: item } + if (!item.title) item.title = key + const href = + item.href || + routes[key]?.route || + (key === "index" ? menu.route : `${menu.route}/${key}`) + + if (key === "index") { + return ( + , + state: NavigationMenu.Link.State, + ) => ( + + + Explore {menu.title} + + + + )} + /> + ) + } + + return ( + , + state: NavigationMenu.Link.State, + ) => ( + + + {item.title} + + + )} + /> + ) + })} ) diff --git a/src/components/sidebar/index.tsx b/src/components/sidebar/index.tsx index bd126b6d5e..d4917affee 100644 --- a/src/components/sidebar/index.tsx +++ b/src/components/sidebar/index.tsx @@ -31,6 +31,7 @@ import { import ArrowBarLeft from "@/app/conf/_design-system/pixelarticons/arrow-bar-left.svg?svgr" import { Anchor } from "@/app/conf/_design-system/anchor" +import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr" import { renderComponent } from "../utils/render-component" import { ThemeSwitch } from "../theme-switch" @@ -57,7 +58,7 @@ const Folder = memo(function FolderInner(props: FolderProps) { const classes = { link: cn( - "flex px-2 py-1.5 text-sm transition-colors [word-break:break-word]", + "flex px-2 capitalize py-1.5 text-sm transition-colors [word-break:break-word]", "cursor-pointer contrast-more:border contrast-more:hover:underline gql-focus-visible focus-visible:outline-offset-1", ), inactive: cn( @@ -143,10 +144,16 @@ function FolderImpl({ item, anchors, onFocus }: FolderProps): ReactElement { (menu.children || []).map(route => [route.name, route]), ) item.children = Object.entries(menu.items || {}).map(([key, item]) => { + if (typeof item === "string") item = { title: item } + if (!item.title) item.title = key + const route = routes[key] || { name: key, route: menu.route + "/" + key, } + + if (key === "index") route.route = menu.route + return { ...route, ...item, @@ -273,13 +280,24 @@ function File({
  • { setMenu(false) }} onFocus={onFocus} > - {item.title} + {item.name !== "index" ? ( + item.title + ) : ( + <> + Explore {item.title} + + + )} {active && anchors.length > 0 && (
      diff --git a/src/components/toc-hero/toc-hero-contents.tsx b/src/components/toc-hero/toc-hero-contents.tsx index 84f060efb3..bbdd207a85 100644 --- a/src/components/toc-hero/toc-hero-contents.tsx +++ b/src/components/toc-hero/toc-hero-contents.tsx @@ -1,6 +1,7 @@ import { clsx } from "clsx" import { ChevronRight } from "@/app/conf/_design-system/pixelarticons/chevron-right" +import { slugify } from "@/app/(main)/resources/[category]/texts" export interface TocHeroContentsProps extends React.HTMLAttributes { @@ -34,7 +35,7 @@ export function TocHeroContents({ typeof section === "string" ? { name: section, - href: `#${section.toLowerCase().replace(/ /g, "-")}`, + href: `#${slugify(section)}`, } : section diff --git a/src/env.d.ts b/src/env.d.ts index a5309effaf..dced5d24c3 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -17,6 +17,11 @@ declare module "*?raw" { export default content } +declare module "*.svg?resource" { + const url: string + export default url +} + // We're importing a transitive dependency to avoid a bug. declare module "next-themes" { export function ThemeProvider(props: { diff --git a/src/pages/_meta.tsx b/src/pages/_meta.tsx index 115fb056f2..37511d0d8b 100644 --- a/src/pages/_meta.tsx +++ b/src/pages/_meta.tsx @@ -10,17 +10,34 @@ export default { type: "page", title: "Learn", }, + resources: { + type: "menu", + title: "Resource Hub", + route: "/resources", + items: { + index: "Resource Hub", + frontend: "", + backend: "", + federation: "", + ai: "AI", + security: "", + monitoring: "", + "tools-and-libraries": "Tools & Libraries", + spec: { + type: "page", + title: "Specification", + href: "https://spec.graphql.org", + newWindow: true, + }, + video: "Video Resources Library", + reading: "Reading Resources Library", + }, + }, community: { type: "menu", title: "Community", items: { - "tools-and-libraries": { - title: "Tools and Libraries", - }, - resources: { - title: "Resources", - href: "/community/resources/official-channels", - }, + // TODO: Set a redirect from /community/tools-and-libraries to /resources/tools-and-libraries events: { title: "Events", type: "page", @@ -35,15 +52,9 @@ export default { }, }, faq: { - type: "page", + type: "hidden", title: "FAQ", }, - spec: { - type: "page", - title: "Spec", - href: "https://spec.graphql.org", - newWindow: true, - }, blog: { type: "page", title: "Blog", diff --git a/src/pages/community/resources/blogs-and-newsletters.mdx b/src/pages/community/resources/blogs-and-newsletters.mdx index 46331f19e2..1927c4b4ff 100644 --- a/src/pages/community/resources/blogs-and-newsletters.mdx +++ b/src/pages/community/resources/blogs-and-newsletters.mdx @@ -35,7 +35,6 @@ Here are a list of notable blog posts to help you better understand GraphQL: - [Your First GraphQL Server](https://medium.com/the-graphqlhub/your-first-graphql-server-3c766ab4f0a2#.ovn0y19k4) - Clay Allsopp - [Tutorial: Kick start a JS API with Apollo-server, Dataloader and Knex](https://bamtech.gitbook.io/dev-standards/backend/graphql-js/getting-started-with-apollo-server-dataloader-knex.mo) - Thomas Pucci - [Tutorial: How to Build a GraphQL Server](https://medium.com/apollo-stack/tutorial-building-a-graphql-server-cddaa023c035#.bu6sdnst4) - Jonas Helfer -- [Designing Powerful APIs with GraphQL Query Parameters](https://www.graph.cool/docs/tutorials/designing-powerful-apis-with-graphql-query-parameters-aing7uech3/) - Johannes Schickling - [GraphQL and the amazing Apollo Client](https://medium.com/google-developer-experts/graphql-and-the-amazing-apollo-client-fe57e162a70c) - Gerard Sans - [GraphQL Server Basics (Part I): The Schema](https://www.prisma.io/blog/graphql-server-basics-the-schema-ac5e2950214e) - Nikolas Burk - [GraphQL Server Basics (Part II): The Network Layer](https://www.prisma.io/blog/graphql-server-basics-the-network-layer-51d97d21861) - Nikolas Burk diff --git a/src/pages/learn/index.mdx b/src/pages/learn/index.mdx index c26ab6d58e..a121f17c81 100644 --- a/src/pages/learn/index.mdx +++ b/src/pages/learn/index.mdx @@ -11,7 +11,7 @@ import { LearnHeroStripes } from '../../components/learn-aggregator/learn-hero-s import { pagesBySection } from '../../components/learn-aggregator/learn-pages' import { CommonQuestionsSection } from '../../components/learn-aggregator/common-questions' import { TrainingCoursesSection } from '../../components/learn-aggregator/training-courses' -import { LookingForMore } from "../../components/learn-aggregator/looking-for-more" +import { LookingForMore } from "../../components/looking-for-more" - + + diff --git a/src/resources/data.ts b/src/resources/data.ts new file mode 100644 index 0000000000..db37f4d1b8 --- /dev/null +++ b/src/resources/data.ts @@ -0,0 +1,127 @@ +import path from "node:path" +import { glob } from "node:fs/promises" +import { readFile } from "node:fs/promises" +import { cache } from "react" +import matter from "gray-matter" + +import { ResourceMetadata, type ResourceTag, topics } from "./types" + +const dataGlob = "src/resources/data/*.json" +const codeGlob = "src/code/**/*.md" +const blogGlob = "src/pages/blog/**/*.mdx" + +export const readResources = cache(async () => { + const resources: ResourceMetadata[] = [] + + for await (const file of glob(dataGlob)) { + const raw = await readFile(file, "utf8") + const parsed = JSON.parse(raw) + resources.push(ResourceMetadata.assert(parsed)) + } + + for await (const file of glob(blogGlob)) { + const raw = await readFile(file, "utf8") + const { data, content } = matter(raw) + + const title: string | undefined = data.title + if (!title) continue + + const slug = blogSlug(file) + + const bodyLines = content + .split(/\r?\n/) + .map(line => line.trim()) + .map(line => + line + .replace(/!\[[^\]]*\]\([^)]+\)/g, "") + .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") + .replace(/`+/g, "") + .replace(/[*_~]+/g, "") + .replace(/^#+\s*/, "") + .replace(/<\/?[^>]+>/g, "") + .trim(), + ) + .filter(line => line.length > 0) + + const excerpt = bodyLines.slice(0, 2).join(" ") + + const description = + typeof data.description === "string" && data.description.length > 0 + ? data.description + : excerpt || undefined + + const topicsFromFrontmatter: ResourceTag[] = Array.isArray(data.topics) + ? data.topics.filter((tag): tag is ResourceTag => + topics.includes(tag as (typeof topics)[number]), + ) + : [] + + const topicTagsFromTags: ResourceTag[] = Array.isArray(data.tags) + ? data.tags.filter((tag): tag is ResourceTag => + topics.includes(tag as (typeof topics)[number]), + ) + : [] + + const tags: ResourceTag[] = [ + "blog", + ...topicsFromFrontmatter, + ...topicTagsFromTags, + ] + + resources.push( + ResourceMetadata.assert({ + title, + url: slug, + author: data.byline, + description, + kind: "blog", + tags, + }), + ) + } + + for await (const file of glob(codeGlob)) { + const raw = await readFile(file, "utf8") + const { data } = matter(raw) + const tags: ResourceMetadata["tags"] = Array.isArray(data.tags) + ? data.tags + : [] + + if (!tags.includes("tools-and-libraries")) { + tags.push("tools-and-libraries") + } + + const url: string | undefined = + data.url ?? + (data.github ? `https://github.com/${data.github}` : undefined) ?? + (data.npm ? `https://npmjs.com/package/${data.npm}` : undefined) + + const title = data.name ?? path.parse(file).name + + resources.push( + ResourceMetadata.assert({ + title, + url, + description: data.description, + tags, + }), + ) + } + + return resources +}) + +export async function getResourcesByTag(tag: ResourceTag) { + const resources = await readResources() + return resources.filter(resource => resource.tags.includes(tag)) +} + +function blogSlug(file: string) { + const relative = path.relative("src/pages", file) + const withoutExt = relative.replace(/\.mdx$/, "") + const normalized = withoutExt.split(path.sep).join("/") + const clean = normalized.endsWith("/index") + ? normalized.slice(0, -"index".length - 1) + : normalized + return `/${clean}` +} diff --git a/src/resources/data/a-beginner-s-guide-to-graphql.json b/src/resources/data/a-beginner-s-guide-to-graphql.json new file mode 100644 index 0000000000..2552c02efd --- /dev/null +++ b/src/resources/data/a-beginner-s-guide-to-graphql.json @@ -0,0 +1,6 @@ +{ + "title": "A Beginner’s Guide to GraphQL", + "url": "https://www.freecodecamp.org/news/a-beginners-guide-to-graphql-86f849ce1bec/", + "author": "Leonardo Maldonado", + "tags": ["blog"] +} diff --git a/src/resources/data/a-graphql-framework-for-non-js-servers-syrus-akbary.json b/src/resources/data/a-graphql-framework-for-non-js-servers-syrus-akbary.json new file mode 100644 index 0000000000..aac5f9768a --- /dev/null +++ b/src/resources/data/a-graphql-framework-for-non-js-servers-syrus-akbary.json @@ -0,0 +1,7 @@ +{ + "title": "A GraphQL Framework for Non-JS Servers", + "author": "Syrus Akbary", + "url": "https://www.youtube.com/watch?v=RNoyPSrQyPs", + "tags": ["video", "backend"], + "duration": "21:32" +} diff --git a/src/resources/data/a-postgresql-backed-graphql-baas.json b/src/resources/data/a-postgresql-backed-graphql-baas.json new file mode 100644 index 0000000000..eacf30f518 --- /dev/null +++ b/src/resources/data/a-postgresql-backed-graphql-baas.json @@ -0,0 +1,7 @@ +{ + "title": "A PostgreSQL backed GraphQL BaaS", + "author": "Tanmai Gopal", + "url": "https://www.youtube.com/watch?v=neIZcc8y3B0", + "tags": ["video", "backend"], + "duration": "14:27" +} diff --git a/src/resources/data/all-talks-from-graphql-europe.json b/src/resources/data/all-talks-from-graphql-europe.json new file mode 100644 index 0000000000..6ab2b1477e --- /dev/null +++ b/src/resources/data/all-talks-from-graphql-europe.json @@ -0,0 +1,5 @@ +{ + "title": "All Talks from GraphQL Europe", + "url": "https://www.youtube.com/playlist?list=PLn2e1F9Rfr6n_WFm9fPE-_wYPrYvSTySt", + "tags": ["video", "frontend", "backend"] +} diff --git a/src/resources/data/apollo-client-put-graphql-data-in-your-ui.json b/src/resources/data/apollo-client-put-graphql-data-in-your-ui.json new file mode 100644 index 0000000000..bfa3fdd38f --- /dev/null +++ b/src/resources/data/apollo-client-put-graphql-data-in-your-ui.json @@ -0,0 +1,7 @@ +{ + "title": "Apollo Client: Put GraphQL Data in Your UI", + "author": "Sashko Stubailo", + "url": "https://www.youtube.com/watch?v=u1E0CbGeICo", + "tags": ["video", "frontend"], + "duration": "18:27" +} diff --git a/src/resources/data/apollo-odyssey.json b/src/resources/data/apollo-odyssey.json new file mode 100644 index 0000000000..aa11a294bd --- /dev/null +++ b/src/resources/data/apollo-odyssey.json @@ -0,0 +1,5 @@ +{ + "title": "Apollo Odyssey", + "url": "https://apollographql.com/tutorials", + "tags": ["guide"] +} diff --git a/src/resources/data/apollo-s-blog.json b/src/resources/data/apollo-s-blog.json new file mode 100644 index 0000000000..e2b5979181 --- /dev/null +++ b/src/resources/data/apollo-s-blog.json @@ -0,0 +1,5 @@ +{ + "title": "Apollo's Blog", + "url": "https://apollographql.com/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/architecture-of-a-high-performance-graphql-to-sql-engine.json b/src/resources/data/architecture-of-a-high-performance-graphql-to-sql-engine.json new file mode 100644 index 0000000000..f107cf7911 --- /dev/null +++ b/src/resources/data/architecture-of-a-high-performance-graphql-to-sql-engine.json @@ -0,0 +1,6 @@ +{ + "title": "Architecture of a high performance GraphQL to SQL engine", + "url": "https://blog.hasura.io/architecture-of-a-high-performance-graphql-to-sql-server-58d9944b8a87", + "author": "Sandip Devarkonda", + "tags": ["blog"] +} diff --git a/src/resources/data/awesome-graphql.json b/src/resources/data/awesome-graphql.json new file mode 100644 index 0000000000..0befab9e45 --- /dev/null +++ b/src/resources/data/awesome-graphql.json @@ -0,0 +1,5 @@ +{ + "title": "awesome-graphql", + "url": "https://github.com/chentsulin/awesome-graphql", + "tags": ["guide"] +} diff --git a/src/resources/data/brand-guidelines.json b/src/resources/data/brand-guidelines.json new file mode 100644 index 0000000000..f32f7804f8 --- /dev/null +++ b/src/resources/data/brand-guidelines.json @@ -0,0 +1,5 @@ +{ + "title": "brand guidelines", + "url": "/brand", + "tags": ["guide"] +} diff --git a/src/resources/data/build-a-full-graphql-backend-in-under-5-minutes-michael-paris.json b/src/resources/data/build-a-full-graphql-backend-in-under-5-minutes-michael-paris.json new file mode 100644 index 0000000000..7fc5936027 --- /dev/null +++ b/src/resources/data/build-a-full-graphql-backend-in-under-5-minutes-michael-paris.json @@ -0,0 +1,7 @@ +{ + "title": "Build a Full GraphQL Backend in Under 5 Minutes", + "author": "Michael Paris", + "url": "https://www.youtube.com/watch?v=bJ8pnYd6jPQ", + "tags": ["video", "backend"], + "duration": "4:55" +} diff --git a/src/resources/data/build-a-graphql-server-for-node-js-using-postgresql-mysql-lee-benson.json b/src/resources/data/build-a-graphql-server-for-node-js-using-postgresql-mysql-lee-benson.json new file mode 100644 index 0000000000..d48a5744c9 --- /dev/null +++ b/src/resources/data/build-a-graphql-server-for-node-js-using-postgresql-mysql-lee-benson.json @@ -0,0 +1,7 @@ +{ + "title": "Build a GraphQL server for Node.js, using PostgreSQL/MySQL", + "author": "Lee Benson", + "url": "https://www.youtube.com/watch?v=DNPVqK_woRQ", + "tags": ["video", "backend"], + "duration": "45:04" +} diff --git a/src/resources/data/building-native-mobile-apps-with-graphql-martjin-walraven-react-europe-2016.json b/src/resources/data/building-native-mobile-apps-with-graphql-martjin-walraven-react-europe-2016.json new file mode 100644 index 0000000000..948a1ff738 --- /dev/null +++ b/src/resources/data/building-native-mobile-apps-with-graphql-martjin-walraven-react-europe-2016.json @@ -0,0 +1,7 @@ +{ + "title": "Building Native Mobile Apps with GraphQL", + "author": "Martjin Walraven", + "url": "https://www.youtube.com/watch?v=z5rz3saDPJ8", + "tags": ["video", "frontend"], + "duration": "23:55" +} diff --git a/src/resources/data/building-the-f8-app-using-graphql-relay.json b/src/resources/data/building-the-f8-app-using-graphql-relay.json new file mode 100644 index 0000000000..3c36df9045 --- /dev/null +++ b/src/resources/data/building-the-f8-app-using-graphql-relay.json @@ -0,0 +1,5 @@ +{ + "title": "Building the f8 App: Using GraphQL & Relay", + "url": "http://makeitopen.com/docs/en/1-A2-relay.html", + "tags": ["blog"] +} diff --git a/src/resources/data/chillicream-s-blog.json b/src/resources/data/chillicream-s-blog.json new file mode 100644 index 0000000000..68a2657d38 --- /dev/null +++ b/src/resources/data/chillicream-s-blog.json @@ -0,0 +1,5 @@ +{ + "title": "ChilliCream's Blog", + "url": "https://chillicream.com/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/community-events-section.json b/src/resources/data/community-events-section.json new file mode 100644 index 0000000000..2c3af619b8 --- /dev/null +++ b/src/resources/data/community-events-section.json @@ -0,0 +1,5 @@ +{ + "title": "community events section", + "url": "/community/upcoming-events/#meetups", + "tags": ["guide"] +} diff --git a/src/resources/data/craft-graphql-apis-in-elixir-with-absinthe.json b/src/resources/data/craft-graphql-apis-in-elixir-with-absinthe.json new file mode 100644 index 0000000000..b8c5cf6344 --- /dev/null +++ b/src/resources/data/craft-graphql-apis-in-elixir-with-absinthe.json @@ -0,0 +1,6 @@ +{ + "title": "Craft GraphQL APIs in Elixir with Absinthe", + "url": "https://pragprog.com/titles/wwgraphql/craft-graphql-apis-in-elixir-with-absinthe/", + "author": "Bruce Williams & Ben Wilson", + "tags": ["book"] +} diff --git a/src/resources/data/designing-powerful-apis-with-graphql-query-parameters.json b/src/resources/data/designing-powerful-apis-with-graphql-query-parameters.json new file mode 100644 index 0000000000..d3f99c4a56 --- /dev/null +++ b/src/resources/data/designing-powerful-apis-with-graphql-query-parameters.json @@ -0,0 +1,5 @@ +{ + "title": "Designing Powerful APIs with GraphQL Query Parameters", + "url": "https://www.graph.cool/docs/tutorials/designing-powerful-apis-with-graphql-query-parameters-aing7uech3/", + "tags": ["blog"] +} diff --git a/src/resources/data/dev-to-graphql-tag.json b/src/resources/data/dev-to-graphql-tag.json new file mode 100644 index 0000000000..7ce2241769 --- /dev/null +++ b/src/resources/data/dev-to-graphql-tag.json @@ -0,0 +1,5 @@ +{ + "title": "DEV.to GraphQL tag", + "url": "https://dev.to/t/graphql", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/development-of-real-time-apps-with-graphql-node-js-vince-ning-michael-paris-sf-node-meetup-february-2017.json b/src/resources/data/development-of-real-time-apps-with-graphql-node-js-vince-ning-michael-paris-sf-node-meetup-february-2017.json new file mode 100644 index 0000000000..b8d5ed3bce --- /dev/null +++ b/src/resources/data/development-of-real-time-apps-with-graphql-node-js-vince-ning-michael-paris-sf-node-meetup-february-2017.json @@ -0,0 +1,7 @@ +{ + "title": "Development of real-time apps with GraphQL Node.js", + "author": "Vince Ning & Michael Paris", + "url": "https://youtu.be/yh_A6CEqsSM", + "tags": ["video", "backend"], + "duration": "22:33" +} diff --git a/src/resources/data/escape-security-blog.json b/src/resources/data/escape-security-blog.json new file mode 100644 index 0000000000..a0190d21a3 --- /dev/null +++ b/src/resources/data/escape-security-blog.json @@ -0,0 +1,5 @@ +{ + "title": "Escape Security Blog", + "url": "https://escape.tech/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/exploring-graphql.json b/src/resources/data/exploring-graphql.json new file mode 100644 index 0000000000..1729969bb2 --- /dev/null +++ b/src/resources/data/exploring-graphql.json @@ -0,0 +1,7 @@ +{ + "title": "Exploring GraphQL", + "url": "https://youtube.com/watch?v=WQLzZf34FJ8", + "author": "Lee Byron", + "tags": ["video"], + "duration": "27:18" +} diff --git a/src/resources/data/from-rest-to-graphql.json b/src/resources/data/from-rest-to-graphql.json new file mode 100644 index 0000000000..b672e423aa --- /dev/null +++ b/src/resources/data/from-rest-to-graphql.json @@ -0,0 +1,6 @@ +{ + "title": "From REST to GraphQL", + "url": "https://0x2a.sh/from-rest-to-graphql-b4e95e94c26b#.tag7nzkrb", + "author": "Garen J. Torikian", + "tags": ["blog"] +} diff --git a/src/resources/data/from-zero-to-graphql-in-30-minutes.json b/src/resources/data/from-zero-to-graphql-in-30-minutes.json new file mode 100644 index 0000000000..3460116995 --- /dev/null +++ b/src/resources/data/from-zero-to-graphql-in-30-minutes.json @@ -0,0 +1,7 @@ +{ + "title": "From Zero to GraphQL in 30 Minutes", + "url": "https://youtube.com/watch?v=UBGzsb2UkeY", + "author": "Steven Luscher", + "tags": ["video"], + "duration": "30:13" +} diff --git a/src/resources/data/fullstack-graphql.json b/src/resources/data/fullstack-graphql.json new file mode 100644 index 0000000000..0f163bf5ec --- /dev/null +++ b/src/resources/data/fullstack-graphql.json @@ -0,0 +1,6 @@ +{ + "title": "Fullstack GraphQL", + "url": "https://www.graphqladmin.com/books/fullstack-graphql", + "author": "Julian Mayorga", + "tags": ["book"] +} diff --git a/src/resources/data/graphql-and-the-amazing-apollo-client.json b/src/resources/data/graphql-and-the-amazing-apollo-client.json new file mode 100644 index 0000000000..e74ff64ec1 --- /dev/null +++ b/src/resources/data/graphql-and-the-amazing-apollo-client.json @@ -0,0 +1,6 @@ +{ + "title": "GraphQL and the amazing Apollo Client", + "url": "https://medium.com/google-developer-experts/graphql-and-the-amazing-apollo-client-fe57e162a70c", + "author": "Gerard Sans", + "tags": ["blog"] +} diff --git a/src/resources/data/graphql-apis.json b/src/resources/data/graphql-apis.json new file mode 100644 index 0000000000..6c7365fdae --- /dev/null +++ b/src/resources/data/graphql-apis.json @@ -0,0 +1,5 @@ +{ + "title": "graphql-apis", + "url": "https://github.com/APIs-guru/graphql-apis", + "tags": ["guide"] +} diff --git a/src/resources/data/graphql-at-facebook.json b/src/resources/data/graphql-at-facebook.json new file mode 100644 index 0000000000..ab864678b8 --- /dev/null +++ b/src/resources/data/graphql-at-facebook.json @@ -0,0 +1,7 @@ +{ + "title": "GraphQL at Facebook", + "url": "https://youtube.com/watch?v=etax3aEe2dA", + "author": "Dan Schafer", + "tags": ["video"], + "duration": "26:30" +} diff --git a/src/resources/data/graphql-best-practices-hands-on-experience-with-schema-design-security-and-error-handling-for-developers.json b/src/resources/data/graphql-best-practices-hands-on-experience-with-schema-design-security-and-error-handling-for-developers.json new file mode 100644 index 0000000000..c5fba326c9 --- /dev/null +++ b/src/resources/data/graphql-best-practices-hands-on-experience-with-schema-design-security-and-error-handling-for-developers.json @@ -0,0 +1,6 @@ +{ + "title": "GraphQL Best Practices: Hands-on experience with schema design, security, and error handling for developers", + "url": "https://www.amazon.com/dp/B0D9H7MJQV", + "author": "Marc-André Giroux & Apoorva Pandey", + "tags": ["book"] +} diff --git a/src/resources/data/graphql-code-of-conduct.json b/src/resources/data/graphql-code-of-conduct.json new file mode 100644 index 0000000000..64d708b1d2 --- /dev/null +++ b/src/resources/data/graphql-code-of-conduct.json @@ -0,0 +1,5 @@ +{ + "title": "GraphQL Code of Conduct", + "url": "/codeofconduct/", + "tags": ["guide"] +} diff --git a/src/resources/data/graphql-concepts-visualized.json b/src/resources/data/graphql-concepts-visualized.json new file mode 100644 index 0000000000..0e8e235f89 --- /dev/null +++ b/src/resources/data/graphql-concepts-visualized.json @@ -0,0 +1,6 @@ +{ + "title": "GraphQL Concepts Visualized", + "url": "https://medium.com/apollo-stack/the-concepts-of-graphql-bc68bd819be3#.hfczgtdsj", + "author": "Dhaivat Pandya", + "tags": ["blog"] +} diff --git a/src/resources/data/graphql-editor-blog.json b/src/resources/data/graphql-editor-blog.json new file mode 100644 index 0000000000..e178be9bcc --- /dev/null +++ b/src/resources/data/graphql-editor-blog.json @@ -0,0 +1,5 @@ +{ + "title": "GraphQL Editor Blog", + "url": "https://blog.graphqleditor.com", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/graphql-explained.json b/src/resources/data/graphql-explained.json new file mode 100644 index 0000000000..8f969374d4 --- /dev/null +++ b/src/resources/data/graphql-explained.json @@ -0,0 +1,6 @@ +{ + "title": "GraphQL Explained", + "url": "https://medium.com/apollo-stack/graphql-explained-5844742f195e#.zdykxos6i", + "author": "JH", + "tags": ["blog"] +} diff --git a/src/resources/data/graphql-from-zero-to-scala.json b/src/resources/data/graphql-from-zero-to-scala.json new file mode 100644 index 0000000000..16794d200c --- /dev/null +++ b/src/resources/data/graphql-from-zero-to-scala.json @@ -0,0 +1,7 @@ +{ + "title": "GraphQL: From Zero to Scala", + "author": "Jérémie Astori", + "url": "https://www.youtube.com/watch?v=6ttypoLyRaU", + "tags": ["video", "backend"], + "duration": "15:31" +} diff --git a/src/resources/data/graphql-future.json b/src/resources/data/graphql-future.json new file mode 100644 index 0000000000..34e0edabf9 --- /dev/null +++ b/src/resources/data/graphql-future.json @@ -0,0 +1,7 @@ +{ + "title": "GraphQL Future", + "url": "https://youtube.com/watch?v=ViXL0YQnioU", + "author": "Lee Byron", + "tags": ["video"], + "duration": "32:18" +} diff --git a/src/resources/data/graphql-in-native-applications.json b/src/resources/data/graphql-in-native-applications.json new file mode 100644 index 0000000000..18112cdf2b --- /dev/null +++ b/src/resources/data/graphql-in-native-applications.json @@ -0,0 +1,7 @@ +{ + "title": "GraphQL in native applications", + "author": "Igor Canadi & Alex Langenfeld", + "url": "https://atscaleconference.com/videos/graphql-in-native-applications-at-scale/", + "tags": ["video", "frontend"], + "duration": "26:15" +} diff --git a/src/resources/data/graphql-in-production-backend-as-a-service-michael-paris-vince-ning-graphql-in-production-meetup-sf-august-2016.json b/src/resources/data/graphql-in-production-backend-as-a-service-michael-paris-vince-ning-graphql-in-production-meetup-sf-august-2016.json new file mode 100644 index 0000000000..e4500dbfcd --- /dev/null +++ b/src/resources/data/graphql-in-production-backend-as-a-service-michael-paris-vince-ning-graphql-in-production-meetup-sf-august-2016.json @@ -0,0 +1,7 @@ +{ + "title": "GraphQL in Production: Backend as a Service", + "author": "Michael Paris & Vince Ning", + "url": "https://www.youtube.com/watch?v=U2NKoStGBvE", + "tags": ["video", "backend"], + "duration": "19:08" +} diff --git a/src/resources/data/graphql-js-tutorial.json b/src/resources/data/graphql-js-tutorial.json new file mode 100644 index 0000000000..8ad1d6af05 --- /dev/null +++ b/src/resources/data/graphql-js-tutorial.json @@ -0,0 +1,5 @@ +{ + "title": "GraphQL-JS tutorial", + "url": "/graphql-js", + "tags": ["guide"] +} diff --git a/src/resources/data/graphql-screencasts.json b/src/resources/data/graphql-screencasts.json new file mode 100644 index 0000000000..7f21b01ad3 --- /dev/null +++ b/src/resources/data/graphql-screencasts.json @@ -0,0 +1,5 @@ +{ + "title": "GraphQL Screencasts", + "url": "https://graphql.wtf", + "tags": ["guide"] +} diff --git a/src/resources/data/graphql-server-tutorial-for-node-js-with-sql-mongodb-and-rest-jonas-helfer.json b/src/resources/data/graphql-server-tutorial-for-node-js-with-sql-mongodb-and-rest-jonas-helfer.json new file mode 100644 index 0000000000..9ba61ed60f --- /dev/null +++ b/src/resources/data/graphql-server-tutorial-for-node-js-with-sql-mongodb-and-rest-jonas-helfer.json @@ -0,0 +1,7 @@ +{ + "title": "GraphQL server tutorial for Node.js with SQL, MongoDB and REST", + "author": "Jonas Helfer", + "url": "https://www.youtube.com/watch?v=PHabPhgRUuU", + "tags": ["video", "backend"], + "duration": "1:03:01" +} diff --git a/src/resources/data/graphql-servers.json b/src/resources/data/graphql-servers.json new file mode 100644 index 0000000000..95b1b633b3 --- /dev/null +++ b/src/resources/data/graphql-servers.json @@ -0,0 +1,7 @@ +{ + "title": "GraphQL Servers", + "author": "Nick Schrock", + "url": "https://www.youtube.com/watch?v=KOudxKJXsjc", + "tags": ["video", "backend"], + "duration": "28:03" +} diff --git a/src/resources/data/graphql-source-code-overview.json b/src/resources/data/graphql-source-code-overview.json new file mode 100644 index 0000000000..bc2f659367 --- /dev/null +++ b/src/resources/data/graphql-source-code-overview.json @@ -0,0 +1,7 @@ +{ + "title": "GraphQL Source Code Overview", + "author": "Lee Byron", + "url": "https://youtube.com/watch?v=IqtYr6RX32Q", + "tags": ["video", "backend"], + "duration": "25:32" +} diff --git a/src/resources/data/graphql-tutorials.json b/src/resources/data/graphql-tutorials.json new file mode 100644 index 0000000000..994d2ff091 --- /dev/null +++ b/src/resources/data/graphql-tutorials.json @@ -0,0 +1,5 @@ +{ + "title": "GraphQL Tutorials", + "url": "https://hasura.io/learn", + "tags": ["guide"] +} diff --git a/src/resources/data/graphql-weekly.json b/src/resources/data/graphql-weekly.json new file mode 100644 index 0000000000..2d90509692 --- /dev/null +++ b/src/resources/data/graphql-weekly.json @@ -0,0 +1,5 @@ +{ + "title": "GraphQL Weekly", + "url": "https://graphqlweekly.com", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/graphql-wtf-episodes-feed.json b/src/resources/data/graphql-wtf-episodes-feed.json new file mode 100644 index 0000000000..f59eceabdb --- /dev/null +++ b/src/resources/data/graphql-wtf-episodes-feed.json @@ -0,0 +1,5 @@ +{ + "title": "GraphQL WTF Episodes Feed", + "url": "https://graphql.wtf", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/hands-on-full-stack-web-development-with-graphql-and-react.json b/src/resources/data/hands-on-full-stack-web-development-with-graphql-and-react.json new file mode 100644 index 0000000000..57c9172b6b --- /dev/null +++ b/src/resources/data/hands-on-full-stack-web-development-with-graphql-and-react.json @@ -0,0 +1,6 @@ +{ + "title": "Hands-on Full-Stack Web Development with GraphQL and React", + "url": "https://www.packtpub.com/en-us/product/hands-on-full-stack-web-development-with-graphql-and-react-9781789135763", + "author": "Sebastian Grebe", + "tags": ["guide"] +} diff --git a/src/resources/data/hasura-s-blog.json b/src/resources/data/hasura-s-blog.json new file mode 100644 index 0000000000..7addc43662 --- /dev/null +++ b/src/resources/data/hasura-s-blog.json @@ -0,0 +1,5 @@ +{ + "title": "Hasura's Blog", + "url": "https://hasura.io/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/inigo-s-security-blog.json b/src/resources/data/inigo-s-security-blog.json new file mode 100644 index 0000000000..ef9f0f1c82 --- /dev/null +++ b/src/resources/data/inigo-s-security-blog.json @@ -0,0 +1,5 @@ +{ + "title": "Inigo's Security Blog", + "url": "https://inigo.io/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/learning-graphql-and-relay.json b/src/resources/data/learning-graphql-and-relay.json new file mode 100644 index 0000000000..a5b131f8f9 --- /dev/null +++ b/src/resources/data/learning-graphql-and-relay.json @@ -0,0 +1,6 @@ +{ + "title": "Learning GraphQL and Relay", + "url": "https://www.amazon.com/Learning-GraphQL-Relay-Samer-Buna/dp/1786465752", + "author": "Samer Buna", + "tags": ["book"] +} diff --git a/src/resources/data/learning-graphql.json b/src/resources/data/learning-graphql.json new file mode 100644 index 0000000000..ee063bd648 --- /dev/null +++ b/src/resources/data/learning-graphql.json @@ -0,0 +1,6 @@ +{ + "title": "Learning GraphQL", + "url": "https://www.amazon.com/Learning-GraphQL-Declarative-Fetching-Modern/dp/1492030716/", + "author": "Eve Porcello & Alex Banks", + "tags": ["book"] +} diff --git a/src/resources/data/modernize-your-angular-app-with-graphql.json b/src/resources/data/modernize-your-angular-app-with-graphql.json new file mode 100644 index 0000000000..e62c2b7c82 --- /dev/null +++ b/src/resources/data/modernize-your-angular-app-with-graphql.json @@ -0,0 +1,7 @@ +{ + "title": "Modernize Your Angular App with GraphQL", + "author": "Uri Goldshtein", + "url": "https://www.youtube.com/watch?v=E8feZBidZcs", + "tags": ["video", "frontend"], + "duration": "37:38" +} diff --git a/src/resources/data/official-graphql-blog.json b/src/resources/data/official-graphql-blog.json new file mode 100644 index 0000000000..63976c18d7 --- /dev/null +++ b/src/resources/data/official-graphql-blog.json @@ -0,0 +1,5 @@ +{ + "title": "Official GraphQL Blog", + "url": "https://graphql.org/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/production-ready-graphql.json b/src/resources/data/production-ready-graphql.json new file mode 100644 index 0000000000..48022997b1 --- /dev/null +++ b/src/resources/data/production-ready-graphql.json @@ -0,0 +1,6 @@ +{ + "title": "Production Ready GraphQL", + "url": "https://book.productionreadygraphql.com/", + "author": "Marc-Andre Giroux", + "tags": ["guide"] +} diff --git a/src/resources/data/relay-2-simpler-faster-and-more-predictable-greg-hurrell.json b/src/resources/data/relay-2-simpler-faster-and-more-predictable-greg-hurrell.json new file mode 100644 index 0000000000..f0a6960ed5 --- /dev/null +++ b/src/resources/data/relay-2-simpler-faster-and-more-predictable-greg-hurrell.json @@ -0,0 +1,7 @@ +{ + "title": "Relay 2 - simpler, faster, and more predictable", + "author": "Greg Hurrell", + "url": "https://www.youtube.com/watch?v=OEfUBN9dAI8", + "tags": ["video", "frontend", "backend"], + "duration": "1:26:01" +} diff --git a/src/resources/data/relicensing-the-graphql-specification.json b/src/resources/data/relicensing-the-graphql-specification.json new file mode 100644 index 0000000000..334cfdf4c0 --- /dev/null +++ b/src/resources/data/relicensing-the-graphql-specification.json @@ -0,0 +1,6 @@ +{ + "title": "Relicensing the GraphQL specification", + "url": "https://medium.com/@leeb/relicensing-the-graphql-specification-e7d07a52301b", + "author": "Lee Byron", + "tags": ["blog"] +} diff --git a/src/resources/data/stepzen-s-blog.json b/src/resources/data/stepzen-s-blog.json new file mode 100644 index 0000000000..208aca0ebb --- /dev/null +++ b/src/resources/data/stepzen-s-blog.json @@ -0,0 +1,5 @@ +{ + "title": "StepZen's Blog", + "url": "https://stepzen.com/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/the-graphql-guide.json b/src/resources/data/the-graphql-guide.json new file mode 100644 index 0000000000..b34d66f420 --- /dev/null +++ b/src/resources/data/the-graphql-guide.json @@ -0,0 +1,6 @@ +{ + "title": "The GraphQL Guide", + "url": "https://graphql.guide", + "author": "Loren Sands-Ramshaw", + "tags": ["guide"] +} diff --git a/src/resources/data/the-guild-s-blog.json b/src/resources/data/the-guild-s-blog.json new file mode 100644 index 0000000000..461761c85b --- /dev/null +++ b/src/resources/data/the-guild-s-blog.json @@ -0,0 +1,5 @@ +{ + "title": "The Guild's Blog", + "url": "https://the-guild.dev/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/the-guild-s-newsletter.json b/src/resources/data/the-guild-s-newsletter.json new file mode 100644 index 0000000000..c34b24e874 --- /dev/null +++ b/src/resources/data/the-guild-s-newsletter.json @@ -0,0 +1,5 @@ +{ + "title": "The Guild's Newsletter", + "url": "https://getrevue.co/profile/TheGuild", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/the-road-to-graphql.json b/src/resources/data/the-road-to-graphql.json new file mode 100644 index 0000000000..a9a97f091f --- /dev/null +++ b/src/resources/data/the-road-to-graphql.json @@ -0,0 +1,6 @@ +{ + "title": "The Road to GraphQL", + "url": "https://www.robinwieruch.de/the-road-to-graphql-book/", + "author": "Robin Wieruch", + "tags": ["guide"] +} diff --git a/src/resources/data/trademark-policy.json b/src/resources/data/trademark-policy.json new file mode 100644 index 0000000000..c51132fd8e --- /dev/null +++ b/src/resources/data/trademark-policy.json @@ -0,0 +1,5 @@ +{ + "title": "Linux Foundation Trademark Policy", + "url": "https://lfprojects.org/policies/trademark-policy/", + "tags": ["guide"] +} diff --git a/src/resources/data/tutorial-how-to-build-a-graphql-server.json b/src/resources/data/tutorial-how-to-build-a-graphql-server.json new file mode 100644 index 0000000000..270598c7f5 --- /dev/null +++ b/src/resources/data/tutorial-how-to-build-a-graphql-server.json @@ -0,0 +1,6 @@ +{ + "title": "Tutorial: How to Build a GraphQL Server", + "url": "https://medium.com/apollo-stack/tutorial-building-a-graphql-server-cddaa023c035#.bu6sdnst4", + "author": "Jonas Helfer & Johanna Griffin", + "tags": ["blog"] +} diff --git a/src/resources/data/tutorial-kick-start-a-js-api-with-apollo-server-dataloader-and-knex.json b/src/resources/data/tutorial-kick-start-a-js-api-with-apollo-server-dataloader-and-knex.json new file mode 100644 index 0000000000..48eaef09bb --- /dev/null +++ b/src/resources/data/tutorial-kick-start-a-js-api-with-apollo-server-dataloader-and-knex.json @@ -0,0 +1,6 @@ +{ + "title": "Tutorial: Kick start a JS API with Apollo-server, Dataloader and Knex", + "url": "https://bamtech.gitbook.io/dev-standards/backend/graphql-js/getting-started-with-apollo-server-dataloader-knex.mo", + "author": "Thomas Pucci", + "tags": ["blog"] +} diff --git a/src/resources/data/unleashing-the-power-of-graphql-using-angular-2-gerard-sans-ng-be-2016.json b/src/resources/data/unleashing-the-power-of-graphql-using-angular-2-gerard-sans-ng-be-2016.json new file mode 100644 index 0000000000..0da38a9a87 --- /dev/null +++ b/src/resources/data/unleashing-the-power-of-graphql-using-angular-2-gerard-sans-ng-be-2016.json @@ -0,0 +1,7 @@ +{ + "title": "Unleashing the power of GraphQL using Angular 2", + "author": "Gerard Sans", + "url": "https://www.youtube.com/watch?v=VYpJ9pfugM8", + "tags": ["video", "frontend"], + "duration": "45:35" +} diff --git a/src/resources/data/wundergraph-s-blog.json b/src/resources/data/wundergraph-s-blog.json new file mode 100644 index 0000000000..645953db5d --- /dev/null +++ b/src/resources/data/wundergraph-s-blog.json @@ -0,0 +1,5 @@ +{ + "title": "WunderGraph's Blog", + "url": "https://wundergraph.com/blog", + "tags": ["blog-or-newsletter"] +} diff --git a/src/resources/data/yoga-graphql-server-tutorial.json b/src/resources/data/yoga-graphql-server-tutorial.json new file mode 100644 index 0000000000..c1bf2f20ae --- /dev/null +++ b/src/resources/data/yoga-graphql-server-tutorial.json @@ -0,0 +1,5 @@ +{ + "title": "Yoga GraphQL Server Tutorial", + "url": "https://the-guild.dev/graphql/yoga-server/tutorial", + "tags": ["guide"] +} diff --git a/src/resources/data/your-first-graphql-server.json b/src/resources/data/your-first-graphql-server.json new file mode 100644 index 0000000000..0af9082ceb --- /dev/null +++ b/src/resources/data/your-first-graphql-server.json @@ -0,0 +1,6 @@ +{ + "title": "Your First GraphQL Server", + "url": "https://medium.com/the-graphqlhub/your-first-graphql-server-3c766ab4f0a2#.ovn0y19k4", + "author": "Clay Allsopp", + "tags": ["blog"] +} diff --git a/src/resources/types.ts b/src/resources/types.ts new file mode 100644 index 0000000000..4b2cc25ef1 --- /dev/null +++ b/src/resources/types.ts @@ -0,0 +1,41 @@ +import { type } from "arktype" + +export const topics = [ + "frontend", + "backend", + "federation", + "schema-design", + "api-platform-and-gateways", + "developer-experience", + "security", + "ai", + "monitoring", + "tools", +] as const +export type Topic = (typeof topics)[number] + +export const kinds = [ + "video", + "blog", + "tools-and-libraries", + "guide", + "book", + "blog-or-newsletter", + "docs", +] as const +export type Kind = (typeof kinds)[number] + +export type ResourceTag = Topic | Kind + +export const ResourceMetadata = type({ + title: "string>0", + url: type("string.url").or("/^\\/.+$/"), + "author?": "string", + "kind?": type.enumerated(...kinds), + "topics?": type.enumerated(...topics).array(), + "description?": "string>0", + "duration?": "string", + tags: type.enumerated(...topics, ...kinds).array(), +}) + +export type ResourceMetadata = typeof ResourceMetadata.inferOut diff --git a/tailwind.config.ts b/tailwind.config.ts index 16649f5949..1dbf34a0fe 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -128,9 +128,10 @@ const config: Config = { plugin(({ addBase }) => { // heading styles addBase({ - ".typography-d1, .typography-h1, .typography-h2, .typography-h3": { - lineHeight: "1.2", - }, + ".typography-d1, .typography-h1, .typography-h2, .typography-h3, .typography-h4": + { + lineHeight: "1.2", + }, ".typography-d1": { fontSize: "48px", "@screen lg": { @@ -155,6 +156,12 @@ const config: Config = { fontSize: "32px", }, }, + ".typography-h4": { + fontSize: "20px", + "@screen md": { + fontSize: "28px", + }, + }, }) // paragraph styles diff --git a/test/e2e/resources-hub.spec.ts b/test/e2e/resources-hub.spec.ts new file mode 100644 index 0000000000..bc1816f630 --- /dev/null +++ b/test/e2e/resources-hub.spec.ts @@ -0,0 +1,24 @@ +import { expect, test } from "@playwright/test" + +const pages = [ + "/resources", + "/resources/frontend", + "/resources/backend", + "/resources/federation", + "/resources/ai", + "/resources/security", + "/resources/monitoring", + "/code", + "/conf", + "/resources/reading", + "/resources/video", +] + +test.describe("Resource hub pages exist", () => { + for (const path of pages) { + test(`renders ${path}`, async ({ page }) => { + const response = await page.goto(path) + expect(response?.ok()).toBeTruthy() + }) + } +}) diff --git a/vercel.json b/vercel.json index 244e7c6f2c..87a8d6f90a 100644 --- a/vercel.json +++ b/vercel.json @@ -400,6 +400,46 @@ "destination": "/blog/2020-10-15-newsletter-september-2020", "permanent": true }, + { + "source": "/community/resources/official-channels", + "destination": "/resources/official-channels", + "permanent": true + }, + { + "source": "/community/resources/training-courses", + "destination": "/resources/training-courses", + "permanent": true + }, + { + "source": "/community/resources/community-channels", + "destination": "/resources/community-channels", + "permanent": true + }, + { + "source": "/community/resources/blogs-and-newsletters", + "destination": "/resources/blogs-and-newsletters", + "permanent": true + }, + { + "source": "/community/resources/videos", + "destination": "/resources/videos", + "permanent": true + }, + { + "source": "/community/resources/vendor-channels", + "destination": "/resources/vendor-channels", + "permanent": true + }, + { + "source": "/community/resources/books", + "destination": "/resources/books", + "permanent": true + }, + { + "source": "/community/resources/more-resources", + "destination": "/resources", + "permanent": true + }, { "source": "/blog/2020-10-15-graphql-foundation-monthly-newsletter-september-2020/", "destination": "/blog/2020-10-15-newsletter-september-2020",