From 4b06d756c53678fee39ef6a5e935c2875424484f Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Mon, 9 Aug 2021 09:53:15 +1000 Subject: [PATCH 01/28] Update index.tsx (#6278) --- docs/pages/updates/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/updates/index.tsx b/docs/pages/updates/index.tsx index 65f93a1638a..b15ff603327 100644 --- a/docs/pages/updates/index.tsx +++ b/docs/pages/updates/index.tsx @@ -143,6 +143,7 @@ export default function WhatsNew() { , and subscribe to notifications on GitHub. + Date: Tue, 10 Aug 2021 10:57:27 +1000 Subject: [PATCH 02/28] Fix updates bit on the website (#6287) --- docs/components/Markdown.tsx | 8 ++------ docs/pages/releases/2021-03-22.mdx | 4 ++-- docs/pages/releases/2021-03-23.mdx | 4 ++-- docs/pages/releases/2021-03-30.mdx | 4 ++-- docs/pages/releases/2021-04-06.mdx | 4 ++-- docs/pages/releases/2021-04-20.mdx | 4 ++-- docs/pages/releases/2021-05-03.mdx | 4 ++-- docs/pages/releases/2021-05-05.mdx | 4 ++-- docs/pages/releases/2021-05-11.mdx | 4 ++-- docs/pages/releases/2021-05-17.mdx | 4 ++-- docs/pages/releases/2021-05-19.mdx | 4 ++-- docs/pages/releases/2021-06-02.mdx | 4 ++-- docs/pages/releases/2021-06-15.mdx | 4 ++-- docs/pages/releases/2021-06-28.mdx | 4 ++-- docs/pages/releases/2021-06-29.mdx | 4 ++-- docs/pages/releases/2021-06-30.mdx | 4 ++-- docs/pages/releases/2021-07-13.mdx | 4 ++-- docs/pages/releases/2021-07-29.mdx | 4 ++-- docs/pages/releases/index.mdx | 4 ++-- docs/pages/updates/index.tsx | 8 ++++---- docs/pages/updates/prisma-day-2021.mdx | 4 ++-- docs/pages/updates/roadmap.tsx | 4 ++-- docs/pages/updates/whats-new-in-v6.mdx | 4 ++-- 23 files changed, 48 insertions(+), 52 deletions(-) diff --git a/docs/components/Markdown.tsx b/docs/components/Markdown.tsx index 43b2e9109f8..02b8aa3f663 100644 --- a/docs/components/Markdown.tsx +++ b/docs/components/Markdown.tsx @@ -45,13 +45,9 @@ export function Markdown({ ); } -export async function getServerSideProps() { +export async function getStaticProps() { const { readdirSync } = require('fs'); - const { normalize } = require('path'); - const dir = __dirname.endsWith('/server') - ? normalize(`${__dirname}/../../pages/releases`) - : normalize(`${__dirname}/../../../pages/releases`); - + const dir = __dirname.replace(/docs.+$/, 'docs/pages/releases'); const releases = (readdirSync(dir, 'utf8') as Array) .filter(name => !name.startsWith('.') && !name.startsWith('index')) .map(name => name.replace('.mdx', '')) diff --git a/docs/pages/releases/2021-03-22.mdx b/docs/pages/releases/2021-03-22.mdx index f9e9636ac18..61397a86cbb 100644 --- a/docs/pages/releases/2021-03-22.mdx +++ b/docs/pages/releases/2021-03-22.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 22nd March 2021 @@ -50,4 +50,4 @@ Now you can include negative values for `float`, `decimal` and `integer` fields. You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-03-22) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-03-23.mdx b/docs/pages/releases/2021-03-23.mdx index cb792f3bdcb..4138c78c47c 100644 --- a/docs/pages/releases/2021-03-23.mdx +++ b/docs/pages/releases/2021-03-23.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 23rd March 2021 @@ -23,4 +23,4 @@ Mitchell [solved a bug](https://github.com/keystonejs/keystone/pull/5168) where You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-03-23) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-03-30.mdx b/docs/pages/releases/2021-03-30.mdx index 082c8d8a92c..67bc3a29477 100644 --- a/docs/pages/releases/2021-03-30.mdx +++ b/docs/pages/releases/2021-03-30.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 30th March 2021 @@ -44,4 +44,4 @@ We fixed a bug that existed in `updateMany` on lists with declarative access con You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-03-30) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-04-06.mdx b/docs/pages/releases/2021-04-06.mdx index 3255807b507..659f1428147 100644 --- a/docs/pages/releases/2021-04-06.mdx +++ b/docs/pages/releases/2021-04-06.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 6th April 2021 @@ -38,4 +38,4 @@ Pagination in the Admin UI has fresh styles and is easier to use. You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-04-06) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-04-20.mdx b/docs/pages/releases/2021-04-20.mdx index 728d313d133..3dae3c770ff 100644 --- a/docs/pages/releases/2021-04-20.mdx +++ b/docs/pages/releases/2021-04-20.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; # Release: 20th April 2021 @@ -60,4 +60,4 @@ const [post] = await context.db.lists.Post.findMany({ You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-04-20) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-05-03.mdx b/docs/pages/releases/2021-05-03.mdx index 8c8586ebe81..d701f6a165a 100644 --- a/docs/pages/releases/2021-05-03.mdx +++ b/docs/pages/releases/2021-05-03.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 3rd May 2021 @@ -27,4 +27,4 @@ This release involved a bunch of busywork behind the scenes in Keystone 6. Strip You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-05-03) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-05-05.mdx b/docs/pages/releases/2021-05-05.mdx index 35fcb410e35..13b7458d10e 100644 --- a/docs/pages/releases/2021-05-05.mdx +++ b/docs/pages/releases/2021-05-05.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 5th May 2021 @@ -20,4 +20,4 @@ If you look closely you’ll see the core team working on example projects to sh You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-05-05) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-05-11.mdx b/docs/pages/releases/2021-05-11.mdx index 44463a14382..c1d075c405c 100644 --- a/docs/pages/releases/2021-05-11.mdx +++ b/docs/pages/releases/2021-05-11.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 11th May 2021 @@ -21,4 +21,4 @@ A bunch of admin UI tweaks in this release, among other minor fixes. We also hav You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-05-11) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-05-17.mdx b/docs/pages/releases/2021-05-17.mdx index d64a04de378..a3cf8d2f729 100644 --- a/docs/pages/releases/2021-05-17.mdx +++ b/docs/pages/releases/2021-05-17.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 17th May 2021 @@ -24,4 +24,4 @@ Thanks to @cameronbraid for spotting a [session issue](https://github.com/keysto You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-05-17) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-05-19.mdx b/docs/pages/releases/2021-05-19.mdx index 17fb68d746c..86e646e5ad7 100644 --- a/docs/pages/releases/2021-05-19.mdx +++ b/docs/pages/releases/2021-05-19.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 19th May 2021 @@ -35,4 +35,4 @@ If you were directly importing from `@keystone-next/admin-ui` you can now import You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-05-19) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-06-02.mdx b/docs/pages/releases/2021-06-02.mdx index e5d3e8e1dbb..42dc7d7f6cf 100644 --- a/docs/pages/releases/2021-06-02.mdx +++ b/docs/pages/releases/2021-06-02.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 2nd June 2021 @@ -184,4 +184,4 @@ We've updated our Prisma dependency from `2.22.1` to `2.24.0`! Check out the [Pr You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-06-02) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-06-15.mdx b/docs/pages/releases/2021-06-15.mdx index 6e04474584d..cd3639e4206 100644 --- a/docs/pages/releases/2021-06-15.mdx +++ b/docs/pages/releases/2021-06-15.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 15th June 2021 @@ -93,4 +93,4 @@ We've updated our Prisma dependencies to `2.24.1`, check out the [Prisma release You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-06-15) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-06-28.mdx b/docs/pages/releases/2021-06-28.mdx index a1f85f4fe5a..4a6d7ccebc7 100644 --- a/docs/pages/releases/2021-06-28.mdx +++ b/docs/pages/releases/2021-06-28.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 28th June 2021 @@ -64,4 +64,4 @@ We've updated our Prisma dependencies to `2.25.0`, check out the [Prisma release You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-06-28) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-06-29.mdx b/docs/pages/releases/2021-06-29.mdx index a140e784a92..399ce1a5aab 100644 --- a/docs/pages/releases/2021-06-29.mdx +++ b/docs/pages/releases/2021-06-29.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 29th June 2021 @@ -64,4 +64,4 @@ config({ You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-06-29) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-06-30.mdx b/docs/pages/releases/2021-06-30.mdx index b8bf531d523..abfedc3d0e2 100644 --- a/docs/pages/releases/2021-06-30.mdx +++ b/docs/pages/releases/2021-06-30.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 30th June 2021 @@ -23,4 +23,4 @@ We've discovered an issue where `cloudinaryImage` and `relationship` fields were You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-06-30) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-07-13.mdx b/docs/pages/releases/2021-07-13.mdx index a5c1569c921..5e84262da4f 100644 --- a/docs/pages/releases/2021-07-13.mdx +++ b/docs/pages/releases/2021-07-13.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 13th July 2021 @@ -69,4 +69,4 @@ Our `packages-next` folder has moved over to `packages` as part of our push to a You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-07-13) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/2021-07-29.mdx b/docs/pages/releases/2021-07-29.mdx index b4a23c1902e..007b1aa015c 100644 --- a/docs/pages/releases/2021-07-29.mdx +++ b/docs/pages/releases/2021-07-29.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; # Release: 29th July 2021 @@ -97,4 +97,4 @@ Updated Prisma dependencies to `2.27.0`, check out the [Prisma releases page](ht You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-07-29) on GitHub. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/releases/index.mdx b/docs/pages/releases/index.mdx index 67512ebed1e..538727144f1 100644 --- a/docs/pages/releases/index.mdx +++ b/docs/pages/releases/index.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { Emoji } from '../../components/primitives/Emoji'; import { Status } from '../../components/primitives/Status'; @@ -79,4 +79,4 @@ Prisma migrations , Noteworthy bug-squashing < ?> 🔎 You can also find all the **Keystone 6** releases on [GitHub](https://github.com/keystonejs/keystone/releases)! export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/updates/index.tsx b/docs/pages/updates/index.tsx index b15ff603327..a26302df2fd 100644 --- a/docs/pages/updates/index.tsx +++ b/docs/pages/updates/index.tsx @@ -3,7 +3,7 @@ import { HTMLAttributes, ReactNode } from 'react'; import { jsx } from '@emotion/react'; import Link from 'next/link'; -import { getServerSideProps } from '../../components/Markdown'; +import { getStaticProps } from '../../components/Markdown'; import { InlineCode } from '../../components/primitives/Code'; import { Button } from '../../components/primitives/Button'; import { Alert } from '../../components/primitives/Alert'; @@ -190,7 +190,7 @@ export default function WhatsNew() { frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen - > + /> Follow along in with the repo @@ -247,7 +247,7 @@ export default function WhatsNew() { frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen - > + /> @@ -397,4 +397,4 @@ export default function WhatsNew() { ); } -export { getServerSideProps }; +export { getStaticProps }; diff --git a/docs/pages/updates/prisma-day-2021.mdx b/docs/pages/updates/prisma-day-2021.mdx index 38cb1c75b41..56b736058c4 100644 --- a/docs/pages/updates/prisma-day-2021.mdx +++ b/docs/pages/updates/prisma-day-2021.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; # Prisma Day 2021 Talk @@ -110,4 +110,4 @@ So this is our take on what that looks like. I'd love you to check it out, let u Thanks for having me. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } diff --git a/docs/pages/updates/roadmap.tsx b/docs/pages/updates/roadmap.tsx index b0831f18c8e..6a66741b49b 100644 --- a/docs/pages/updates/roadmap.tsx +++ b/docs/pages/updates/roadmap.tsx @@ -2,7 +2,7 @@ import { ComponentProps, Fragment, ReactNode } from 'react'; import { jsx } from '@emotion/react'; -import { getServerSideProps } from '../../components/Markdown'; +import { getStaticProps } from '../../components/Markdown'; import { InlineCode } from '../../components/primitives/Code'; import { Button } from '../../components/primitives/Button'; import { Highlight } from '../../components/primitives/Highlight'; @@ -416,4 +416,4 @@ export default function Roadmap() { ); } -export { getServerSideProps }; +export { getStaticProps }; diff --git a/docs/pages/updates/whats-new-in-v6.mdx b/docs/pages/updates/whats-new-in-v6.mdx index f790b456ba9..87c87363cc1 100644 --- a/docs/pages/updates/whats-new-in-v6.mdx +++ b/docs/pages/updates/whats-new-in-v6.mdx @@ -1,4 +1,4 @@ -import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Markdown, getStaticProps } from '../../components/Markdown'; import { SubscribeForm } from '../../components/SubscribeForm'; import { Badge } from '../../components/primitives/Badge'; import { Emoji } from '../../components/primitives/Emoji'; @@ -58,4 +58,4 @@ We also have new reference examples for how to use advanced features, like imple roles-based access control system. export default ({ children, ...props }) => {children}; -export { getServerSideProps } +export { getStaticProps } From 4746a229bf9262269707406df32ecf30bd744ae2 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Tue, 10 Aug 2021 11:31:48 +1000 Subject: [PATCH 03/28] Content management update (#6282) * Add Wes Testimonial. Cleanup. * Typos --- docs/pages/for-content-management.tsx | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/pages/for-content-management.tsx b/docs/pages/for-content-management.tsx index f519c175973..4c86ed3b8a8 100644 --- a/docs/pages/for-content-management.tsx +++ b/docs/pages/for-content-management.tsx @@ -110,6 +110,16 @@ export default function ForOrganisations() { /> + + I love how Keystone’s access control lets me declare every single Create, Read, Update, + and Delete operation at both the model and field level. + It’s my favorite way of implementing Auth. +
@@ -261,7 +271,12 @@ export default function ForOrganisations() { - + The new @KeystoneJS rich text editor has incredible inline React component support, including editing props and everything! @@ -309,9 +324,6 @@ export default function ForOrganisations() { - {/* - Try the example → - */} Relationships guide → From 512b8deb5af77f5301506ac93bc39486285a9b63 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Tue, 10 Aug 2021 11:36:05 +1000 Subject: [PATCH 04/28] Fixed whitespace (#6283) --- docs/components/content/MWrapper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/components/content/MWrapper.tsx b/docs/components/content/MWrapper.tsx index 62aa670d77c..b620956764b 100644 --- a/docs/components/content/MWrapper.tsx +++ b/docs/components/content/MWrapper.tsx @@ -14,7 +14,7 @@ export function MWrapper({ as: Tag = 'div', ...props }: MWrapperProps) { return ( Date: Tue, 10 Aug 2021 11:44:20 +1000 Subject: [PATCH 05/28] Update Header.tsx (#6289) --- docs/components/Header.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/components/Header.tsx b/docs/components/Header.tsx index ea1adb65830..23839cc99d7 100644 --- a/docs/components/Header.tsx +++ b/docs/components/Header.tsx @@ -128,6 +128,9 @@ export function Header() { apiKey: '211e94c001e6b4c6744ae72fb252eaba', indexName: 'keystonejs', inputSelector: '#search-field', + algoliaOptions: { + facetFilters: ['tags:stable'], + }, }); } else if (searchAttempt >= 10) { // @ts-ignore From 3d4c2685c6a1c42246b6c7881e7fc46eae520c20 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Tue, 10 Aug 2021 12:00:17 +1000 Subject: [PATCH 06/28] Add CTA block for Web Box eCommerce Course (#6273) --- docs/components/content/AdvancedReactCta.tsx | 116 +++++++++++++++++++ docs/pages/why-keystone.tsx | 63 ++++++---- docs/public/assets/wesbos-cta.jpg | Bin 0 -> 106606 bytes 3 files changed, 159 insertions(+), 20 deletions(-) create mode 100644 docs/components/content/AdvancedReactCta.tsx create mode 100644 docs/public/assets/wesbos-cta.jpg diff --git a/docs/components/content/AdvancedReactCta.tsx b/docs/components/content/AdvancedReactCta.tsx new file mode 100644 index 00000000000..0acce3af78a --- /dev/null +++ b/docs/components/content/AdvancedReactCta.tsx @@ -0,0 +1,116 @@ +/** @jsx jsx */ +import type { HTMLAttributes } from 'react'; +import { jsx } from '@emotion/react'; +import Image from 'next/image'; + +import wesBosCta from '../../public/assets/wesbos-cta.jpg'; + +import { useMediaQuery } from '../../lib/media'; +import { Button } from '../primitives/Button'; +import { Type } from '../primitives/Type'; +import { ArrowR } from '../icons/ArrowR'; +import { Tick } from '../icons/Tick'; +import { Section } from './Section'; + +export function AdvancedReactCta(props: HTMLAttributes) { + const mq = useMediaQuery(); + + return ( +
+
div': { + display: 'inline-grid !important', + justifyContent: 'center', + alignSelf: 'center', + }, + }} + > + Wes Bos Avatar +
+
+ + Learn Keystone for eCommerce with Wes Bos + + + Master eCommerce with Keystone, React, & GraphQL. Join Wes as he teaches you how to build + a full-stack online store with of today's best JavaScript technology. + +
    +
  • + + + 11 modules + +
  • +
  • + + + 70 videos + +
  • +
  • + + + 28,000 students + +
  • +
+
+ +
+
+
+ ); +} diff --git a/docs/pages/why-keystone.tsx b/docs/pages/why-keystone.tsx index bdba1d054b0..f4e07441955 100644 --- a/docs/pages/why-keystone.tsx +++ b/docs/pages/why-keystone.tsx @@ -14,6 +14,7 @@ import { Automated } from '../components/icons/Automated'; import { Migration } from '../components/icons/Migration'; import { Section } from '../components/content/Section'; import { Button } from '../components/primitives/Button'; +import { AdvancedReactCta } from '../components/content/AdvancedReactCta'; import { EndCta } from '../components/content/EndCta'; import { Emoji } from '../components/primitives/Emoji'; import { Updates } from '../components/icons/Updates'; @@ -89,7 +90,7 @@ export default function WhyKeystonePage() { margin: '1rem 0', }} > - TODO + Depiction of Keystone’s Admin UI
    -
  • - - eCommerce - - - Keystone gives you the power and control you need to build a complete backend for - eCommerce, while making it easy to integrate platforms like Shopify and Stripe to - get things done. API first, make Keystone a key player in your eCommerce content - mesh. - +
+ + +
+
+ + eCommerce + + + Keystone gives you the power and control you need to build a complete backend for + eCommerce, while making it easy to integrate platforms like Shopify and Stripe to get + things done. API first, make Keystone a key player in your eCommerce content mesh. + + + Try the eCommerce example → + +
+
+ + I use Keystone in my{' '} - Try the eCommerce example → - - - + Advanced React + {' '} + course because it’s super quick to get my content types up and running, add custom + server-side cart & checkout logic, and the fine grain access control is just + fantastic! + +
- {/*
TODO: WES BOS COURSE LINK...
*/} +
TgARNHQ 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∨&Dm;y&QRy}t{Ck*zZXcA$9|Kfz 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 z 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 L5RW7M+kgMr>Dx4L literal 0 HcmV?d00001 From f97a9ca31aef2ff20ac4b255cf4d21803a4affd5 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Tue, 10 Aug 2021 12:09:25 +1000 Subject: [PATCH 07/28] Content update (#6290) --- docs/components/Page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/components/Page.tsx b/docs/components/Page.tsx index c56ed0c3796..9c896ac8f5c 100644 --- a/docs/components/Page.tsx +++ b/docs/components/Page.tsx @@ -18,8 +18,8 @@ import { Footer } from './Footer'; function Announcement() { return ( - Keystone 6 is in Community Preview! What does that mean? see our{' '} - Roadmap. For Keystone 5 docs, visit{' '} + Keystone 6 is in Community Preview! For Keystone 5 docs + visit{' '} v5.keystonejs.com From 2963ac79345386637e41821700ce0185d42cbc79 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Tue, 10 Aug 2021 13:20:00 +1000 Subject: [PATCH 08/28] Style fixes (#6291) * Fix styles: CommunityCta * Fix Styles: Homepage * Stylefix: Wes CTA block * Style fixes: Why Keystone * Style fixes: Content Management * Style fixes: Developers * Style fixes: Organisations * Style fixes: Prose lite Reverts text color to default `--text` var per Figma * Style fixes: Docs Home * Stye fixes: examples CTA --- docs/components/content/AdvancedReactCta.tsx | 6 +- docs/components/content/CommunityCta.tsx | 10 +- docs/components/docs/ExamplesList.tsx | 3 +- docs/components/docs/GitHubExamplesCTA.tsx | 2 + docs/lib/prose-lite.ts | 1 - docs/pages/for-content-management.tsx | 36 ++-- docs/pages/for-developers.tsx | 48 +++-- docs/pages/for-organisations.tsx | 25 ++- docs/pages/index.tsx | 65 ++++--- docs/pages/why-keystone.tsx | 192 ++++++++++++------- 10 files changed, 240 insertions(+), 148 deletions(-) diff --git a/docs/components/content/AdvancedReactCta.tsx b/docs/components/content/AdvancedReactCta.tsx index 0acce3af78a..96071599b91 100644 --- a/docs/components/content/AdvancedReactCta.tsx +++ b/docs/components/content/AdvancedReactCta.tsx @@ -81,19 +81,19 @@ export function AdvancedReactCta(props: HTMLAttributes) { >
  • - + 11 modules
  • - + 70 videos
  • - + 28,000 students
  • diff --git a/docs/components/content/CommunityCta.tsx b/docs/components/content/CommunityCta.tsx index a710fe40464..dfb2001721c 100644 --- a/docs/components/content/CommunityCta.tsx +++ b/docs/components/content/CommunityCta.tsx @@ -54,7 +54,7 @@ export function CommunityCta(props: HTMLAttributes) { Learn with others in a supportive community - + Share your work and get the help you need in the Keystone community Slack: an inclusive space to share ideas and explore what‘s possible. @@ -88,11 +88,15 @@ export function CommunityCta(props: HTMLAttributes) { >
  • - 2000+ members + + 2000+ members +
  • - Personalised support + + Personalised support +
  • diff --git a/docs/components/docs/ExamplesList.tsx b/docs/components/docs/ExamplesList.tsx index 00c7cb14ec8..ec56892d60e 100644 --- a/docs/components/docs/ExamplesList.tsx +++ b/docs/components/docs/ExamplesList.tsx @@ -124,6 +124,7 @@ export function Examples() { users rate items on a 5-star scale. Builds on the Blog starter project. diff --git a/docs/components/docs/GitHubExamplesCTA.tsx b/docs/components/docs/GitHubExamplesCTA.tsx index 28401fa9fdb..9f1d6c700ad 100644 --- a/docs/components/docs/GitHubExamplesCTA.tsx +++ b/docs/components/docs/GitHubExamplesCTA.tsx @@ -18,6 +18,8 @@ export function GitHubExamplesCTA() { - - Read the guide → - + + + Read the guide → + + Working with @KeystoneJS is such a pleasant experience. After hand rolling a few GraphQL @@ -188,9 +190,11 @@ export default function ForDevelopers() { configurable CMS built in. Program with JavaScript, store changes in version control, and integrate with your preferred CI tools.
    - - Keystone for content management → - + + + Keystone for content management → + +
    - - Access control API → - + + + Access control API → + +
  • @@ -305,9 +313,11 @@ export default function ForDevelopers() { One to one. One to many. Many to many. Self-referential. It's all there. - - Relationships guide → - + + + Relationships guide → + +
  • @@ -318,9 +328,11 @@ export default function ForDevelopers() { Kick start new projects and try examples on for size from the comfort of your terminal. - - CLI guide → - + + + CLI guide → + +
  • diff --git a/docs/pages/for-organisations.tsx b/docs/pages/for-organisations.tsx index 8646bd10488..3005c719989 100644 --- a/docs/pages/for-organisations.tsx +++ b/docs/pages/for-organisations.tsx @@ -109,12 +109,16 @@ export default function ForOrganisations() {
  • - - Keystone for developers → - - - Keystone for content management → - + + + Keystone for developers → + + + + + Keystone for content management → + +
    Working with @KeystoneJS is such a pleasant experience. After hand rolling a few GraphQL @@ -210,6 +216,7 @@ export default function ForOrganisations() { Batteries included. No limitations. - + Ship a backend easily without surrendering control.
    Keystone has all you need to start fast and scale on your terms. @@ -221,61 +221,61 @@ export default function IndexPage() { >
  • - + All the field types
  • - + Access Control
  • - + Session Management
  • - + Custom Schema
  • - + Database Migrations
  • - + TypeScript Support
  • - + Powerful Filters
  • - + Relational Data
  • - + Automated CRUD
  • - + Event Hooks
  • @@ -289,7 +289,7 @@ export default function IndexPage() { How it works - + Enable a content culture that’s productive, collaborative, and fun.
    Open, flexible, and natural. A tool your team can grow with. @@ -377,7 +377,7 @@ export default function IndexPage() { Rapidly spec your backend with all the primitive and advanced field types you need. - + Add logic, access control, and custom queries & mutations to create an API that's unique to your app. @@ -662,6 +662,7 @@ export const lists = createSchema({ Unify your team dynamic - + Enable a content culture that’s productive, collaborative, and fun. Open, flexible, and natural. A tool your team can grow with. @@ -784,40 +791,46 @@ export const lists = createSchema({ Developers - + Backend superpowers for frontend devs. Built the way you’d want it made, Keystone is at home with the tools you know and love. - - Keystone for Developers → - + + + Keystone for Developers → + +
  • Content people - + Get the fields, forms, and workflows you need to do your best work. Tell the full story with a rich text editor that can be configured for any content need. - - Keystone for Content Management → - + + + Keystone for Content Management → + +
  • Organisations - + Realise your vision with a backend you can shape to fit your logic. Own your data, cultivate a productive content culture, send your message anywhere, and scale on your terms. - - Keystone for Organisations → - + + + Keystone for Organisations → + +
  • diff --git a/docs/pages/why-keystone.tsx b/docs/pages/why-keystone.tsx index f4e07441955..0df59cc52c1 100644 --- a/docs/pages/why-keystone.tsx +++ b/docs/pages/why-keystone.tsx @@ -173,7 +173,13 @@ export default function WhyKeystonePage() { What’s in the box? - + Everything you need to start fast and scale sustainably. We’ve done the heavy lifting so you can work on what matters without getting boxed in. @@ -200,9 +206,11 @@ export default function WhyKeystonePage() { Out of the box data ops for every field type. A powerful GraphQL API from day one. - - Access control API → - + + + Access control API → + +
  • @@ -213,13 +221,15 @@ export default function WhyKeystonePage() { Extend the CRUD API for more control over what you do. Customise it to your frontend needs. - - Try the example → - + + + Try the example → + +
  • @@ -230,9 +240,11 @@ export default function WhyKeystonePage() { Highly configurable. Design systems friendly. BYO custom React components. Structured JSON output. - - Try the editor → - + + + Try the editor → + +
  • @@ -242,9 +254,11 @@ export default function WhyKeystonePage() { Start and end sessions from the GraphQL API. Secure your data using access control. - - Session API → - + + + Session API → + +
  • @@ -265,9 +279,11 @@ export default function WhyKeystonePage() { Build your own roles-based access controls. No limits on the amount and kind of roles you can configure. - - Access Control API → - + + + Access Control API → + +
  • @@ -277,9 +293,11 @@ export default function WhyKeystonePage() { One to one. One to many. Many to many. Self referential. It’s all there. - - Relationships guide → - + + + Relationships guide → + +
  • @@ -289,9 +307,11 @@ export default function WhyKeystonePage() { Find what you need when you need it with intuitive filters. - - Query Filters guide → - + + + Query Filters guide → + +
  • @@ -311,9 +331,11 @@ export default function WhyKeystonePage() { An editing environment you can shape to the needs of any project. No boilerplate. Everything as you make it. - - Fields API → - + + + Fields API → + +
  • @@ -324,9 +346,11 @@ export default function WhyKeystonePage() { Put custom logic in your data ops. Modify data, trigger events, validate inputs – it’s up to you. - - Hooks guide → - + + + Hooks guide → + +
  • @@ -337,9 +361,11 @@ export default function WhyKeystonePage() { Kickstart new projects and try examples on for size from the comfort of your terminal. - - CLI guide → - + + + CLI guide → + +
  • @@ -362,7 +388,13 @@ export default function WhyKeystonePage() { What will you build? - + Naturally, it’s up to you. Here’s some things we've used Keystone for:
      Apps - + A programmable backend you can rely on for Web and Native apps of all sizes. Start with Keystone’s built-in features then add your own, and integrate 3rd-party systems or microservices. - + You can control data input exclusively from your frontend, or use Keystone’s intuitive and customisable CMS when you need it. - - Try the Task Manager example → - + + + Try the Task Manager example → + +
    • Websites - + From simple blog, to complex multi-brand site networks, Keystone’s the backend fit for modern web experiences. It’s a CMS that ships with no hard opinions, so you can build the fields and types you actually need. And a WYSIWIG you can plug custom components into, that outputs structured JSON. - + Give your content people the tools they need to do their best work. - - Try the Blog example → - + + + Try the Blog example → + +
    @@ -437,18 +473,20 @@ export default function WhyKeystonePage() { eCommerce - + Keystone gives you the power and control you need to build a complete backend for eCommerce, while making it easy to integrate platforms like Shopify and Stripe to get things done. API first, make Keystone a key player in your eCommerce content mesh. - - Try the eCommerce example → - + + + Try the eCommerce example → + +
    @@ -530,40 +568,46 @@ export default function WhyKeystonePage() { Developers - + Backend superpowers for frontend devs. Built the way you’d want it made, Keystone is at home with the tools you know and love. - - Keystone for Developers → - + + + Keystone for Developers → + +
  • Content people - + Get the fields, forms, and workflows you need to do your best work. Tell the full story with a rich text editor that can be configured for any content need. - - Keystone for Content Management → - + + + Keystone for Content Management → + +
  • Organisations - + Realise your vision with a backend you can shape to fit your logic. Own your data, cultivate a productive content culture, send your message anywhere, and scale on your terms. - - Keystone for Organisations → - + + + Keystone for Organisations → + +
  • From d534409e32759439beae1a654dc930702d41f3df Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Thu, 12 Aug 2021 08:11:32 +1000 Subject: [PATCH 09/28] Added new content to /updates (#6300) --- docs/pages/updates/index.tsx | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/pages/updates/index.tsx b/docs/pages/updates/index.tsx index a26302df2fd..92518a0fdd8 100644 --- a/docs/pages/updates/index.tsx +++ b/docs/pages/updates/index.tsx @@ -165,7 +165,32 @@ export default function WhatsNew() { gap: 0, })} > - + + + We're opening Admin UI up to support a more personal content experience. Now you can: + + To deliver a more productive editor experience that's aligned with the needs and brand of + your organisation. + + + + We've added an optional /_healthcheck endpoint to Keystone's + express server. Use it to ensure your Keystone instance is up and running with website + monitoring solutions. + +
    Date: Fri, 13 Aug 2021 09:28:37 +1000 Subject: [PATCH 10/28] Update fields.mdx (#6304) --- docs/pages/docs/apis/fields.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/docs/apis/fields.mdx b/docs/pages/docs/apis/fields.mdx index 38b26c6e6a4..88d5c0ef067 100644 --- a/docs/pages/docs/apis/fields.mdx +++ b/docs/pages/docs/apis/fields.mdx @@ -458,7 +458,7 @@ Read our [relationships guide](../guides/relationships) for details on Keystone - `ref` (required): A string of the form `` or `.`. - `many` (default: `false`): Configures the cardinality of the relationship. -- `defaultValue` (default: `undefined`): Can be either a relationship input value or an async function which takes an argument `({ context, originalInput })` and returns a relationship input value. +- `defaultValue` (default: `undefined`): Can be either a relationship input value `{ connect: { id: ID } }` or an async function which takes an argument `({ context, originalInput })` and returns a relationship input value. This value will be used for the field when creating items if no explicit value is set. `context` is a [`KeystoneContext`](./context) object. `originalInput` is an object containing the data passed in to the `create` mutation. From e29b5e4f79eaaad1bb04cbb5ceee40fccdb79023 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Mon, 16 Aug 2021 09:30:43 +1000 Subject: [PATCH 11/28] Fixed typo (#6322) --- docs/pages/docs/guides/hooks.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/docs/guides/hooks.mdx b/docs/pages/docs/guides/hooks.mdx index f343cbd2cfb..404eeaf1eec 100644 --- a/docs/pages/docs/guides/hooks.mdx +++ b/docs/pages/docs/guides/hooks.mdx @@ -157,7 +157,7 @@ export default config({ }, hooks: { afterChange: ({ operation, updatedItem }) => { - if (operation === 'create) { + if (operation === 'create') { sendWelcomeEmail(updatedItem.name, updatedItem.email); } } From 64e6c1daf9251cc2f2f4da26007b2805d139ed50 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Mon, 16 Aug 2021 11:10:45 +1000 Subject: [PATCH 12/28] Added top margin to docs page component (#6301) --- docs/components/Page.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/components/Page.tsx b/docs/components/Page.tsx index 9c896ac8f5c..85d0848561b 100644 --- a/docs/components/Page.tsx +++ b/docs/components/Page.tsx @@ -100,6 +100,7 @@ export function DocsPage({ Date: Mon, 16 Aug 2021 12:39:49 +1000 Subject: [PATCH 13/28] Added styles to table (#6315) * Added styles to table * Update prose-lite.ts * Update prose-lite.ts * Update prose-lite.ts Co-authored-by: Thomas Walker --- docs/lib/prose-lite.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/lib/prose-lite.ts b/docs/lib/prose-lite.ts index bbe636e4cd3..d96b0595a7c 100644 --- a/docs/lib/prose-lite.ts +++ b/docs/lib/prose-lite.ts @@ -153,6 +153,9 @@ export const proseStyles = { marginBottom: '2rem', fontSize: 'var(--font-xsmall)', lineHeight: 1.7142857, + maxWidth: '100%', + overflowX: 'auto' as const, + display: 'block', }, thead: { color: 'var(--text)', @@ -173,6 +176,9 @@ export const proseStyles = { paddingRight: '0.5714285714em', paddingBottom: '0.5714285714em', paddingLeft: '0.5714285714em', + '> code': { + whiteSpace: 'nowrap' as const, + }, }, fontSize: 'var(--font-small)', lineHeight: 1.75, From 4427bbccc193740cfb386a36cf887c527634a398 Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Mon, 16 Aug 2021 15:48:11 +1000 Subject: [PATCH 14/28] Un-nest tags. (#6327) --- docs/components/Page.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/components/Page.tsx b/docs/components/Page.tsx index 85d0848561b..b0fe1ae53b8 100644 --- a/docs/components/Page.tsx +++ b/docs/components/Page.tsx @@ -41,7 +41,7 @@ function OpenGraph({ ogImage = `${siteUrl}/og-image-landscape.png`; } return ( - + {title} @@ -53,7 +53,7 @@ function OpenGraph({ - + ); } @@ -85,9 +85,7 @@ export function DocsPage({ return ( - - - +
    - - - +
    Date: Mon, 16 Aug 2021 17:13:57 +1000 Subject: [PATCH 15/28] Fixed link value (#6328) --- docs/pages/docs/guides/hooks.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/docs/guides/hooks.mdx b/docs/pages/docs/guides/hooks.mdx index 404eeaf1eec..b76871e50a1 100644 --- a/docs/pages/docs/guides/hooks.mdx +++ b/docs/pages/docs/guides/hooks.mdx @@ -220,6 +220,6 @@ export default config({ }); ``` -See the [fields API](../apis/hooks) for the details of all the arguments available for all the different hook functions. +See the [Hooks API](../apis/hooks) for the details of all the arguments available for all the different hook functions. export default ({ children }) => {children}; From 36f6550dba2de045fec4d0433d401dcfcd9c8611 Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Tue, 17 Aug 2021 11:11:13 +1000 Subject: [PATCH 16/28] Update `website_live` (#6336) * Update patch dependencies (patch) (#6253) * Update search config to match new DocSearch config (#6255) * Update dependency/apollo client (#6259) * update apolloclient dependency to latest * changeset * Update dependency @graphql-tools/merge to v7 (#6246) Co-authored-by: Renovate Bot * Rename first to take (#6266) * Expose stacktraces from exceptions thrown in before/after hooks. (#6263) * 6268/next typescript config error (#6269) * add typescript ignoreBuildErrors flag * changeset * Updated /updates with new things (#6272) * Check exceptions returned from GraphQL (#6271) * Lock file maintenance (#6277) * Allow bearer auth in header using sessionToken (#6276) * Use Next 11 in the website (#6256) * 6223/custom pages guide improvements (#6264) * update example to include helper components * update docs and examples * update docs and examples * update to docs * more updates * changeset * correct incorrect props in README.md * update smoke test * update images * update example * update tests * remove next dep from package.json * updates * re-add schema.prisma for admin-ui-navigation example * Update docs/pages/docs/guides/custom-admin-ui-pages.mdx Co-authored-by: Tim Leslie * Update docs/pages/docs/guides/custom-admin-ui-pages.mdx Co-authored-by: Tim Leslie * Update docs/pages/docs/guides/custom-admin-ui-pages.mdx Co-authored-by: Tim Leslie * Update docs/pages/docs/guides/custom-admin-ui-pages.mdx Co-authored-by: Tim Leslie * Update docs/pages/docs/guides/custom-admin-ui-pages.mdx Co-authored-by: Tim Leslie * Update docs/pages/docs/guides/custom-admin-ui-pages.mdx Co-authored-by: Tim Leslie * Update docs/pages/docs/guides/custom-admin-ui-pages.mdx Co-authored-by: Tim Leslie * Update docs/pages/docs/guides/custom-admin-ui-pages.mdx Co-authored-by: Tim Leslie * Apply suggestions from code review Co-authored-by: Tim Leslie * update docs * revert change to next-env.d.ts * Apply suggestions from code review Co-authored-by: Tim Leslie Co-authored-by: Tim Leslie * Update dependency eslint-plugin-import to ^2.24.0 (#6285) Co-authored-by: Renovate Bot * Fix updates bit on the website (#6288) * Include stacktrace flag (#6267) * Remove `gqlType` option on `autoIncrement` field type (#6280) * Use playwright install-deps (#6294) * Update patch dependencies (patch) (#6284) * Update prisma monorepo to v2.29.0 (minor) (#6292) * Nested filters (#6095) * GraphQL API docs changes (#6297) * Update dependency @types/jest to v27 (#6293) * Ignore generated files in prisma-utils (#6305) * Move import of mergeSchemas (#6310) * Update resolveInput error handling (#6316) * Upgrade Next to 11.1.0 for the website (#6311) * Update @graphql-ts/schema (#6312) * Lock file maintenance (#6320) * Split create/update field input resolvers for relationship fields (#6317) * Expand editable area (#6318) * POC - Expand editable area * Change things * Create cyan-rabbits-look.md Co-authored-by: mitchellhamilton * Fixed import url on CustomNavigation component (#6308) Co-authored-by: Tim Leslie * Update text filter API table (#6330) * Update the tags in the docs navigation (#6329) * Update patch dependencies (patch) (#6331) * Add a GraphQL API upgrade guide (#6281) * Fix issue with VisuallyHidden checkbox interactions in table (#6334) * resolve CHROME BUG * changeset * 6261/fix delete alert (#6296) * refactor confirm procedure to only add success toast on success * new deletion logic in Listview * add crud-notifications test project * update deletion solution to be more pragmatic at scale * update bug fix to be more verbose * update schema.graphql * minor updates * fix yarn lint:examples to not break when running more than one test-project * minor updates to copy * remove log * changeset Co-authored-by: Tim Leslie * Version Packages (#6199) Co-authored-by: github-actions[bot] Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Charles Co-authored-by: Renovate Bot Co-authored-by: Mitchell Hamilton Co-authored-by: Tim Leslie Co-authored-by: Ronald Aveling Co-authored-by: Gautam Singh <5769869+gautamsi@users.noreply.github.com> Co-authored-by: Charlie Jonas Co-authored-by: KeystoneJS Release Bot <69774846+keystonejs-release-bot@users.noreply.github.com> Co-authored-by: github-actions[bot] --- .changeset/brown-glasses-complain.md | 7 - .changeset/chilled-grapes-cry.md | 7 - .changeset/cold-cycles-cry.md | 6 - .changeset/dull-humans-yell.md | 8 - .changeset/early-experts-allow.md | 7 - .changeset/eleven-mayflies-kneel.md | 6 - .changeset/empty-baboons-unite.md | 5 - .changeset/fast-guests-tickle.md | 5 - .changeset/fluffy-coins-argue.md | 6 - .changeset/good-cycles-ring.md | 5 - .changeset/happy-seals-cheat.md | 5 - .changeset/khaki-ducks-poke.md | 5 - .changeset/lovely-pears-sort.md | 7 - .changeset/nasty-windows-study.md | 21 - .changeset/olive-maps-serve.md | 5 - .changeset/olive-poems-explode.md | 5 - .changeset/proud-impalas-flash.md | 5 - .changeset/rich-tomatoes-own.md | 22 - .changeset/silly-impalas-type.md | 5 - .changeset/smooth-moose-eat.md | 5 - .changeset/strange-crabs-glow.md | 12 - .changeset/tidy-suits-refuse.md | 5 - .changeset/tough-geese-trade.md | 5 - .changeset/wet-frogs-share.md | 22 - .changeset/wise-pianos-flash.md | 8 - .github/workflows/tests.yml | 8 +- .prettierignore | 1 + design-system/packages/button/package.json | 4 +- design-system/packages/core/package.json | 6 +- design-system/packages/fields/package.json | 4 +- design-system/packages/icons/package.json | 4 +- design-system/packages/loading/package.json | 4 +- design-system/packages/modals/package.json | 4 +- design-system/packages/notice/CHANGELOG.md | 6 + design-system/packages/notice/package.json | 6 +- design-system/packages/options/package.json | 2 +- design-system/packages/pill/package.json | 2 +- design-system/packages/popover/package.json | 6 +- .../packages/segmented-control/CHANGELOG.md | 6 + .../packages/segmented-control/package.json | 6 +- design-system/packages/toast/CHANGELOG.md | 6 + design-system/packages/toast/package.json | 6 +- design-system/packages/tooltip/package.json | 2 +- design-system/website/package.json | 4 +- docs/CHANGELOG.md | 7 + docs/babel.config.js | 1 + docs/components/docs/Navigation.tsx | 32 +- docs/next-env.d.ts | 2 + docs/next.config.js | 6 +- docs/package.json | 17 +- docs/pages/docs/apis/access-control.mdx | 2 +- docs/pages/docs/apis/auth.mdx | 12 +- docs/pages/docs/apis/config.mdx | 11 +- docs/pages/docs/apis/db-items.mdx | 44 +- docs/pages/docs/apis/filters.mdx | 154 +- docs/pages/docs/apis/graphql.mdx | 358 ++-- docs/pages/docs/apis/list-items.mdx | 44 +- docs/pages/docs/apis/schema.mdx | 2 - .../guides/custom-admin-ui-navigation.mdx | 2 +- .../docs/guides/custom-admin-ui-pages.mdx | 159 +- docs/pages/docs/guides/custom-fields.mdx | 17 +- docs/pages/docs/guides/document-fields.mdx | 8 +- docs/pages/docs/guides/filters.mdx | 56 +- docs/pages/docs/guides/hooks.mdx | 4 +- docs/pages/docs/guides/relationships.mdx | 12 +- docs/pages/docs/guides/testing.mdx | 6 +- docs/pages/docs/guides/virtual-fields.mdx | 10 +- docs/pages/ds.tsx | 4 +- docs/pages/index.tsx | 6 +- docs/pages/updates/new-graphql-api.mdx | 405 +++++ .../custom-page-completed.png | Bin 0 -> 38028 bytes .../custom-page-w-page-container.png | Bin 0 -> 30913 bytes .../custom-page-with-styled-header.png | Bin 0 -> 34145 bytes .../custom-admin-ui-pages/header-prop.png | Bin 0 -> 9690 bytes .../simple-custom-page.png | Bin 0 -> 30570 bytes examples-staging/assets-cloud/CHANGELOG.md | 8 + examples-staging/assets-cloud/package.json | 6 +- examples-staging/assets-cloud/schema.graphql | 136 +- examples-staging/assets-local/CHANGELOG.md | 8 + examples-staging/assets-local/package.json | 6 +- examples-staging/assets-local/schema.graphql | 136 +- examples-staging/auth/CHANGELOG.md | 11 + examples-staging/auth/package.json | 8 +- examples-staging/auth/schema.graphql | 175 +- examples-staging/basic/CHANGELOG.md | 13 + examples-staging/basic/package.json | 17 +- examples-staging/basic/schema.graphql | 320 ++-- examples-staging/basic/schema.ts | 2 +- examples-staging/ecommerce/CHANGELOG.md | 15 + examples-staging/ecommerce/access.ts | 10 +- .../ecommerce/mutations/addToCart.ts | 2 +- examples-staging/ecommerce/package.json | 24 +- examples-staging/ecommerce/schema.graphql | 654 +++---- .../ecommerce/tests/mutations.test.ts | 4 +- examples-staging/embedded-nextjs/CHANGELOG.md | 8 + examples-staging/embedded-nextjs/package.json | 6 +- .../embedded-nextjs/schema.graphql | 72 +- .../graphql-api-endpoint/CHANGELOG.md | 12 + .../graphql-api-endpoint/package.json | 12 +- .../graphql-api-endpoint/schema.graphql | 344 ++-- examples-staging/playground/CHANGELOG.md | 8 + examples-staging/playground/package.json | 6 +- examples-staging/playground/schema.graphql | 58 +- examples-staging/roles/CHANGELOG.md | 12 + examples-staging/roles/access.ts | 14 +- examples-staging/roles/package.json | 10 +- examples-staging/roles/schema.graphql | 284 +-- examples-staging/sandbox/CHANGELOG.md | 9 + examples-staging/sandbox/package.json | 8 +- examples-staging/sandbox/schema.graphql | 167 +- examples/blog/CHANGELOG.md | 8 + examples/blog/package.json | 6 +- examples/blog/schema.graphql | 136 +- examples/custom-admin-ui-logo/CHANGELOG.md | 9 + examples/custom-admin-ui-logo/package.json | 8 +- examples/custom-admin-ui-logo/schema.graphql | 130 +- .../custom-admin-ui-navigation/CHANGELOG.md | 13 + .../custom-admin-ui-navigation/package.json | 8 +- .../custom-admin-ui-navigation/schema.graphql | 130 +- examples/custom-admin-ui-pages/CHANGELOG.md | 15 + examples/custom-admin-ui-pages/README.md | 42 +- .../admin/components/CustomNavigation.tsx | 17 + .../custom-admin-ui-pages/admin/config.ts | 5 + .../admin/pages/custom-page.tsx | 39 +- examples/custom-admin-ui-pages/package.json | 9 +- examples/custom-admin-ui-pages/schema.graphql | 130 +- examples/custom-field-view/CHANGELOG.md | 9 + examples/custom-field-view/package.json | 8 +- examples/custom-field-view/schema.graphql | 130 +- examples/custom-field/CHANGELOG.md | 9 + examples/custom-field/package.json | 8 +- examples/custom-field/schema.graphql | 144 +- examples/default-values/CHANGELOG.md | 8 + examples/default-values/package.json | 6 +- examples/default-values/schema.graphql | 130 +- examples/default-values/schema.ts | 2 +- examples/document-field/CHANGELOG.md | 9 + examples/document-field/package.json | 8 +- examples/document-field/schema.graphql | 136 +- examples/extend-graphql-schema/CHANGELOG.md | 10 + examples/extend-graphql-schema/README.md | 2 +- .../extend-graphql-schema/custom-schema.ts | 8 +- examples/extend-graphql-schema/package.json | 6 +- examples/extend-graphql-schema/schema.graphql | 208 ++- examples/json/CHANGELOG.md | 8 + examples/json/package.json | 6 +- examples/json/schema.graphql | 98 +- examples/task-manager/CHANGELOG.md | 8 + examples/task-manager/package.json | 6 +- examples/task-manager/schema.graphql | 130 +- examples/testing/CHANGELOG.md | 15 + examples/testing/package.json | 18 +- examples/testing/schema.graphql | 251 +-- examples/virtual-field/CHANGELOG.md | 9 + examples/virtual-field/README.md | 2 +- examples/virtual-field/package.json | 8 +- examples/virtual-field/schema.graphql | 136 +- examples/virtual-field/schema.ts | 2 +- examples/with-auth/CHANGELOG.md | 11 + examples/with-auth/package.json | 8 +- examples/with-auth/schema.graphql | 251 +-- package.json | 28 +- packages/admin-ui-utils/CHANGELOG.md | 7 + packages/admin-ui-utils/package.json | 8 +- packages/auth/CHANGELOG.md | 31 + packages/auth/package.json | 18 +- packages/auth/src/schema.ts | 2 +- packages/cloudinary/CHANGELOG.md | 8 + packages/cloudinary/package.json | 10 +- packages/fields-document/CHANGELOG.md | 35 + packages/fields-document/package.json | 14 +- .../src/DocumentEditor/index.tsx | 31 +- .../fields-document/src/relationship-data.tsx | 2 +- packages/fields/CHANGELOG.md | 117 ++ packages/fields/package.json | 18 +- packages/fields/src/tests/test-fixtures.ts | 32 +- .../fields/src/types/autoIncrement/index.ts | 91 +- .../autoIncrement/tests/test-fixtures.ts | 115 +- packages/fields/src/types/checkbox/index.ts | 5 + .../fields/src/types/checkbox/views/index.tsx | 5 +- packages/fields/src/types/decimal/index.ts | 5 + .../fields/src/types/decimal/views/index.tsx | 16 +- packages/fields/src/types/float/index.ts | 5 + .../fields/src/types/float/views/index.tsx | 16 +- packages/fields/src/types/integer/index.ts | 5 + .../fields/src/types/integer/views/index.tsx | 16 +- packages/fields/src/types/password/index.ts | 21 + .../fields/src/types/password/views/index.tsx | 4 +- .../fields/src/types/relationship/index.ts | 12 + .../relationship/tests/implementation.test.ts | 4 +- .../relationship/views/RelationshipSelect.tsx | 16 +- packages/fields/src/types/select/index.ts | 13 + .../fields/src/types/select/views/index.tsx | 28 +- packages/fields/src/types/text/index.ts | 10 +- .../fields/src/types/text/views/index.tsx | 23 +- packages/fields/src/types/timestamp/index.ts | 5 + packages/keystone/CHANGELOG.md | 165 ++ packages/keystone/package.json | 32 +- .../admin-ui/id-field-view.tsx | 13 +- .../admin-ui/next-config.ts | 3 + .../admin-ui/pages/ItemPage/index.tsx | 14 +- .../admin-ui/pages/ListPage/index.tsx | 102 +- .../next-graphql.ts | 2 +- .../src/admin-ui/system/getAdminMetaSchema.ts | 13 +- .../keystone/src/admin-ui/templates/api.ts | 2 +- .../keystone/src/lib/core/graphql-errors.ts | 11 +- .../src/lib/core/mutations/access-control.ts | 6 +- .../src/lib/core/mutations/create-update.ts | 64 +- .../keystone/src/lib/core/mutations/hooks.ts | 11 +- .../src/lib/core/queries/resolvers.ts | 24 +- .../keystone/src/lib/core/types-for-lists.ts | 45 +- .../keystone/src/lib/core/where-inputs.ts | 82 +- packages/keystone/src/lib/id-field.ts | 63 + .../src/lib/server/createApolloServer.ts | 44 +- .../src/lib/server/createExpressServer.ts | 16 +- packages/keystone/src/schema/schema.ts | 2 +- .../__snapshots__/artifacts.test.ts.snap | 66 +- .../keystone/src/scripts/tests/build.test.ts | 2 +- .../fixtures/basic-project/schema.graphql | 58 +- packages/keystone/src/session/index.ts | 9 +- packages/session-store-redis/package.json | 2 +- packages/testing/CHANGELOG.md | 7 + packages/testing/package.json | 6 +- packages/types/CHANGELOG.md | 126 ++ packages/types/package.json | 7 +- packages/types/src/config/index.ts | 34 + packages/types/src/filters/enum-filter.ts | 76 + packages/types/src/filters/index.ts | 88 + .../types/src/filters/providers/postgresql.ts | 647 +++++++ .../types/src/filters/providers/sqlite.ts | 438 +++++ packages/types/src/index.ts | 1 + .../json-field-type-polyfill-for-sqlite.ts | 4 +- packages/types/src/next-fields.ts | 92 +- packages/types/src/utils.ts | 2 +- packages/utils/CHANGELOG.md | 7 + packages/utils/package.json | 6 +- prisma-utils/.gitignore | 1 + prisma-utils/README.md | 5 + prisma-utils/package.json | 17 + prisma-utils/src/index.ts | 313 ++++ tests/admin-ui-tests/CHANGELOG.md | 9 + tests/admin-ui-tests/package.json | 10 +- tests/api-tests/CHANGELOG.md | 13 + tests/api-tests/access-control/authed.test.ts | 16 +- .../mutations-field-static.test.ts | 14 +- .../mutations-list-declarative.test.ts | 18 +- .../mutations-list-static.test.ts | 21 +- .../access-control/not-authed.test.ts | 18 +- tests/api-tests/access-control/utils.ts | 8 +- tests/api-tests/auth-header.test.ts | 5 +- .../extend-graphql-schema.test.ts | 12 +- tests/api-tests/fields/filter.test.ts | 602 ++++--- tests/api-tests/fields/types/Virtual.test.ts | 8 +- tests/api-tests/fields/types/document.test.ts | 4 +- tests/api-tests/fields/unique.test.ts | 4 + tests/api-tests/hooks/hook-errors.test.ts | 895 ++++++---- tests/api-tests/hooks/list-hooks.test.ts | 73 +- tests/api-tests/id-field.test.ts | 2 +- tests/api-tests/package.json | 10 +- tests/api-tests/queries/cache-hints.test.ts | 4 +- tests/api-tests/queries/filters.test.ts | 14 +- tests/api-tests/queries/limits.test.ts | 24 +- tests/api-tests/queries/relationships.test.ts | 40 +- .../many-to-many-one-sided.test.ts | 12 +- .../crud-self-ref/many-to-many.test.ts | 12 +- .../one-to-many-one-sided.test.ts | 10 +- .../crud-self-ref/one-to-many.test.ts | 22 +- .../crud-self-ref/one-to-one.test.ts | 28 +- .../crud/many-to-many-one-sided.test.ts | 24 +- .../relationships/crud/many-to-many.test.ts | 24 +- .../crud/one-to-many-one-sided.test.ts | 10 +- .../relationships/crud/one-to-many.test.ts | 22 +- .../relationships/crud/one-to-one.test.ts | 48 +- .../filtering/access-control.test.ts | 4 +- .../relationships/filtering/filtering.test.ts | 33 +- .../relationships/filtering/nested.test.ts | 16 +- .../relationships/many-to-one-to-one.test.ts | 38 +- .../create-and-connect-many.test.ts | 4 +- .../nested-mutations/create-many.test.ts | 14 +- .../nested-mutations/create-singular.test.ts | 6 +- .../relationships/shared-names.test.ts | 2 +- tests/api-tests/utils.ts | 82 +- tests/benchmarks/CHANGELOG.md | 9 + tests/benchmarks/package.json | 8 +- .../custom-admin-ui-pages.test.ts | 3 +- tests/examples-smoke-tests/package.json | 2 +- tests/test-projects/basic/CHANGELOG.md | 8 + tests/test-projects/basic/package.json | 6 +- tests/test-projects/basic/schema.graphql | 130 +- .../crud-notifications/CHANGELOG.md | 8 + .../crud-notifications/README.md | 4 + .../crud-notifications/keystone.ts | 23 + .../crud-notifications/package.json | 22 + .../crud-notifications/schema.graphql | 329 ++++ .../crud-notifications/schema.prisma | 27 + .../crud-notifications/schema.ts | 38 + yarn.lock | 1572 +++++++++-------- 297 files changed, 9663 insertions(+), 5030 deletions(-) delete mode 100644 .changeset/brown-glasses-complain.md delete mode 100644 .changeset/chilled-grapes-cry.md delete mode 100644 .changeset/cold-cycles-cry.md delete mode 100644 .changeset/dull-humans-yell.md delete mode 100644 .changeset/early-experts-allow.md delete mode 100644 .changeset/eleven-mayflies-kneel.md delete mode 100644 .changeset/empty-baboons-unite.md delete mode 100644 .changeset/fast-guests-tickle.md delete mode 100644 .changeset/fluffy-coins-argue.md delete mode 100644 .changeset/good-cycles-ring.md delete mode 100644 .changeset/happy-seals-cheat.md delete mode 100644 .changeset/khaki-ducks-poke.md delete mode 100644 .changeset/lovely-pears-sort.md delete mode 100644 .changeset/nasty-windows-study.md delete mode 100644 .changeset/olive-maps-serve.md delete mode 100644 .changeset/olive-poems-explode.md delete mode 100644 .changeset/proud-impalas-flash.md delete mode 100644 .changeset/rich-tomatoes-own.md delete mode 100644 .changeset/silly-impalas-type.md delete mode 100644 .changeset/smooth-moose-eat.md delete mode 100644 .changeset/strange-crabs-glow.md delete mode 100644 .changeset/tidy-suits-refuse.md delete mode 100644 .changeset/tough-geese-trade.md delete mode 100644 .changeset/wet-frogs-share.md delete mode 100644 .changeset/wise-pianos-flash.md create mode 100644 docs/babel.config.js create mode 100644 docs/pages/updates/new-graphql-api.mdx create mode 100644 docs/public/assets/guides/custom-admin-ui-pages/custom-page-completed.png create mode 100644 docs/public/assets/guides/custom-admin-ui-pages/custom-page-w-page-container.png create mode 100644 docs/public/assets/guides/custom-admin-ui-pages/custom-page-with-styled-header.png create mode 100644 docs/public/assets/guides/custom-admin-ui-pages/header-prop.png create mode 100644 docs/public/assets/guides/custom-admin-ui-pages/simple-custom-page.png create mode 100644 examples/custom-admin-ui-pages/admin/components/CustomNavigation.tsx create mode 100644 examples/custom-admin-ui-pages/admin/config.ts create mode 100644 packages/types/src/filters/enum-filter.ts create mode 100644 packages/types/src/filters/index.ts create mode 100644 packages/types/src/filters/providers/postgresql.ts create mode 100644 packages/types/src/filters/providers/sqlite.ts create mode 100644 prisma-utils/.gitignore create mode 100644 prisma-utils/README.md create mode 100644 prisma-utils/package.json create mode 100644 prisma-utils/src/index.ts create mode 100644 tests/test-projects/crud-notifications/CHANGELOG.md create mode 100644 tests/test-projects/crud-notifications/README.md create mode 100644 tests/test-projects/crud-notifications/keystone.ts create mode 100644 tests/test-projects/crud-notifications/package.json create mode 100644 tests/test-projects/crud-notifications/schema.graphql create mode 100644 tests/test-projects/crud-notifications/schema.prisma create mode 100644 tests/test-projects/crud-notifications/schema.ts diff --git a/.changeset/brown-glasses-complain.md b/.changeset/brown-glasses-complain.md deleted file mode 100644 index 1dd24fb1cbd..00000000000 --- a/.changeset/brown-glasses-complain.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@keystone-next/fields': major -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -Removed `_ListKeyMeta` and `_toManyRelationshipFieldMeta` fields. You should use `listKeyCount` and `toManyRelationshipFieldCount` instead diff --git a/.changeset/chilled-grapes-cry.md b/.changeset/chilled-grapes-cry.md deleted file mode 100644 index 8900a01a4fe..00000000000 --- a/.changeset/chilled-grapes-cry.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@keystone-next/example-ecommerce': patch -'@keystone-next/fields-document': patch -'@keystone-next/keystone': patch ---- - -Updated internal type definitions. diff --git a/.changeset/cold-cycles-cry.md b/.changeset/cold-cycles-cry.md deleted file mode 100644 index 01d444759a0..00000000000 --- a/.changeset/cold-cycles-cry.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -Removed all arguments from `context.lists.List.count` and `context.db.lists.List.count` except for `where`. diff --git a/.changeset/dull-humans-yell.md b/.changeset/dull-humans-yell.md deleted file mode 100644 index 2c54e688c64..00000000000 --- a/.changeset/dull-humans-yell.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -The create one mutation now requires a non-null `data` argument and the create many mutation accepts a list of `ItemCreateInput` directly instead of being nested inside of an object with the `ItemCreateInput` in a `data` field. - -If you have a list called `Item`, `createItem` now looks like `createItem(data: ItemCreateInput!): Item` and `createItems` now looks like `createItems(data: [ItemCreateInput!]!): [Item]`. diff --git a/.changeset/early-experts-allow.md b/.changeset/early-experts-allow.md deleted file mode 100644 index 0ba0c99a6e4..00000000000 --- a/.changeset/early-experts-allow.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@keystone-next/fields': major -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -Removed `search` argument from the GraphQL API for finding many items, Lists/DB API and to-many relationship fields. You should use `contains` filters instead. diff --git a/.changeset/eleven-mayflies-kneel.md b/.changeset/eleven-mayflies-kneel.md deleted file mode 100644 index 1dbd83a8621..00000000000 --- a/.changeset/eleven-mayflies-kneel.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@keystone-ui/notice': patch -'@keystone-ui/toast': patch ---- - -Updated css to preserve whitespace formatting of error messages. diff --git a/.changeset/empty-baboons-unite.md b/.changeset/empty-baboons-unite.md deleted file mode 100644 index 0e612be3d43..00000000000 --- a/.changeset/empty-baboons-unite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/keystone': patch ---- - -Removed unused code path in Admin UI error display. diff --git a/.changeset/fast-guests-tickle.md b/.changeset/fast-guests-tickle.md deleted file mode 100644 index 6fcc1576912..00000000000 --- a/.changeset/fast-guests-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/fields': patch ---- - -Updated timestamp field to default time to 00:00 when no time is selected. diff --git a/.changeset/fluffy-coins-argue.md b/.changeset/fluffy-coins-argue.md deleted file mode 100644 index 214507238cc..00000000000 --- a/.changeset/fluffy-coins-argue.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@keystone-next/example-testing': patch -'@keystone-next/keystone': patch ---- - -Added more details to validation failure error messages. diff --git a/.changeset/good-cycles-ring.md b/.changeset/good-cycles-ring.md deleted file mode 100644 index 9aefdfff97e..00000000000 --- a/.changeset/good-cycles-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/example-custom-admin-ui-pages': major ---- - -Initial version of the custom-admin-ui-pages example. diff --git a/.changeset/happy-seals-cheat.md b/.changeset/happy-seals-cheat.md deleted file mode 100644 index 3251f247d82..00000000000 --- a/.changeset/happy-seals-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/keystone': major ---- - -Removed the `uid` and `name` properties from the errors returned by the GraphQL API. diff --git a/.changeset/khaki-ducks-poke.md b/.changeset/khaki-ducks-poke.md deleted file mode 100644 index 4f3a5c1675a..00000000000 --- a/.changeset/khaki-ducks-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/fields-document': patch ---- - -Suppressed error logging during tests. diff --git a/.changeset/lovely-pears-sort.md b/.changeset/lovely-pears-sort.md deleted file mode 100644 index 4522dc1b66e..00000000000 --- a/.changeset/lovely-pears-sort.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@keystone-next/fields': major -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -Removed `sortBy` argument from the GraphQL API for finding many items, Lists/DB API and to-many relationship fields. You should use `orderBy` instead. diff --git a/.changeset/nasty-windows-study.md b/.changeset/nasty-windows-study.md deleted file mode 100644 index ddfd1ef4d16..00000000000 --- a/.changeset/nasty-windows-study.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'@keystone-next/fields': major -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -`disconnectAll` has been renamed to `disconnect` in to-one relationship inputs and the old `disconnect` field has been removed. There are also seperate input types for create and update where the input for create doesn't have `disconnect`. It's also now required that if you provide a to-one relationship input, you must provide exactly one field to the input. - -If you have a list called `Item`, the to-one relationship inputs now look like this: - -```graphql -input ItemRelateToOneForCreateInput { - create: ItemCreateInput - connect: ItemWhereUniqueInput -} -input ItemRelateToOneForUpdateInput { - create: ItemCreateInput - connect: ItemWhereUniqueInput - disconnect: Boolean -} -``` diff --git a/.changeset/olive-maps-serve.md b/.changeset/olive-maps-serve.md deleted file mode 100644 index ac2ca1c4f58..00000000000 --- a/.changeset/olive-maps-serve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/keystone': patch ---- - -Added more details to before/after change/delete hook error messages. diff --git a/.changeset/olive-poems-explode.md b/.changeset/olive-poems-explode.md deleted file mode 100644 index 05499df63c9..00000000000 --- a/.changeset/olive-poems-explode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/keystone': patch ---- - -Removed unused dependency `@graphql-tools/schema`. diff --git a/.changeset/proud-impalas-flash.md b/.changeset/proud-impalas-flash.md deleted file mode 100644 index c190c8b8943..00000000000 --- a/.changeset/proud-impalas-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-ui/segmented-control': patch ---- - -Fixed segmented-control focus style. diff --git a/.changeset/rich-tomatoes-own.md b/.changeset/rich-tomatoes-own.md deleted file mode 100644 index 03f9139408a..00000000000 --- a/.changeset/rich-tomatoes-own.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'@keystone-next/fields': major -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -`disconnectAll` has been replaced by `set` in to-many relationship inputs, the equivalent to `disconnectAll: true` is now `set: []`. There are also seperate input types for create and update where the input for create doesn't have `disconnect` or `set`. The inputs in the lists in the input field are now also non-null. - -If you have a list called `Item`, the to-many relationship inputs now look like this: - -```graphql -input ItemRelateToManyForCreateInput { - create: [ItemCreateInput!] - connect: [ItemWhereUniqueInput!] -} -input ItemRelateToManyForUpdateInput { - disconnect: [ItemWhereUniqueInput!] - set: [ItemWhereUniqueInput!] - create: [ItemCreateInput!] - connect: [ItemWhereUniqueInput!] -} -``` diff --git a/.changeset/silly-impalas-type.md b/.changeset/silly-impalas-type.md deleted file mode 100644 index e58a4065155..00000000000 --- a/.changeset/silly-impalas-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/example-custom-admin-ui-navigation': major ---- - -Initial version of the custom-admin-ui-navigation example. diff --git a/.changeset/smooth-moose-eat.md b/.changeset/smooth-moose-eat.md deleted file mode 100644 index 63c01fa8093..00000000000 --- a/.changeset/smooth-moose-eat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@keystone-next/keystone": patch ---- - -Updated Prisma dependencies to `2.28.0`. diff --git a/.changeset/strange-crabs-glow.md b/.changeset/strange-crabs-glow.md deleted file mode 100644 index ada423ec843..00000000000 --- a/.changeset/strange-crabs-glow.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@keystone-next/keystone': major -'@keystone-next/types': major -'@keystone-next/fields-document': patch -'@keystone-next/fields': patch ---- - -The generated CRUD queries, and some of the input types, in the GraphQL API have been renamed. - -If you have a list called `Item`, the query for multiple values, `allItems` will be renamed to `items`. The query for a single value, `Item`, will be renamed to `item`. - -Also, the input type used in the `updateItems` mutation has been renamed from `ItemsUpdateInput` to `ItemUpdateArgs`. diff --git a/.changeset/tidy-suits-refuse.md b/.changeset/tidy-suits-refuse.md deleted file mode 100644 index 88067ac37d5..00000000000 --- a/.changeset/tidy-suits-refuse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/keystone': patch ---- - -Updated internal error handling to use the `apollo-server-errors` package instead of `apollo-errors`. \ No newline at end of file diff --git a/.changeset/tough-geese-trade.md b/.changeset/tough-geese-trade.md deleted file mode 100644 index 02343afcfbd..00000000000 --- a/.changeset/tough-geese-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@keystone-next/types': patch ---- - -Updated types to allow the `'id'` field in `ui.labelField`, `ui.listView.initialColumns`, and `ui.listView.initialSort`. diff --git a/.changeset/wet-frogs-share.md b/.changeset/wet-frogs-share.md deleted file mode 100644 index e939b428e24..00000000000 --- a/.changeset/wet-frogs-share.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'@keystone-next/auth': major -'@keystone-next/fields': major -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -The update mutations now accept `where` unique inputs instead of only an `id` and the `where` and `data` arguments are non-null. - -If you have a list called `Item`, the update mutations now look like this: - -```graphql -type Mutation { - updateItem(where: ItemWhereUniqueInput!, data: ItemUpdateInput!): Item - updateItems(data: [ItemUpdateArgs!]!): [Item] -} - -input ItemUpdateArgs { - where: ItemWhereUniqueInput! - data: ItemUpdateInput! -} -``` diff --git a/.changeset/wise-pianos-flash.md b/.changeset/wise-pianos-flash.md deleted file mode 100644 index 9a6d732caf1..00000000000 --- a/.changeset/wise-pianos-flash.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@keystone-next/keystone': major -'@keystone-next/types': major ---- - -The delete mutations now accept `where` unique inputs instead of only an `id`. - -If you have a list called `Item`, `deleteItem` now looks like `deleteItem(where: ItemWhereUniqueInput!): Item` and `deleteItems` now looks like `deleteItems(where: [ItemWhereUniqueInput!]!): [Item]` diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0e1e0d9cf98..fb9ea6031fd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -298,10 +298,10 @@ jobs: run: yarn - name: Install Dependencies of Browsers if: needs.should_run_tests.outputs.shouldRunTests == 'true' - uses: microsoft/playwright-github-action@v1 - - name: Install Browsers for Playwright + run: npx playwright install-deps + - name: Install Browsers if: needs.should_run_tests.outputs.shouldRunTests == 'true' - run: node ./node_modules/playwright/install.js + run: npx playwright install - name: Unit tests if: needs.should_run_tests.outputs.shouldRunTests == 'true' run: yarn jest --ci --runInBand tests/examples-smoke-tests/${{ matrix.test }} @@ -396,3 +396,5 @@ jobs: run: yarn lint:markdown - name: Example schemas run: yarn lint:examples + - name: Prisma Filters + run: yarn lint:filters diff --git a/.prettierignore b/.prettierignore index dd5329b7230..023ecfd4978 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,3 +8,4 @@ coverage **/.keystone docs/public/assets/ .keystone/tests +prisma-utils/src/generated diff --git a/design-system/packages/button/package.json b/design-system/packages/button/package.json index fa23ea95133..5f81ce360d2 100644 --- a/design-system/packages/button/package.json +++ b/design-system/packages/button/package.json @@ -5,10 +5,10 @@ "main": "dist/button.cjs.js", "module": "dist/button.esm.js", "devDependencies": { - "@types/react": "^17.0.15" + "@types/react": "^17.0.18" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.0.0", "@keystone-ui/icons": "^4.0.0", "@keystone-ui/loading": "^4.0.0", diff --git a/design-system/packages/core/package.json b/design-system/packages/core/package.json index ed7cad3272a..672d1c3ff4d 100644 --- a/design-system/packages/core/package.json +++ b/design-system/packages/core/package.json @@ -5,7 +5,7 @@ "main": "dist/core.cjs.js", "module": "dist/core.esm.js", "devDependencies": { - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "react": "^17.0.2", "react-dom": "^17.0.2" }, @@ -14,8 +14,8 @@ "react-dom": "^17.0.2" }, "dependencies": { - "@babel/runtime": "^7.14.8", - "@emotion/react": "^11.4.0", + "@babel/runtime": "^7.15.3", + "@emotion/react": "^11.4.1", "@types/facepaint": "^1.2.2", "facepaint": "^1.2.1" }, diff --git a/design-system/packages/fields/package.json b/design-system/packages/fields/package.json index 8813ee40496..a82ec123dc0 100644 --- a/design-system/packages/fields/package.json +++ b/design-system/packages/fields/package.json @@ -5,10 +5,10 @@ "main": "dist/fields.cjs.js", "module": "dist/fields.esm.js", "devDependencies": { - "@types/react": "^17.0.15" + "@types/react": "^17.0.18" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.1.0", "@keystone-ui/icons": "^4.0.0", "@keystone-ui/popover": "^4.0.2", diff --git a/design-system/packages/icons/package.json b/design-system/packages/icons/package.json index 3938ba866a9..7a3c11a6eb2 100644 --- a/design-system/packages/icons/package.json +++ b/design-system/packages/icons/package.json @@ -12,7 +12,7 @@ "@svgr/plugin-jsx": "^5.5.0", "@svgr/plugin-prettier": "^5.5.0", "@svgr/plugin-svgo": "^5.5.0", - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "chalk": "^4.1.2", "feather-icons": "^4.28.0", "fs-extra": "^10.0.0", @@ -21,7 +21,7 @@ "to-pascal-case": "^1.0.0" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.0.0" }, "peerDependencies": { diff --git a/design-system/packages/loading/package.json b/design-system/packages/loading/package.json index b037e822fec..98a7c74bebc 100644 --- a/design-system/packages/loading/package.json +++ b/design-system/packages/loading/package.json @@ -5,10 +5,10 @@ "main": "dist/loading.cjs.js", "module": "dist/loading.esm.js", "devDependencies": { - "@types/react": "^17.0.15" + "@types/react": "^17.0.18" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.0.0", "react": "^17.0.2" }, diff --git a/design-system/packages/modals/package.json b/design-system/packages/modals/package.json index b71ad2e6deb..43f4ad1d17c 100644 --- a/design-system/packages/modals/package.json +++ b/design-system/packages/modals/package.json @@ -5,10 +5,10 @@ "main": "dist/modals.cjs.js", "module": "dist/modals.esm.js", "devDependencies": { - "@types/react": "^17.0.15" + "@types/react": "^17.0.18" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.0.0", "react": "^17.0.2", diff --git a/design-system/packages/notice/CHANGELOG.md b/design-system/packages/notice/CHANGELOG.md index 54449b00a52..9f450557c56 100644 --- a/design-system/packages/notice/CHANGELOG.md +++ b/design-system/packages/notice/CHANGELOG.md @@ -1,5 +1,11 @@ # @keystone-ui/notice +## 4.0.1 + +### Patch Changes + +- [#6220](https://github.com/keystonejs/keystone/pull/6220) [`c2bb6a9a5`](https://github.com/keystonejs/keystone/commit/c2bb6a9a596fc52a3c61ec5d91c79758e417e61d) Thanks [@timleslie](https://github.com/timleslie)! - Updated css to preserve whitespace formatting of error messages. + ## 4.0.0 ### Major Changes diff --git a/design-system/packages/notice/package.json b/design-system/packages/notice/package.json index e3a225031c7..77428a5a921 100644 --- a/design-system/packages/notice/package.json +++ b/design-system/packages/notice/package.json @@ -1,14 +1,14 @@ { "name": "@keystone-ui/notice", - "version": "4.0.0", + "version": "4.0.1", "license": "MIT", "main": "dist/notice.cjs.js", "module": "dist/notice.esm.js", "devDependencies": { - "@types/react": "^17.0.15" + "@types/react": "^17.0.18" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.0.0", "@keystone-ui/icons": "^4.0.0", diff --git a/design-system/packages/options/package.json b/design-system/packages/options/package.json index 71e80dde29d..2a16ea71b94 100644 --- a/design-system/packages/options/package.json +++ b/design-system/packages/options/package.json @@ -11,7 +11,7 @@ "react": "^17.0.2" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.0.0", "@keystone-ui/fields": "^4.1.2", "@keystone-ui/icons": "^4.0.0", diff --git a/design-system/packages/pill/package.json b/design-system/packages/pill/package.json index 53ee6464e98..7687bdb2767 100644 --- a/design-system/packages/pill/package.json +++ b/design-system/packages/pill/package.json @@ -11,7 +11,7 @@ "react": "^17.0.2" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.0.0", "@keystone-ui/icons": "^4.0.0" }, diff --git a/design-system/packages/popover/package.json b/design-system/packages/popover/package.json index b1b4acb6245..52762002475 100644 --- a/design-system/packages/popover/package.json +++ b/design-system/packages/popover/package.json @@ -13,10 +13,10 @@ "react-dom": "^17.0.2" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.0.0", - "@popperjs/core": "^2.9.2", - "focus-trap": "^6.6.0", + "@popperjs/core": "^2.9.3", + "focus-trap": "^6.6.1", "react-popper": "^2.2.5" }, "engines": { diff --git a/design-system/packages/segmented-control/CHANGELOG.md b/design-system/packages/segmented-control/CHANGELOG.md index 339613f37d8..537191bf60f 100644 --- a/design-system/packages/segmented-control/CHANGELOG.md +++ b/design-system/packages/segmented-control/CHANGELOG.md @@ -1,5 +1,11 @@ # @keystone-ui/segmented-control +## 4.0.2 + +### Patch Changes + +- [#6235](https://github.com/keystonejs/keystone/pull/6235) [`6cd7ab78e`](https://github.com/keystonejs/keystone/commit/6cd7ab78e018fa0ffaddc1258426d23da19cd854) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Fixed segmented-control focus style. + ## 4.0.1 ### Patch Changes diff --git a/design-system/packages/segmented-control/package.json b/design-system/packages/segmented-control/package.json index dc1d54f67d7..51d0acd8fdd 100644 --- a/design-system/packages/segmented-control/package.json +++ b/design-system/packages/segmented-control/package.json @@ -1,15 +1,15 @@ { "name": "@keystone-ui/segmented-control", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "main": "dist/segmented-control.cjs.js", "module": "dist/segmented-control.esm.js", "devDependencies": { - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "react": "^17.0.2" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.1.0" }, "peerDependencies": { diff --git a/design-system/packages/toast/CHANGELOG.md b/design-system/packages/toast/CHANGELOG.md index 24cfbd25711..ae0d11b3ad0 100644 --- a/design-system/packages/toast/CHANGELOG.md +++ b/design-system/packages/toast/CHANGELOG.md @@ -1,5 +1,11 @@ # @keystone-ui/toast +## 4.0.2 + +### Patch Changes + +- [#6220](https://github.com/keystonejs/keystone/pull/6220) [`c2bb6a9a5`](https://github.com/keystonejs/keystone/commit/c2bb6a9a596fc52a3c61ec5d91c79758e417e61d) Thanks [@timleslie](https://github.com/timleslie)! - Updated css to preserve whitespace formatting of error messages. + ## 4.0.1 ### Patch Changes diff --git a/design-system/packages/toast/package.json b/design-system/packages/toast/package.json index 13eebb8f486..3c1ebc4c2df 100644 --- a/design-system/packages/toast/package.json +++ b/design-system/packages/toast/package.json @@ -1,15 +1,15 @@ { "name": "@keystone-ui/toast", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "main": "dist/toast.cjs.js", "module": "dist/toast.esm.js", "devDependencies": { - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "react": "^17.0.2" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.0.0", "@keystone-ui/icons": "^4.0.0" }, diff --git a/design-system/packages/tooltip/package.json b/design-system/packages/tooltip/package.json index 8d1769949e8..57cefc86d4d 100644 --- a/design-system/packages/tooltip/package.json +++ b/design-system/packages/tooltip/package.json @@ -13,7 +13,7 @@ "react-dom": "^17.0.2" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/core": "^3.0.0", "@keystone-ui/popover": "^4.0.0", "apply-ref": "^1.0.0" diff --git a/design-system/website/package.json b/design-system/website/package.json index 80c912e0e83..f5816f89485 100644 --- a/design-system/website/package.json +++ b/design-system/website/package.json @@ -7,7 +7,7 @@ "dev": "next -p 8080" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.0.0", "@keystone-ui/fields": "^4.1.2", @@ -21,7 +21,7 @@ "@keystone-ui/toast": "^4.0.1", "@keystone-ui/tooltip": "^4.0.0", "@preconstruct/next": "^3.0.0", - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "@types/react-dom": "^17.0.9", "@types/tinycolor2": "^1.4.3", "@types/webpack": "^4.41.30", diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 201702ee365..a1174b7a435 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,12 @@ # @keystone-next/website +## 3.1.4 + +### Patch Changes + +- Updated dependencies [[`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`e985aa010`](https://github.com/keystonejs/keystone/commit/e985aa0104d30a779f21ec05d80e6b98ece87dfb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`69f47bfed`](https://github.com/keystonejs/keystone/commit/69f47bfed1eaa1269cfdc42071268a914bd4aa17), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda)]: + - @keystone-next/fields-document@8.0.0 + ## 3.1.3 ### Patch Changes diff --git a/docs/babel.config.js b/docs/babel.config.js new file mode 100644 index 00000000000..8808c5f11d7 --- /dev/null +++ b/docs/babel.config.js @@ -0,0 +1 @@ +module.exports = require('../babel.config'); diff --git a/docs/components/docs/Navigation.tsx b/docs/components/docs/Navigation.tsx index e4e4f641ca5..f854aea01c7 100644 --- a/docs/components/docs/Navigation.tsx +++ b/docs/components/docs/Navigation.tsx @@ -136,28 +136,22 @@ export function DocsNavigation() { Keystone 5 vs 6 Command Line Relationships - Query Filters + + Query Filters Updated + Hooks Document Fields Document Field Demo - - Virtual Fields New - - - Testing New - + Virtual Fields + Testing - Custom Fields New - - - Custom Admin UI Logo New + Custom Fields Updated + Custom Admin UI Logo - Custom Admin UI Pages New - - - Custom Admin UI Navigation New + Custom Admin UI Pages Updated + Custom Admin UI Navigation Access Control @@ -190,8 +184,12 @@ export function DocsNavigation() { DB Item API GraphQL - GraphQL API - Query Filter API + + GraphQL API Updated + + + Query Filter API Updated + ); diff --git a/docs/next-env.d.ts b/docs/next-env.d.ts index 7b7aa2c7727..f87ea7fa66a 100644 --- a/docs/next-env.d.ts +++ b/docs/next-env.d.ts @@ -1,2 +1,4 @@ +// @ts-ignore /// /// +/// diff --git a/docs/next.config.js b/docs/next.config.js index e3c02223414..cd2df154aed 100644 --- a/docs/next.config.js +++ b/docs/next.config.js @@ -1,6 +1,5 @@ const withPreconstruct = require('@preconstruct/next'); const withPlugins = require('next-compose-plugins'); -const withImages = require('next-images'); const mdxHints = require('remark-hint'); const gfm = require('remark-gfm'); @@ -20,7 +19,6 @@ const redirects = { module.exports = withPlugins([ withPreconstruct, - withImages, [ withMDX, { @@ -31,9 +29,7 @@ module.exports = withPlugins([ nextConfig.env = { siteUrl: 'https://keystonejs.com', }; - nextConfig.future = { - webpack5: true, - }; + nextConfig.eslint = { ignoreDuringBuilds: true }; nextConfig.typescript = { ...nextConfig.typescript, // we run TS elsewhere, Next runs against a different TS config which it insists on existing diff --git a/docs/package.json b/docs/package.json index 7e15ef884bb..4020fe7a8a1 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/website", - "version": "3.1.3", + "version": "3.1.4", "private": true, "license": "MIT", "scripts": { @@ -13,20 +13,20 @@ "cypress:run:ci": "yarn build && start-server-and-test start http://localhost:8000 cy:run" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@emotion/cache": "11.4.0", - "@emotion/react": "^11.4.0", + "@emotion/react": "^11.4.1", "@emotion/server": "11.4.0", "@emotion/weak-memoize": "^0.2.5", - "@keystone-next/fields-document": "^7.0.2", + "@keystone-next/fields-document": "^8.0.0", "@mdx-js/loader": "next", "@mdx-js/react": "^1.6.22", - "@next/mdx": "^10.2.3", + "@next/mdx": "^11.1.0", "@preconstruct/next": "^3.0.0", "@sindresorhus/slugify": "^1.1.2", "@types/gtag.js": "^0.0.7", "@types/mdx-js__react": "^1.5.4", - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "@types/react-dom": "^17.0.9", "@types/webpack": "^4.41.30", "classnames": "^2.3.1", @@ -34,9 +34,8 @@ "cypress": "^5.6.0", "date-fns": "^2.23.0", "facepaint": "^1.2.1", - "next": "^10.2.3", + "next": "npm:next@^11.1.0", "next-compose-plugins": "^2.2.1", - "next-images": "^1.8.1", "prism-react-renderer": "^1.2.1", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -46,7 +45,7 @@ "remark-hint": "^1.0.10" }, "devDependencies": { - "next-sitemap": "^1.6.140", + "next-sitemap": "^1.6.157", "start-server-and-test": "^1.13.1", "typescript": "^4.3.5" }, diff --git a/docs/pages/docs/apis/access-control.mdx b/docs/pages/docs/apis/access-control.mdx index b44076c2b11..d408568a48d 100644 --- a/docs/pages/docs/apis/access-control.mdx +++ b/docs/pages/docs/apis/access-control.mdx @@ -105,7 +105,7 @@ export default config({ ListKey: list({ fields: { isLocked: checkbox() }, // Declarative access control definition - access: { isLocked: false }, + access: { isLocked: { equals: false } }, }), }), }); diff --git a/docs/pages/docs/apis/auth.mdx b/docs/pages/docs/apis/auth.mdx index 2026ab6e974..bca3897dbd6 100644 --- a/docs/pages/docs/apis/auth.mdx +++ b/docs/pages/docs/apis/auth.mdx @@ -78,11 +78,11 @@ const { withAuth } = createAuth({ The following elements will be added to the GraphQL API. ```graphql -Mutation { +type Mutation { authenticateUserWithPassword(email: String!, password: String!): UserAuthenticationWithPasswordResult! } -Query { +type Query { authenticatedItem: AuthenticatedItem } @@ -188,7 +188,7 @@ const { withAuth } = createAuth({ Enabling `initFirstItem` will add the following elements to the GraphQL API. ```graphql -Mutation { +type Mutation { createInitialUser(data: CreateInitialUserInput!): UserAuthenticationWithPasswordSuccess! } @@ -274,12 +274,12 @@ const fields = { Enabling `passwordResetLink` will add the following elements to the GraphQL API. ```graphql -Mutation { +type Mutation { sendUserPasswordResetLink(email: String!): SendUserPasswordResetLinkResult redeemUserPasswordResetToken(email: String!, token: String!, password: String!): RedeemUserPasswordResetTokenResult } -Query { +type Query { validateUserPasswordResetToken(email: String!, token: String!): ValidateUserPasswordResetTokenResult } @@ -413,7 +413,7 @@ const fields = { Enabling `magicAuthLink` will add the following elements to the GraphQL API. ```graphql -Mutation { +type Mutation { sendUserMagicAuthLink(email: String!): SendUserMagicAuthLinkResult redeemUserMagicAuthToken(email: String!, token: String!): RedeemUserMagicAuthTokenResult! } diff --git a/docs/pages/docs/apis/config.mdx b/docs/pages/docs/apis/config.mdx index aa881ffab0e..3b6e7618400 100644 --- a/docs/pages/docs/apis/config.mdx +++ b/docs/pages/docs/apis/config.mdx @@ -119,13 +119,15 @@ The `sqlite` provider is not intended to be used in production systems, and has - `decimal`: The `decimal` field type is not supported. - `timestamp`: The `timestamp` field type only supports times within the range `1970 - 2038`. -- `text`: The `text` field type does not support the advanced filtering operations `contains`, `starts_with`, `ends_with`, or case insensitive filtering. -- `autoIncrement`: The `autoIncrement` field type can only be used as an `id` field. +- `text`: The `text` field type does not support setting a filter as case sensitive or insensitive. + Assuming default collation, all the filters except `contains`, `startsWith` and `endsWith` will be case sensitive + and `contains`, `startsWith` and `endsWith` will be case insensitive but only for ASCII characters. +- `autoIncrement`: The `autoIncrement` field type is not supported. - `select`: Using the `dataType: 'enum'` will use a GraphQL `String` type, rather than an `Enum` type. ## ui -``` +```ts import type { AdminUIConfig } from '@keystone-next/types'; ``` @@ -277,6 +279,8 @@ It has a TypeScript type of `GraphQLConfig`. Options: +- `debug` (default: `process.env.NODE_ENV !== 'production'`): If `true`, stacktraces from both Apollo errors and Keystone errors will be included in the errors returned from the GraphQL API. + These can be filtered out with `apolloConfig.formatError` if you need to process them, but do not want them returned over the GraphQL API. - `queryLimits` (default: `undefined`): Allows you to limit the total number of results returned from a query to your GraphQL API. See also the per-list `graphql.queryLimits` option in the [Schema API](./schema). - `apolloConfig` (default: `undefined`): Allows you to pass extra options into the `ApolloServer` constructor. @@ -287,6 +291,7 @@ Options: ```typescript export default config({ graphql: { + debug: process.env.NODE_ENV !== 'production', queryLimits: { maxTotalResults: 100 }, apolloConfig: { playground: process.env.NODE_ENV !== 'production', diff --git a/docs/pages/docs/apis/db-items.mdx b/docs/pages/docs/apis/db-items.mdx index 80544c7d345..e857f995535 100644 --- a/docs/pages/docs/apis/db-items.mdx +++ b/docs/pages/docs/apis/db-items.mdx @@ -16,14 +16,14 @@ For each list in your system the following API is available at `context.db.lists ``` { findOne({ where: { id } }), - findMany({ where, first, skip, sortBy }), - count({ where, first, skip }), + findMany({ where, take, skip, orderBy }), + count({ where }), createOne({ data }), createMany({ data }), - updateOne({ id, data }), + updateOne({ where, data }), updateMany({ data }), - deleteOne({ id }), - deleteMany({ ids }), + deleteOne({ where }), + deleteMany({ where }), } ``` @@ -44,10 +44,10 @@ All arguments are optional. ```typescript const users = await context.db.lists.User.findMany({ - where: { name_starts_with: 'A' }, - first: 10, + where: { name: { startsWith: 'A' } }, + take: 10, skip: 20, - sortBy: ['name_ASC'], + orderBy: [{ name: 'asc' }], }); ``` @@ -57,9 +57,7 @@ All arguments are optional. ```typescript const count = await context.db.lists.User.count({ - where: { name_starts_with: 'A' }, - first: 10, - skip: 20, + where: { name: { startsWith: 'A' } }, }); ``` @@ -80,16 +78,12 @@ const user = await context.db.lists.User.createOne({ const users = await context.db.lists.User.createMany({ data: [ { - data: { - name: 'Alice', - posts: [{ create: { title: 'Alices first post' } }], - }, + name: 'Alice', + posts: [{ create: { title: 'Alices first post' } }], }, { - data: { - name: 'Bob', - posts: [{ create: { title: 'Bobs first post' } }], - }, + name: 'Bob', + posts: [{ create: { title: 'Bobs first post' } }], }, ], }); @@ -99,7 +93,7 @@ const users = await context.db.lists.User.createMany({ ```typescript const user = await context.db.lists.User.updateOne({ - id: '...', + where: { id: '...' }, data: { name: 'Alice', posts: { create: [{ title: 'My first post' }] }, @@ -113,14 +107,14 @@ const user = await context.db.lists.User.updateOne({ const users = await context.db.lists.User.updateMany({ data: [ { - id: '...', + where: { id: '...' }, data: { name: 'Alice', posts: [{ create: { title: 'Alices first post' } }], }, }, { - id: '...', + where: { id: '...' }, data: { name: 'Bob', posts: [{ create: { title: 'Bobs first post' } }], @@ -134,15 +128,15 @@ const users = await context.db.lists.User.updateMany({ ```typescript const user = await context.db.lists.User.deleteOne({ - id: '...', + where: { id: '...' }, }); ``` ### deleteMany ```typescript -const user = await context.db.lists.User.deleteMany({ - ids: ['...', '...'], +const users = await context.db.lists.User.deleteMany({ + where: [{ id: '...' }, { id: '...' }], }); ``` diff --git a/docs/pages/docs/apis/filters.mdx b/docs/pages/docs/apis/filters.mdx index 6fbee0d3c38..5e58fc12cad 100644 --- a/docs/pages/docs/apis/filters.mdx +++ b/docs/pages/docs/apis/filters.mdx @@ -6,30 +6,27 @@ Each field type provides its own set of filters which can be used with [queries] This page lists all the filters available for each field type. For more details on how to use filters in queries please consult to the [GraphQL Queries - Filters](../guides/filters) guide. -Keystone filters are typically named after the field they are filtering. For example, a `text` field called `foo` would have filters named `foo_starts_with`, `foo_contains`, etc. -In all the examples below we will use a field named `foo` as the base of the filter names. - ## Scalar types ### checkbox -| **Filter name** | **Type** | **Description** | -| --------------- | --------- | --------------- | -| `foo` | `Boolean` | Equals | -| `foo_not` | `Boolean` | Does not equal | +| **Filter name** | **Type** | **Description** | +| --------------- | ----------------------- | ------------------------------- | +| `equals` | `Boolean` | Equals | +| `not` | `BooleanNullableFilter` | Does not match the inner filter | ### integer -| **Filter name** | **Type** | **Description** | -| --------------- | -------- | --------------------- | -| `foo` | `Int` | Equals | -| `foo_not` | `Int` | Does not equal | -| `foo_lt` | `Int` | Less than | -| `foo_lte` | `Int` | Less than or equal | -| `foo_gt` | `Int` | Greater than | -| `foo_gte` | `Int` | Greater than or equal | -| `foo_in` | `[Int]` | Is in the array | -| `foo_not_in` | `[Int]` | Is not in the array | +| **Filter name** | **Type** | **Description** | +| --------------- | ------------------- | ------------------------------- | +| `equals` | `Int` | Equals | +| `lt` | `Int` | Less than | +| `lte` | `Int` | Less than or equal | +| `gt` | `Int` | Greater than | +| `gte` | `Int` | Greater than or equal | +| `in` | `[Int!]` | Is in the array | +| `notIn` | `[Int!]` | Is not in the array | +| `not` | `IntNullableFilter` | Does not match the inner filter | ### json @@ -37,67 +34,69 @@ The `json` field type does not support filters. ### float -| **Filter name** | **Type** | **Description** | -| --------------- | --------- | --------------------- | -| `foo` | `Float` | Equals | -| `foo_not` | `Float` | Does not equal | -| `foo_lt` | `Float` | Less than | -| `foo_lte` | `Float` | Less than or equal | -| `foo_gt` | `Float` | Greater than | -| `foo_gte` | `Float` | Greater than or equal | -| `foo_in` | `[Float]` | Is in the array | -| `foo_not_in` | `[Float]` | Is not in the array | +| **Filter name** | **Type** | **Description** | +| --------------- | --------------------- | ------------------------------- | +| `equals` | `Float` | Equals | +| `lt` | `Float` | Less than | +| `lte` | `Float` | Less than or equal | +| `gt` | `Float` | Greater than | +| `gte` | `Float` | Greater than or equal | +| `in` | `[Float!]` | Is in the array | +| `notIn` | `[Float!]` | Is not in the array | +| `not` | `FloatNullableFilter` | Does not match the inner filter | ### password | **Filter name** | **Type** | **Description** | | --------------- | --------- | --------------- | -| `foo_is_set` | `Boolean` | A value is set | +| `isSet` | `Boolean` | A value is set | ### select -| **Filter name** | **Type** | **Description** | -| --------------- | ---------- | ------------------- | -| `foo` | `String` | Equals | -| `foo_not` | `String` | Does not equal | -| `foo_in` | `[String]` | Is in the array | -| `foo_not_in` | `[String]` | Is not in the array | +- If the `dataType` is `string`(the default), the same filters as `text` will be available. +- If the `dataType` is `integer`, the same filters as `integer` will be available. +- If the `dataType` is `enum`, the following filters will be available: + | **Filter name** | **Type** | **Description** | + | --------------- | ---------- | ------------------- | + | `equals` | `ListKeyFieldKeyType` | Equals | + | `in` | `[ListKeyFieldKeyType!]` | Is in the array | + | `notIn` | `[ListKeyFieldKeyType!]` | Is not in the array | + | `not` | `ListKeyFieldKeyTypeNullableFilter` | Does not match the inner filter | ### text -| **Filter name** | **Type** | **Description** | ** Notes ** | -| ----------------------- | ---------- | -------------------------------------- | ----------------- | -| `foo` | `String` | Equals | -| `foo_not` | `String` | Does not equal | -| `foo_contains` | `String` | Contains | -| `foo_not_contains` | `String` | Does not contain | -| `foo_starts_with` | `String` | Starts with | `postgresql` only | -| `foo_not_starts_with` | `String` | Does not start with | `postgresql` only | -| `foo_ends_with` | `String` | Ends with | `postgresql` only | -| `foo_not_ends_with` | `String` | Does not end with | `postgresql` only | -| `foo_i` | `String` | Equals (case insensitive) | `postgresql` only | -| `foo_not_i` | `String` | Does not equal (case insensitive) | `postgresql` only | -| `foo_contains_i` | `String` | Contains (case insensitive) | `postgresql` only | -| `foo_not_contains_i` | `String` | Does not contain (case insensitive) | `postgresql` only | -| `foo_starts_with_i` | `String` | Starts with (case insensitive) | `postgresql` only | -| `foo_not_starts_with_i` | `String` | Does not start with (case insensitive) | `postgresql` only | -| `foo_ends_with_i` | `String` | Ends with (case insensitive) | `postgresql` only | -| `foo_not_ends_with_i` | `String` | Does not end with (case insensitive) | `postgresql` only | -| `foo_in` | `[String]` | Is in the array | -| `foo_not_in` | `[String]` | Is not in the array | +| **Filter name** | **Type** | **Description** | **Notes** | +| --------------- | ---------------------------------------- | ----------------------------------------------------- | --------- | +| `equals` | `String` | Equals | +| `lt` | `String` | Less than | +| `lte` | `String` | Less than or equal | +| `gt` | `String` | Greater than | +| `gte` | `String` | Greater than or equal | +| `contains` | `String` | Contains | [1] | +| `startsWith` | `String` | Starts with | [1] | +| `endsWith` | `String` | Ends with | [1] | +| `in` | `[String!]` | Is in the array | +| `notIn` | `[String!]` | Is not in the array | +| `mode` | `QueryMode` (`default` or `insensitive`) | Whether the filters should be case insensitive or not | [2] | +| `not` | `NestedStringNullableFilter` | Does not match the inner filter | + +#### Notes + +- [1] Will follow the setting of the `mode` on `postgresql` and will be case insensitive but only for ASCII characters on `sqlite` +- [2] `postgresql` only ### timestamp -| **Filter name** | **Type** | **Description** | -| --------------- | ---------- | --------------------- | -| `foo` | `String` | Equals | -| `foo_not` | `String` | Does not equal | -| `foo_lt` | `String` | Less than | -| `foo_lte` | `String` | Less than or equal | -| `foo_gt` | `String` | Greater than | -| `foo_gte` | `String` | Greater than or equal | -| `foo_in` | `[String]` | Is in the array | -| `foo_not_in` | `[String]` | Is not in the array | +| **Filter name** | **Type** | **Description** | +| --------------- | ------------------------ | ------------------------------- | +| `equals` | `String` | Equals | +| `lt` | `String` | Less than | +| `lte` | `String` | Less than or equal | +| `gt` | `String` | Greater than | +| `gte` | `String` | Greater than or equal | +| `in` | `[String!]` | Is in the array | +| `notIn` | `[String!]` | Is not in the array | +| `not` | `DateTimeNullableFilter` | Does not match the inner filter | ## Relationship type @@ -107,31 +106,30 @@ The `json` field type does not support filters. | **Filter name** | **Type** | **Description** | | --------------- | --------------- | ------------------------------------------ | -| `foos_every` | `FooWhereInput` | All related items match the nested filter | -| `foos_some` | `FooWhereInput` | Some related items match the nested filter | -| `foos_none` | `FooWhereInput` | No related items match the nested filter | +| `every` | `FooWhereInput` | All related items match the nested filter | +| `some` | `FooWhereInput` | Some related items match the nested filter | +| `none` | `FooWhereInput` | No related items match the nested filter | #### many: false | **Filter name** | **Type** | **Description** | | --------------- | --------------- | ------------------------- | | `foo` | `FooWhereInput` | Matches the nested filter | -| `foo_is_null` | `Boolean` | Is `null` | ## Index types ### autoIncrement -| **Filter name** | **Type** | **Description** | -| --------------- | -------- | --------------------- | -| `id` | `ID` | Equals | -| `id_not` | `ID` | Does not equal | -| `id_lt` | `ID` | Less than | -| `id_lte` | `ID` | Less than or equal | -| `id_gt` | `ID` | Greater than | -| `id_gte` | `ID` | Greater than or equal | -| `id_in` | `[ID]` | Is in the array | -| `id_not_in` | `[ID]` | Is not in the array | +| **Filter name** | **Type** | **Description** | +| --------------- | ------------------- | ------------------------------- | +| `equals` | `Int` | Equals | +| `lt` | `Int` | Less than | +| `lte` | `Int` | Less than or equal | +| `gt` | `Int` | Greater than | +| `gte` | `Int` | Greater than or equal | +| `in` | `[Int!]` | Is in the array | +| `notIn` | `[Int!]` | Is not in the array | +| `not` | `IntNullableFilter` | Does not match the inner filter | ## Virtual type diff --git a/docs/pages/docs/apis/graphql.mdx b/docs/pages/docs/apis/graphql.mdx index 4299ec7f2b2..37c9099195d 100644 --- a/docs/pages/docs/apis/graphql.mdx +++ b/docs/pages/docs/apis/graphql.mdx @@ -24,18 +24,14 @@ This system will generate the following GraphQL API. ```graphql type Query { - User(where: UserWhereUniqueInput!): User - allUsers(where: UserWhereInput, search: String, orderBy: [UserOrderByInput!]! = [], first: Int, skip: Int! = 0): [User] - _allUsersMeta(where: UserWhereInput, search: String, orderBy: [UserOrderByInput!]! = [], first: Int, skip: Int! = 0): _QueryMeta -} - -type Mutation { - createUser(data: UserCreateInput): User - createUsers(data: [UsersCreateInput]): [User] - updateUser(id: ID!, data: UserUpdateInput): User - updateUsers(data: [UsersUpdateInput]): [User] - deleteUser(id: ID!): User - deleteUsers(ids: [ID!]): [User] + users( + where: UserWhereInput! = {} + orderBy: [UserOrderByInput!]! = [] + take: Int + skip: Int! = 0 + ): [User!] + user(where: UserWhereUniqueInput!): User + usersCount(where: UserWhereInput! = {}): Int } type User { @@ -43,39 +39,61 @@ type User { name: String } -input UserWhereInput { - AND: [UserWhereInput!] - OR: [UserWhereInput!] +input UserWhereUniqueInput { id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_starts_with: String - name_not_starts_with: String - name_ends_with: String - name_not_ends_with: String - name_i: String - name_not_i: String - name_contains_i: String - name_not_contains_i: String - name_starts_with_i: String - name_not_starts_with_i: String - name_ends_with_i: String - name_not_ends_with_i: String - name_in: [String] - name_not_in: [String] } -input UserWhereUniqueInput { - id: ID +input UserWhereInput { + AND: [UserWhereInput!] + OR: [UserWhereInput!] + NOT: [UserWhereInput!] + id: IDFilter + name: StringNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + mode: QueryMode + not: NestedStringNullableFilter +} + +enum QueryMode { + default + insensitive +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter } input UserOrderByInput { @@ -88,83 +106,116 @@ enum OrderDirection { desc } +type Mutation { + createUser(data: UserCreateInput!): User + createUsers(data: [UserCreateInput!]!): [User] + updateUser(where: UserWhereUniqueInput!, data: UserUpdateInput!): User + updateUsers(data: [UserUpdateArgs!]!): [User] + deleteUser(where: UserWhereUniqueInput!): User + deleteUsers(where: [UserWhereUniqueInput!]!): [User] +} + input UserUpdateInput { name: String } -input UsersUpdateInput { - id: ID! - data: UserUpdateInput +input UserUpdateArgs { + where: UserWhereUniqueInput! + data: UserUpdateInput! } input UserCreateInput { name: String } - -input UsersCreateInput { - data: UserCreateInput -} - -type _QueryMeta { - count: Int -} ``` ## Queries -### User +### user ```graphql type Query { - User(where: UserWhereUniqueInput!): User -} - -input UserWhereUniqueInput { - id: ID + user(where: UserWhereUniqueInput!): User } type User { id: ID! name: String } + +input UserWhereUniqueInput { + id: ID +} ``` -### allUsers +### users ```graphql type Query { - allUsers(where: UserWhereInput, search: String, orderBy: [UserOrderByInput!]! = [], first: Int, skip: Int! = 0): [User] + users( + where: UserWhereInput! = {} + orderBy: [UserOrderByInput!]! = [] + take: Int + skip: Int! = 0 + ): [User!] +} + +type User { + id: ID! + name: String } input UserWhereInput { AND: [UserWhereInput!] OR: [UserWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_starts_with: String - name_not_starts_with: String - name_ends_with: String - name_not_ends_with: String - name_i: String - name_not_i: String - name_contains_i: String - name_not_contains_i: String - name_starts_with_i: String - name_not_starts_with_i: String - name_ends_with_i: String - name_not_ends_with_i: String - name_in: [String] - name_not_in: [String] + NOT: [UserWhereInput!] + id: IDFilter + name: StringNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + mode: QueryMode + not: NestedStringNullableFilter +} + +enum QueryMode { + default + insensitive +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter } input UserOrderByInput { @@ -176,63 +227,66 @@ enum OrderDirection { asc desc } - -type User { - id: ID! - name: String -} ``` ### usersCount ```graphql type Query { - usersCount(where: UserWhereInput! = {}): Int! + usersCount(where: UserWhereInput! = {}): Int } input UserWhereInput { AND: [UserWhereInput!] OR: [UserWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_starts_with: String - name_not_starts_with: String - name_ends_with: String - name_not_ends_with: String - name_i: String - name_not_i: String - name_contains_i: String - name_not_contains_i: String - name_starts_with_i: String - name_not_starts_with_i: String - name_ends_with_i: String - name_not_ends_with_i: String - name_in: [String] - name_not_in: [String] -} - -input UserOrderByInput { - id: OrderDirection - name: OrderDirection -} - -enum OrderDirection { - asc - desc -} - -type _QueryMeta { - count: Int + NOT: [UserWhereInput!] + id: IDFilter + name: StringNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + mode: QueryMode + not: NestedStringNullableFilter +} + +enum QueryMode { + default + insensitive +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter } ``` @@ -242,7 +296,7 @@ type _QueryMeta { ```graphql type Mutation { - createUser(data: UserCreateInput): User + createUser(data: UserCreateInput!): User } input UserCreateInput { @@ -259,11 +313,7 @@ type User { ```graphql type Mutation { - createUsers(data: [UsersCreateInput]): [User] -} - -input UsersCreateInput { - data: UserCreateInput + createUsers(data: [UserCreateInput!]!): [User] } input UserCreateInput { @@ -280,7 +330,11 @@ type User { ```graphql type Mutation { - updateUser(id: ID!, data: UserUpdateInput): User + updateUser(where: UserWhereUniqueInput!, data: UserUpdateInput!): User +} + +input UserWhereUniqueInput { + id: ID } input UserUpdateInput { @@ -297,12 +351,16 @@ type User { ```graphql type Mutation { - updateUsers(data: [UsersUpdateInput]): [User] + updateUsers(data: [UserUpdateArgs!]!): [User] } -input UsersUpdateInput { - id: ID! - data: UserUpdateInput +input UserUpdateArgs { + where: UserWhereUniqueInput! + data: UserUpdateInput! +} + +input UserWhereUniqueInput { + id: ID } input UserUpdateInput { @@ -319,7 +377,11 @@ type User { ```graphql type Mutation { - deleteUser(id: ID!): User + deleteUser(where: UserWhereUniqueInput!): User +} + +input UserWhereUniqueInput { + id: ID } type User { @@ -332,7 +394,11 @@ type User { ```graphql type Mutation { - deleteUsers(ids: [ID!]): [User] + deleteUsers(ids: [UserWhereUniqueInput!]!): [User] +} + +input UserWhereUniqueInput { + id: ID } type User { diff --git a/docs/pages/docs/apis/list-items.mdx b/docs/pages/docs/apis/list-items.mdx index f8559c0d52a..12630497fd1 100644 --- a/docs/pages/docs/apis/list-items.mdx +++ b/docs/pages/docs/apis/list-items.mdx @@ -8,14 +8,14 @@ For each list in your system the following API is available at `context.lists. **Note:** This feature will improve over time. It has been released ahead of time to unblock developers. We are working on improving support for other styling frameworks besides Emotion and will be making it easier by exporting more Admin UI components in the near future. +In this guide we'll show you how to add custom pages to the Keystone Admin UI. +As the Admin UI is built on top of [Next.js](https://nextjs.org/docs/basic-features/pages), it exposes the same pages directory for adding custom pages. To create a custom page, ensure that the `admin/pages` directory exists in the root of your Keystone Project. Much like with Next.js, all files in this directory will be added as routes to the Admin UI. The default export of every file in this directory is expected to be a valid React Component rendered out as the contents of the route. ```tsx -// admin/pages/MyCustomPage.tsx -export default function () { +// admin/pages/custom-page.tsx +export default function CustomPage () { return ( <>

    This is a custom Admin UI Page

    -

    It can be accessed via the route '/MyCustomPage'

    +

    It can be accessed via the route /custom-page

    ) } ``` -If you have styling constraints, we recommend using the jsx export from the `@keystone-ui/core` package, as this will ensure that the version of emotion you're using conforms with the version of emotion used internally within Keystone. +x> **Not all Next.js exports are available:** Keystone **only** supports the page component as a default export in the pages directory. This means that unlike with Next, auxillary exports such as `getStaticProps` and `getServerProps` are not supported. + +With this in place, we now have a nice simple custom Admin UI page at `http://localhost:3000/custom-page`. +![example of a simple custom-page in the Admin UI](/assets/guides/custom-admin-ui-pages/simple-custom-page.png) + +## Adding Admin UI layout + +At the moment this page is pretty bare bones. We want our page to look more like an Admin UI page. +Keystone helps us do this via the `PageContainer` component exported from `@keystone-next/keystone/admin-ui/components`. + +The `PageContainer` component takes a `header` prop, which is expected to be a `ReactElement`. +This `header` prop is rendered out as the page title at the top of the page. + +```tsx +// admin/pages/custom-page.tsx +import { PageContainer } from '@keystone-next/keystone/admin-ui/components'; +export default function CustomPage () { + return ( + +

    This is a custom Admin UI Page

    +

    It can be accessed via the route /custom-page

    +
    + ) +} +``` + +With the above snippet, our custom page looks a lot more like the other pages in the Admin UI. +![example of the custom Admin UI page with the PageContainer component](/assets/guides/custom-admin-ui-pages/custom-page-w-page-container.png) + +There's still a problem though, the header doesn't look right. If we compare the header of our custom page with the header for the Dashboard, there's quite a bit of difference in the styling and font-weight. +![example of the Dashboard header element](/assets/guides/custom-admin-ui-pages/header-prop.png) + +Keystone pages leverage the `Heading` component from the `@keystone-ui/core` package to style the header, so let's use this to give our header the same styling. + +```tsx +// admin/pages/custom-page.tsx +import { PageContainer } from '@keystone-next/keystone/admin-ui/components'; +import { Heading } from '@keystone-ui/core'; + +export default function CustomPage () { + return ( + Custom Page}> +

    This is a custom Admin UI Page

    +

    It can be accessed via the route `/custom-page`

    +
    + ) +} +``` + +Much better, our custom page looks and feels like an Admin UI page now. +![custom page with correctly styled header](/assets/guides/custom-admin-ui-pages/custom-page-with-styled-header.png) + +## Custom route in Admin UI Navigation + +Yes, our custom page is looking pretty great, and much more like an Admin UI page, but it's not visible as a navigation item. +We can fix this by adding a custom Navigation component with a route pointing to our custom page. + +First add the following files to the `/admin` directory in the root of your Keystone project. + +```tsx +// admin/config.ts +import type { AdminConfig } from '@keystone-next/types'; +import { CustomNavigation } from './components/CustomNavigation'; +export const components: AdminConfig['components']= { + Navigation: CustomNavigation +}; +``` + +```tsx +// admin/components/CustomNavigation.tsx +import { NavigationContainer, ListNavItems, NavItem } from '@keystone-next/keystone/admin-ui/components'; +import type { NavigationProps } from '@keystone-next/keystone/admin-ui/components'; +export function CustomNavigation({ lists, authenticatedItem }: NavigationProps) { + return ( + + Dashboard + + + ) +} +``` + +!> You will need to restart your Keystone system after adding `admin/config.ts` for the custom Navigation component to be loaded. + +!> If you're interested in more details on creating a custom Navigation component check out the [Custom Admin UI Navigation](/docs/guides/custom-admin-ui-navigation) guide. + +Lastly we'll add our new route to the newly created `CustomNavigation` component. ```tsx -// admin/pages/MyCustomPage.tsx +// admin/components/CustomNavigation.tsx +import { NavigationContainer, ListNavItems, NavItem } from '@keystone-next/keystone/admin-ui/components'; +import type { NavigationProps } from '@keystone-next/keystone/admin-ui/components'; +export function CustomNavigation({ lists, authenticatedItem }: NavigationProps) { + return ( + + Dashboard + + Custom Page + + ) +} +``` + +!> Under the hood Keystone's Admin UI is powered by Next.js, so the route to our custom page is the filename of our custom page component. In this case it's `/custom-page`. + +With all that in place, our custom Admin UI page is now navigable from the Admin UI Navigation component, and we can access it from other pages in the Admin UI. +![completed custom Admin UI page](/assets/guides/custom-admin-ui-pages/custom-page-completed.png) + +## Styling + +There are other styling considerations when adding a custom page to the Admin UI that go beyond making it _look_ and _feel_ like an Admin UI page. +For this, we recommend using the `jsx` runtime export from the `@keystone-ui/core` package, as this will ensure that the version of [emotion](https://emotion.sh/docs/introduction) you're using conforms with the version of emotion used internally used by Keystone. + +The snippet below uses the emotion `jsx` runtime exported from `@keystone-ui/core` to help add some basic allignment and layout styling to the contents of our Admin UI custom page. + +```tsx +// admin/pages/custom-page.tsx /** @jsxRuntime classic */ /** @jsx jsx */ + import { jsx } from '@keystone-ui/core'; -export default function () { +import { PageContainer } from '@keystone-next/keystone/admin-ui/components'; +import { Heading } from '@keystone-ui/core'; + +export default function CustomPage () { return ( - <> + + Custom Page + + )}>

    This is a custom Admin UI Page

    -

    It can be accessed via the route '/MyCustomPage'

    - + width: '100%', + textAlign: 'center', + }}> + This is a custom Admin UI Page + +

    + It can be accessed via the route /custom-page +

    +
    ) } ``` -Of course this is purely a recommendation, if you would prefer to roll your own css-in-js solution in with your custom component please feel free to! Although this may require additional configuration outside of the scope of this guide. - -x> **Not all Next.js exports are available:** Keystone **only** supports the page component as a default export in the pages directory. This means that unlike with Next, auxillary exports such as `getStaticProps` and `getServerProps` are not supported. +Using `emotion` for styling is purely a recommendation, if you would prefer to use another css-in-js or css solution for your custom component please feel free to. This may require additional configuration currently outside of the scope of this guide. export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/custom-fields.mdx b/docs/pages/docs/guides/custom-fields.mdx index 5861be45495..8ccae1ba737 100644 --- a/docs/pages/docs/guides/custom-fields.mdx +++ b/docs/pages/docs/guides/custom-fields.mdx @@ -29,7 +29,7 @@ import { fieldType, schema, orderDirectionEnum, - legacyFilters, + filters, FieldDefaultValue, } from '@keystone-next/types'; @@ -58,6 +58,7 @@ export const myInt = })({ ...config, input: { + where: { arg: schema.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon }, create: { arg: schema.arg({ type: schema.Int }) }, update: { arg: schema.arg({ type: schema.Int }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, @@ -65,18 +66,6 @@ export const myInt = output: schema.field({ type: schema.Int }), views: require.resolve('./view.tsx'), __legacy: { - filters: { - fields: { - ...legacyFilters.fields.equalityInputFields(meta.fieldKey, schema.Int), - ...legacyFilters.fields.orderingInputFields(meta.fieldKey, schema.Int), - ...legacyFilters.fields.inInputFields(meta.fieldKey, schema.Int), - }, - impls: { - ...legacyFilters.impls.equalityConditions(meta.fieldKey), - ...legacyFilters.impls.orderingConditions(meta.fieldKey), - ...legacyFilters.impls.inConditions(meta.fieldKey), - }, - }, isRequired, defaultValue, }, @@ -94,6 +83,7 @@ The `input` object defines the GraphQL inputs for the field type. ```ts input: { + where: { arg: schema.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon }, create: { arg: schema.arg({ type: schema.Int }) }, update: { arg: schema.arg({ type: schema.Int }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, @@ -104,6 +94,7 @@ You can also provide resolvers to transform the value coming from GraphQL into t ```ts input: { + where: { arg: schema.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon }, create: { arg: schema.arg({ type: schema.Int }), resolve: (val, context) => val }, update: { arg: schema.arg({ type: schema.Int }), resolve: (val, context) => val }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }), resolve: (val, context) => val }, diff --git a/docs/pages/docs/guides/document-fields.mdx b/docs/pages/docs/guides/document-fields.mdx index fa27c56962e..9b2b5da7aa7 100644 --- a/docs/pages/docs/guides/document-fields.mdx +++ b/docs/pages/docs/guides/document-fields.mdx @@ -97,11 +97,11 @@ type Post_content_DocumentField { } ``` -To query the content we can run the following GraphQL query, which will return the JSON representation of the content in `allPosts.content.document`. +To query the content we can run the following GraphQL query, which will return the JSON representation of the content in `posts.content.document`. ```graphql query { - allPosts { + posts { content { document } @@ -219,7 +219,7 @@ If you query for the document, the inline relationship block will include the ID "id": "ckqk4hkcg0030f5mu6le6xydu" }, "relationship": "mention", - "children": [{ "text": "" } + "children": [{ "text": "" }] }, ... ``` @@ -229,7 +229,7 @@ To obtain more useful data, we can pass the `hydrateRelationships: true` option ```graphql query { - allPosts { + posts { content { document(hydrateRelationships: true) } diff --git a/docs/pages/docs/guides/filters.mdx b/docs/pages/docs/guides/filters.mdx index 99420a8b3de..869cb8acfe3 100644 --- a/docs/pages/docs/guides/filters.mdx +++ b/docs/pages/docs/guides/filters.mdx @@ -10,25 +10,25 @@ This guide will show you how to use filters to get data you need. ## Scalar Filters -If we want to find all the `Tasks` in our system, we can use the query `allTasks()`. +If we want to find all the `Tasks` in our system, we can use the query `tasks()`. ```graphql { - allTasks { + tasks { id label } } ``` -In general we don't want to grab all the tasks at once, but want to find a particular set of tasks which match a certain condition. -In Keystone we can do this by passing a `where` argument to the `allTasks()` query. +In general we don't want to grab all the tasks at once, but we want to find a particular set of tasks which match a certain condition. +In Keystone we can do this by passing a `where` argument to the `tasks()` query. If we want to find all the tasks with a label equal to `"Hello"` we can write: ```graphql { - allTasks(where: { label: "Hello" }) { + tasks(where: { label: { equals: "Hello" } }) { id label } @@ -39,7 +39,7 @@ Keystone provides a wide range of different filters. If we want to find all thos ```graphql { - allTasks(where: { label_not: "Hello" }) { + tasks(where: { label: { not: { equals: "Hello" } } }) { id label } @@ -50,7 +50,7 @@ The `text()` field type also supports searching for sub-strings within a field: ```graphql { - allTasks(where: { label_contains: "He" }) { + tasks(where: { label: { contains: "He" } }) { id label } @@ -61,7 +61,7 @@ Different field types support different filters. The field `finishBy: timestamp( ```graphql { - allTasks(where: { finishBy_gt: "2022-01-01T00:00:00.000Z" }) { + tasks(where: { finishBy: { gt: "2022-01-01T00:00:00.000Z" } }) { id label } @@ -74,9 +74,9 @@ For more complex queries, you can combine multiple filters, and only those items ```graphql { - allTasks(where: { - label_contains: "He", - finishBy_gt: "2022-01-01T00:00:00.000Z" + tasks(where: { + label: { contains: "He" }, + finishBy: { gt: "2022-01-01T00:00:00.000Z" } }) { id label @@ -94,9 +94,9 @@ The `AND` operater accepts a list of sub-filters, and will only return those ite ```graphql { - allTasks(where: { AND: [ - { label_contains: "H" }, - { label_contains: "ll" } + tasks(where: { AND: [ + { label: { contains: "H" } }, + { label: { contains: "ll" } } ] }) { id label @@ -110,9 +110,9 @@ The `OR` operater accepts a list of sub-filters, and will only return those item ```graphql { - allTasks(where: { OR: [ - { label_contains: "H" }, - { label_contains: "ll" } + tasks(where: { OR: [ + { label: { contains: "H" } }, + { label: { contains: "ll" } } ] }) { id label @@ -120,6 +120,22 @@ The `OR` operater accepts a list of sub-filters, and will only return those item } ``` +### NOT + +The `NOT` operater accepts a list of sub-filters, and will only return those items which don't match the conditions. +You'll generally only pass a single filter to `NOT` rather than a list but if you do a pass a list, they're `AND`ed together. + +```graphql +{ + tasks(where: { NOT: { + label: { contains: "H" } + } }) { + id + label + } +} +``` + ## Relationship Filters As well as filtering by scalar fields, you can also filter against relationship fields. @@ -133,7 +149,7 @@ For example, to find all the tasks where the task is assigned to a used named `" ```graphql { - allTasks(where: { assignedTo: { name: "Alice" } }) { + tasks(where: { assignedTo: { name: { equals: "Alice" } } }) { id label } @@ -142,13 +158,13 @@ For example, to find all the tasks where the task is assigned to a used named `" ### Many -If you have `many: true` configured on the relationship field, then you can find items based on whether `some`, `none`, or `all` of the related items match a `where` filter using the fields from the related list. +If you have `many: true` configured on the relationship field, then you can find items based on whether `some`, `none`, or `every` of the related items match a `where` filter using the fields from the related list. For example, to find all the people which have `some` posts with the label `"Hello"`, we can run the following query: ```graphql { - allPeople(where: { tasks_some: { label: "Hello" } }) { + people(where: { tasks: { some: { label: { equals: "Hello" } } } }) { id name } diff --git a/docs/pages/docs/guides/hooks.mdx b/docs/pages/docs/guides/hooks.mdx index b76871e50a1..fbfb9eff43e 100644 --- a/docs/pages/docs/guides/hooks.mdx +++ b/docs/pages/docs/guides/hooks.mdx @@ -27,7 +27,7 @@ export default config({ hooks: { afterChange: ({ operation, updatedItem }) => { if (operation === 'create') { - console.log('New user created. Name: ${updatedItem.name}, Email: ${updatedItem.email}); + console.log(`New user created. Name: ${updatedItem.name}, Email: ${updatedItem.email}`); } } }, @@ -209,7 +209,7 @@ export default config({ validateInput: ({ addValidationError, resolvedData, fieldPath }) => { const email = resolvedData[fieldPath]; if (email !== undefined && email !== null && !email.includes('@')) { - addValidationError('The email address ${email} provided for the field ${fieldPath} must contain an '@' character); + addValidationError(`The email address ${email} provided for the field ${fieldPath} must contain an '@' character`); } }, }, diff --git a/docs/pages/docs/guides/relationships.mdx b/docs/pages/docs/guides/relationships.mdx index 0708baa3d5b..cd5ac552c5e 100644 --- a/docs/pages/docs/guides/relationships.mdx +++ b/docs/pages/docs/guides/relationships.mdx @@ -54,8 +54,8 @@ In Keystone it’s possible to define relationships from one, or both sides of t Our example above is one-sided: the `Post` list relates to the `User` list via the `authors` field. This kind of relationship will let us query for the `authors` of a post in our GraphQL API like so: ```graphql -Query { - allPosts { +query { + posts { title content authors { @@ -86,8 +86,8 @@ export default config({ fields: { title: text(), content: text(), - // relates authors to posts - authors: relationship({ ref: 'User.posts', many: true }), + // relates authors to posts + authors: relationship({ ref: 'User.posts', many: true }), }, }), }), @@ -101,8 +101,8 @@ In the example above we added a `posts` field to the `User` list, and changed th Now that our relationship is two-sided we can query all the posts written by each user like so: ```graphql -Query { - allUsers { +query { + users { name posts { title diff --git a/docs/pages/docs/guides/testing.mdx b/docs/pages/docs/guides/testing.mdx index 0436857190e..e70afacb7e0 100644 --- a/docs/pages/docs/guides/testing.mdx +++ b/docs/pages/docs/guides/testing.mdx @@ -109,8 +109,8 @@ runner(async ({ context }) => { // Create some users const [alice, bob] = await context.lists.Person.createMany({ data: [ - { data: { name: 'Alice', email: 'alice@example.com', password: 'super-secret' } }, - { data: { name: 'Bob', email: 'bob@example.com', password: 'super-secret' } }, + { name: 'Alice', email: 'alice@example.com', password: 'super-secret' }, + { name: 'Bob', email: 'bob@example.com', password: 'super-secret' }, ], }); @@ -129,7 +129,7 @@ runner(async ({ context }) => { .withSession({ itemId: bob.id, data: {} }) .graphql.raw({ query: `mutation update($id: ID!) { - updateTask(id: $id data: { isComplete: true }) { + updateTask(where: { id: $id }, data: { isComplete: true }) { id } }`, diff --git a/docs/pages/docs/guides/virtual-fields.mdx b/docs/pages/docs/guides/virtual-fields.mdx index 4eae2e7be9a..e1a697ee4f7 100644 --- a/docs/pages/docs/guides/virtual-fields.mdx +++ b/docs/pages/docs/guides/virtual-fields.mdx @@ -42,7 +42,7 @@ We can now run a GraphQL query and request the `hello` field on one of our `Exam ```graphql { - Example(where: { id: "1" }) { + example(where: { id: "1" }) { id hello } @@ -52,7 +52,7 @@ We can now run a GraphQL query and request the `hello` field on one of our `Exam which gives the response: ```javascript -{ Example: { id: "1", hello: "Hello, world! } } +{ example: { id: "1", hello: "Hello, world! } } ``` The value of `hello` is generated from the `resolve` function, which returns the string `"Hello, world!"`. @@ -170,9 +170,9 @@ type Post { We can now perform the following query to get all the excerpts without over-fetching on the client. -``` +```graphql { - allPosts { + posts { id excerpt(length: 100) } @@ -284,7 +284,7 @@ export const lists = createSchema({ where: { id: item.id.toString() }, query: `posts( orderBy: { publishDate: desc } - first: 1 + take: 1 ) { id }`, }); if (posts.length > 0) { diff --git a/docs/pages/ds.tsx b/docs/pages/ds.tsx index ce07fabddf5..b47d461d437 100644 --- a/docs/pages/ds.tsx +++ b/docs/pages/ds.tsx @@ -576,7 +576,7 @@ Some code...`} {`{ - allPosts (first: 2, where: { title_contains: "content" }) { + posts(take: 2, where: { title: { contains: "content" } }) { title author { name @@ -587,7 +587,7 @@ Some code...`} {`{ "data": { - "allPosts": [ + "posts": [ { "title": "How structured content gives you superpowers", "author": { diff --git a/docs/pages/index.tsx b/docs/pages/index.tsx index c4a6f18f522..47f7c55c695 100644 --- a/docs/pages/index.tsx +++ b/docs/pages/index.tsx @@ -565,8 +565,8 @@ export const lists = createSchema({ {`{ - allPosts (first: 2, where: { - title_contains: "content" + posts(take: 2, where: { + title: { contains: "content" } }) { title author { @@ -580,7 +580,7 @@ export const lists = createSchema({ {`{ "data": { - "allPosts": [ + "posts": [ { "title": "How structured content gives you superpowers", "author": { diff --git a/docs/pages/updates/new-graphql-api.mdx b/docs/pages/updates/new-graphql-api.mdx new file mode 100644 index 00000000000..47c6b0afc89 --- /dev/null +++ b/docs/pages/updates/new-graphql-api.mdx @@ -0,0 +1,405 @@ +import { Markdown, getServerSideProps } from '../../components/Markdown'; +import { Emoji } from '../../components/primitives/Emoji'; + +# A new & improved GraphQL API + +As we move closer to a _General Availability_ release for Keystone 6, we've taken the opportunity to make the experience of working with Keystone’s GraphQL API easier to program and reason about. + +This guide describes the improvements we've made, and walks you through the steps you need to take to upgrade your Keystone projects. + +!> If you get stuck, or want to discuss these changes, reach out to us in the [Keystone community slack](https://community.keystonejs.com/). + +## Example Schema + +To illustrate the changes, we’ll refer to the `Task` list in the following schema, from our [Task Manager](https://github.com/keystonejs/keystone/tree/master/examples/task-manager) example project. + +``` +export const lists = createSchema({ + Task: list({ + fields: { + label: text({ isRequired: true }), + priority: select({ + dataType: 'enum', + options: [ + { label: 'Low', value: 'low' }, + { label: 'Medium', value: 'medium' }, + { label: 'High', value: 'high' }, + ], + }), + isComplete: checkbox(), + assignedTo: relationship({ ref: 'Person.tasks', many: false }), + tags: relationship({ ref: 'Tag', many: true }), + finishBy: timestamp(), + }, + }), + Person: list({ + fields: { + name: text({ isRequired: true }), + tasks: relationship({ ref: 'Task.assignedTo', many: true }), + }, + }), + Tag: list({ + fields: { + name: text(), + }, + }), +}); +``` + +## Query + +We’ve changed the names of our top-level queries easier understand. +We also took this opportunity to remove deprecated and unused legacy features. + +### Changes + +| Action | Item | Before | After | +| ---------------------------------------------------- | -------------------------------------------------------------- | ----------------- | -------------- | +|   Renamed | Generated query for a single item | `Task()` | `task()` | +|   Renamed | Generated query for multiple items | `allTasks()` | `tasks()` | +|   Renamed | Pagination argument to align with arguments provided by Prisma | `first` | `take` | +|   Removed | Legacy `search` argument | `search` | `where` | +|   Removed | Deprecated `sortBy` argument | `sortBy` | `orderBy` | +|   Removed | Deprecated `_allTasksMeta` query | `_allTasksMeta()` | `tasksCount()` | + +!> We’ve also changed the format of filters used in `TaskWhereInput`. See [Filter changes](#filters) for more details. + +### Example + +```graphql +// Before + +type Query { + allTasks( + where: TaskWhereInput! = {} + search: String + sortBy: [SortTasksBy!] + @deprecated(reason: "sortBy has been deprecated in favour of orderBy") + orderBy: [TaskOrderByInput!]! = [] + first: Int + skip: Int! = 0 + ): [Task!] + Task(where: TaskWhereUniqueInput!): Task + _allTasksMeta( + where: TaskWhereInput! = {} + search: String + sortBy: [SortTasksBy!] + @deprecated(reason: "sortBy has been deprecated in favour of orderBy") + orderBy: [TaskOrderByInput!]! = [] + first: Int + skip: Int! = 0 + ): _QueryMeta + @deprecated( + reason: "This query will be removed in a future version. Please use tasksCount instead." + ) + tasksCount(where: TaskWhereInput! = {}): Int + ... +} + +// After +type Query { + tasks( + where: TaskWhereInput! = {} + orderBy: [TaskOrderByInput!]! = [] + take: Int + skip: Int! = 0 + ): [Task!] + task(where: TaskWhereUniqueInput!): Task + tasksCount(where: TaskWhereInput! = {}): Int + ... +} +``` + +## Filters + +The filter arguments used in queries have been updated to accept a filter object for each field, rather than having all the filter options available at the top level. + +An example of a query in the old format is: + +```graphql +allTasks( + where: { + label_starts_with: "Hello", + finishBy_lt: "2022-01-01T00:00:00.000Z", + isComplete: true + } +) { id } +``` + +Using the new filter syntax, this becomes: + +```graphql +tasks( + where: { + label: { startsWith: "Hello" } + finishBy: { lt: "2022-01-01T00:00:00.000Z" } + isComplete: { equals: true } + } +) { id } +``` + +There is a one-to-one correspondence between the old filters and the new filters, so you can make a simple systematic set of changes to bring your filters up to date. + +?> **Note:** The old filter syntax used `{ fieldName: value }` to test for equality. The new syntax requires you to make this explicit, and write `{ fieldName: { equals: value} }`. + +!> See the [Filters Guide](/docs/guides/filters) for a detailed walk through the new filtering syntex. + +!> See the [API docs](/docs/apis/filters) for a comprehensive list of all the new filters for each field type. + +## Mutations + +All generated CRUD mutations have the same names and return types, but their inputs have changed. + +- `update` and `delete` mutations no longer accept `id` or `ids` to indicate which items to update. We now use `where` so you can select the item based on any of its unique fields. +- The **types** used for `create` and `update` mutations [have been updated](#input-types). +- All inputs are now **non-optional**. + +#### Create mutation + +| Before | After | +| ----------------------------------------------- | ------------------------------------------------ | +| `createTask(data: TaskCreateInput): Task` | `createTask(data: TaskCreateInput!): Task` | +| `createTasks(data: [TasksCreateInput]): [Task]` | `createTasks(data: [TaskCreateInput!]!): [Task]` | + +```graphql +// Before +mutation { + createTask(data: { label: "Upgrade keystone" }) { + id + } +} + +mutation { + createTasks( + data: [ + { data: { label: "Upgrade keystone" } } + { data: { label: "Build great products" } } + ] + ) { + id + } +} + +// After +mutation { + createTask(data: { label: "Upgrade keystone" }) { + id + } +} + +mutation { + createTasks( + data: [ + { label: "Upgrade keystone" }, + { label: "Build great products" } + ] + ) { + id + } +} +``` + +#### Update mutation + +| Before | After | +| -------------------------------------------------- | ------------------------------------------------------------------------ | +| `updateTask(id: ID!, data: TaskUpdateInput): Task` | `updateTask(where: TaskWhereUniqueInput!, data: TaskUpdateInput!): Task` | +| `updateTasks(data: [TasksUpdateInput]): [Task]` | `updateTasks(data: [TaskUpdateArgs!]!): [Task]` | + +```graphql +// Before +mutation { + updateTask(id: "cksdyag9w0000pioj44kinqsp", data: { isComplete: true }) { + id + } + + updateTasks( + data: [ + { id: "cksdyaga50007pioj1oc37msr", data: { isComplete: true } } + { id: "cksdyj6wd0000epoj0585uzbq", data: { isComplete: true } } + ] + ) { + id + } +} + +// After +mutation { + updateTask( + where: { id: "cksdyag9w0000pioj44kinqsp" } + data: { isComplete: true } + ) { + id + } + + updateTasks( + data: [ + { where: { id: "cksdyaga50007pioj1oc37msr" }, data: { isComplete: true } } + { where: { id: "cksdyj6wd0000epoj0585uzbq" }, data: { isComplete: true } } + ] + ) { + id + } +} +``` + +#### Delete mutation + +| Before | After | +| --------------------------------- | ------------------------------------------------------ | +| `deleteTask(id: ID!): Task` | `deleteTask(where: TaskWhereUniqueInput!): Task` | +| `deleteTasks(ids: [ID!]): [Task]` | `deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]` | + +```graphql +// Before +mutation { + deleteTask(id: "cksdyaga50007pioj1oc37msr") { + id + } + + deleteTasks(ids: ["cksdyjrbj0007epojilbv3d6k", "cksdyjrbp0014epoja2uddwl1"]) { + id + } +} + +// After +mutation { + deleteTask(where: { id: "cksdyag9w0000pioj44kinqsp" }) { + id + } + + deleteTasks( + where: [ + { id: "ckrlp28lf001908lu9tyzxhuq" } + { id: "ckroflp7h0019t9lulhw6pggp" } + ] + ) { + id + } +} +``` + +## Input Types + +We’ve updated the input types used for relationship fields in `update` and `create` operations, removing obsolete options and making the syntax between the two operations easier to differentiate. + +- There are now separate types for `create` and `update` operations. +- Inputs for `create` operations no longer support the `disconnect` or `disconnectAll` options. These options didn't do anything during a `create` operation in the previous API. +- For to-one relationships, the `disconnect` option is now a `Boolean`, rather than accepting a unique input. If you only have one related item, there's no need to specify its value when disconnecting it. +- For to-many relationships, the `disconnectAll` operation has been removed in favour of a new `set` operation, which allows you to explicitly set the connected items. + You can use `{ set: [] }` to achieve the same results as the old `{ disconnectAll: true }`. + +### Example + +```graphql +// Before + +input TasksUpdateInput { + id: ID! + data: TaskUpdateInput +} + +input TaskUpdateInput { + label: String + priority: TaskPriorityType + isComplete: Boolean + assignedTo: PersonRelateToOneInput + tags: TagRelateToManyInput + finishBy: String +} + +input TasksCreateInput { + data: TaskCreateInput +} + +input TaskCreateInput { + label: String + priority: TaskPriorityType + isComplete: Boolean + assignedTo: PersonRelateToOneInput + tags: TagRelateToManyInput + finishBy: String +} + +input PersonRelateToOneInput { + create: PersonCreateInput + connect: PersonWhereUniqueInput + disconnect: PersonWhereUniqueInput + disconnectAll: Boolean +} + +input TagRelateToManyInput { + create: [TagCreateInput] + connect: [TagWhereUniqueInput] + disconnect: [TagWhereUniqueInput] + disconnectAll: Boolean +} + +// After + +input TaskUpdateArgs { + where: TaskWhereUniqueInput! + data: TaskUpdateInput! +} + +input TaskUpdateInput { + label: String + priority: TaskPriorityType + isComplete: Boolean + assignedTo: PersonRelateToOneForUpdateInput + tags: TagRelateToManyForUpdateInput + finishBy: String +} + +input TaskCreateInput { + label: String + priority: TaskPriorityType + isComplete: Boolean + assignedTo: PersonRelateToOneForCreateInput + tags: TagRelateToManyForCreateInput + finishBy: String +} + +input PersonRelateToOneForUpdateInput { + create: PersonCreateInput + connect: PersonWhereUniqueInput + disconnect: Boolean +} + +input PersonRelateToOneForCreateInput { + create: PersonCreateInput + connect: PersonWhereUniqueInput +} + +input TagRelateToManyForUpdateInput { + disconnect: [TagWhereUniqueInput!] + set: [TagWhereUniqueInput!] + create: [TagCreateInput!] + connect: [TagWhereUniqueInput!] +} + +input TagRelateToManyForCreateInput { + create: [TagCreateInput!] + connect: [TagWhereUniqueInput!] +} +``` + +## Upgrade Checklist + +While there are a lot of changes to this API, if you approach the upgrade process systematically your experience should be pretty smooth. If you get stuck or have questions, reach out to us in the [Keystone community slack](https://community.keystonejs.com/) to get the help you need. + +?> Before you begin: check that your project doesn't rely on any of the features we've marked as deprecated in this document, or the `search` argument to filters. If you do, apply the recommended substitute. + +1. Update top level queries. Be sure to rename `Task` to `task` and `allTasks` to `tasks` for all your queries. +2. Update filters. Find and replace all the old Keystone filters with their new equivalent. +3. Update mutation arguments to match the new input types. Make sure you replace `{ id: "..."}` with `{where: { id: "..."} }` in your `update` and `delete` operations. +4. Update relationship inputs to `create` and `update` operations. Ensure you've replaced usage of `{ disconnectAll: true }` with `{ set: [] }` in to-many relationships, and have used `{ disconnect: true }` rather than `{ disconnect: { id: "..."} }` in to-one relationships. + +!> Finally, make sure you apply corresponding changes to filters and input arguments when using the [List Items API](docs/apis/list-items). + +--- + +That's everything! While we acknowledge that API changes are an inconvenience, we believe the time spent navigating these upgrades will be offset many times over by a more fun and productive developer experience going forward. + +export default ({ children, ...props }) => {children}; +export { getServerSideProps } diff --git a/docs/public/assets/guides/custom-admin-ui-pages/custom-page-completed.png b/docs/public/assets/guides/custom-admin-ui-pages/custom-page-completed.png new file mode 100644 index 0000000000000000000000000000000000000000..52ba2d2df316bed7556a84ad3bd8be11a0c8ce1f GIT binary patch literal 38028 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 We0tM#fLHi1FnGH9xvXnd++Cc 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 G9yBI0at%1znRI)c+!m*!O?xW!v004N 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 literal 0 HcmV?d00001 diff --git a/docs/public/assets/guides/custom-admin-ui-pages/custom-page-with-styled-header.png b/docs/public/assets/guides/custom-admin-ui-pages/custom-page-with-styled-header.png new file mode 100644 index 0000000000000000000000000000000000000000..80fc2092c07b3911221a621ca433e83416f62367 GIT binary patch literal 34145 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 literal 0 HcmV?d00001 diff --git a/docs/public/assets/guides/custom-admin-ui-pages/header-prop.png b/docs/public/assets/guides/custom-admin-ui-pages/header-prop.png new file mode 100644 index 0000000000000000000000000000000000000000..6da7fde25b8cff061e5603491d2d9cb6c9ab55bb GIT binary patch literal 9690 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

    Ubd)poe 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 { await q({ query, variables: { productId: product2.id } }); await q({ query, variables: { productId: product1.id } }); const result1 = await context.sudo().lists.CartItem.findMany({ - where: { product: { id: product1.id } }, + where: { product: { id: { equals: product1.id } } }, query: 'quantity', }); expect(result1).toHaveLength(1); expect(result1[0].quantity).toEqual(3); const result2 = await context.sudo().lists.CartItem.findMany({ - where: { product: { id: product2.id } }, + where: { product: { id: { equals: product2.id } } }, query: 'quantity', }); expect(result2).toHaveLength(1); diff --git a/examples-staging/embedded-nextjs/CHANGELOG.md b/examples-staging/embedded-nextjs/CHANGELOG.md index cb97e8a55a9..9e03eb1293c 100644 --- a/examples-staging/embedded-nextjs/CHANGELOG.md +++ b/examples-staging/embedded-nextjs/CHANGELOG.md @@ -1,5 +1,13 @@ # @keystone-next/example-next-lite +## 3.0.6 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + ## 3.0.5 ### Patch Changes diff --git a/examples-staging/embedded-nextjs/package.json b/examples-staging/embedded-nextjs/package.json index e09673d4696..54d6edeac8c 100644 --- a/examples-staging/embedded-nextjs/package.json +++ b/examples-staging/embedded-nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-embedded-nextjs", - "version": "3.0.5", + "version": "3.0.6", "private": true, "license": "MIT", "scripts": { @@ -9,8 +9,8 @@ "build": "next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", "dotenv": "^10.0.0", "next": "^10.2.3", "react": "^17.0.2", diff --git a/examples-staging/embedded-nextjs/schema.graphql b/examples-staging/embedded-nextjs/schema.graphql index 4aeb0f138f9..0c723ddb1de 100644 --- a/examples-staging/embedded-nextjs/schema.graphql +++ b/examples-staging/embedded-nextjs/schema.graphql @@ -8,32 +8,50 @@ type Post { input PostWhereInput { AND: [PostWhereInput!] OR: [PostWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - title: String - title_not: String - title_contains: String - title_not_contains: String - title_in: [String] - title_not_in: [String] - slug: String - slug_not: String - slug_contains: String - slug_not_contains: String - slug_in: [String] - slug_not_in: [String] - content: String - content_not: String - content_contains: String - content_not_contains: String - content_in: [String] - content_not_in: [String] + NOT: [PostWhereInput!] + id: IDFilter + title: StringNullableFilter + slug: StringNullableFilter + content: StringNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter } input PostWhereUniqueInput { @@ -90,7 +108,7 @@ type Query { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] post(where: PostWhereUniqueInput!): Post diff --git a/examples-staging/graphql-api-endpoint/CHANGELOG.md b/examples-staging/graphql-api-endpoint/CHANGELOG.md index c12e0035a69..3ba38d49da6 100644 --- a/examples-staging/graphql-api-endpoint/CHANGELOG.md +++ b/examples-staging/graphql-api-endpoint/CHANGELOG.md @@ -1,5 +1,17 @@ # keystone-next-app +## 1.0.6 + +### Patch Changes + +- [#6310](https://github.com/keystonejs/keystone/pull/6310) [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053) Thanks [@timleslie](https://github.com/timleslie)! - Updated dependencies to use `mergeSchemas` from `@graphql-tools/schema`, rather than its old location in `@graphql-tools/merge`. You might see a reordering of the contents of your `graphql.schema` file. + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`e985aa010`](https://github.com/keystonejs/keystone/commit/e985aa0104d30a779f21ec05d80e6b98ece87dfb), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`69f47bfed`](https://github.com/keystonejs/keystone/commit/69f47bfed1eaa1269cfdc42071268a914bd4aa17), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/fields-document@8.0.0 + - @keystone-next/auth@31.0.0 + ## 1.0.5 ### Patch Changes diff --git a/examples-staging/graphql-api-endpoint/package.json b/examples-staging/graphql-api-endpoint/package.json index a8b38a078d8..11958b4f72c 100644 --- a/examples-staging/graphql-api-endpoint/package.json +++ b/examples-staging/graphql-api-endpoint/package.json @@ -1,6 +1,6 @@ { "name": "keystone-next-app", - "version": "1.0.5", + "version": "1.0.6", "private": true, "scripts": { "dev": "keystone-next dev", @@ -11,11 +11,11 @@ "node": "^12.20 || >= 14.13" }, "dependencies": { - "@babel/runtime": "^7.14.8", - "@keystone-next/auth": "^30.0.0", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/fields-document": "^7.0.3", - "@keystone-next/keystone": "^23.0.0", + "@babel/runtime": "^7.15.3", + "@keystone-next/auth": "^31.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/fields-document": "^8.0.0", + "@keystone-next/keystone": "^24.0.0", "apollo-server-micro": "^2.25.2" }, "repository": "https://github.com/keystonejs/keystone/tree/master/examples-staging/graphql-api-endpoint" diff --git a/examples-staging/graphql-api-endpoint/schema.graphql b/examples-staging/graphql-api-endpoint/schema.graphql index 13fde63066a..1203d46c607 100644 --- a/examples-staging/graphql-api-endpoint/schema.graphql +++ b/examples-staging/graphql-api-endpoint/schema.graphql @@ -1,3 +1,62 @@ +input CreateInitialUserInput { + name: String + email: String + password: String +} + +type Mutation { + createInitialUser( + data: CreateInitialUserInput! + ): UserAuthenticationWithPasswordSuccess! + authenticateUserWithPassword( + email: String! + password: String! + ): UserAuthenticationWithPasswordResult! + createUser(data: UserCreateInput!): User + createUsers(data: [UserCreateInput!]!): [User] + updateUser(where: UserWhereUniqueInput!, data: UserUpdateInput!): User + updateUsers(data: [UserUpdateArgs!]!): [User] + deleteUser(where: UserWhereUniqueInput!): User + deleteUsers(where: [UserWhereUniqueInput!]!): [User] + createPost(data: PostCreateInput!): Post + createPosts(data: [PostCreateInput!]!): [Post] + updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post + updatePosts(data: [PostUpdateArgs!]!): [Post] + deletePost(where: PostWhereUniqueInput!): Post + deletePosts(where: [PostWhereUniqueInput!]!): [Post] + createTag(data: TagCreateInput!): Tag + createTags(data: [TagCreateInput!]!): [Tag] + updateTag(where: TagWhereUniqueInput!, data: TagUpdateInput!): Tag + updateTags(data: [TagUpdateArgs!]!): [Tag] + deleteTag(where: TagWhereUniqueInput!): Tag + deleteTags(where: [TagWhereUniqueInput!]!): [Tag] + endSession: Boolean! +} + +union AuthenticatedItem = User + +union UserAuthenticationWithPasswordResult = + UserAuthenticationWithPasswordSuccess + | UserAuthenticationWithPasswordFailure + +type UserAuthenticationWithPasswordSuccess { + sessionToken: String! + item: User! +} + +type UserAuthenticationWithPasswordFailure { + code: PasswordAuthErrorCode! + message: String! +} + +enum PasswordAuthErrorCode { + FAILURE + IDENTITY_NOT_FOUND + SECRET_NOT_SET + MULTIPLE_IDENTITY_MATCHES + SECRET_MISMATCH +} + type User { id: ID! name: String @@ -6,7 +65,7 @@ type User { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] postsCount(where: PostWhereInput! = {}): Int @@ -19,54 +78,67 @@ type PasswordState { input UserWhereInput { AND: [UserWhereInput!] OR: [UserWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_starts_with: String - name_not_starts_with: String - name_ends_with: String - name_not_ends_with: String - name_i: String - name_not_i: String - name_contains_i: String - name_not_contains_i: String - name_starts_with_i: String - name_not_starts_with_i: String - name_ends_with_i: String - name_not_ends_with_i: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_starts_with: String - email_not_starts_with: String - email_ends_with: String - email_not_ends_with: String - email_i: String - email_not_i: String - email_contains_i: String - email_not_contains_i: String - email_starts_with_i: String - email_not_starts_with_i: String - email_ends_with_i: String - email_not_ends_with_i: String - email_in: [String] - email_not_in: [String] - password_is_set: Boolean - posts_every: PostWhereInput - posts_some: PostWhereInput - posts_none: PostWhereInput + NOT: [UserWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + password: PasswordFilter + posts: PostManyRelationFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + mode: QueryMode + not: NestedStringNullableFilter +} + +enum QueryMode { + default + insensitive +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input PasswordFilter { + isSet: Boolean! +} + +input PostManyRelationFilter { + every: PostWhereInput + some: PostWhereInput + none: PostWhereInput } input UserWhereUniqueInput { @@ -126,7 +198,7 @@ type Post { tags( where: TagWhereInput! = {} orderBy: [TagOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Tag!] tagsCount(where: TagWhereInput! = {}): Int @@ -139,49 +211,30 @@ type Post_content_DocumentField { input PostWhereInput { AND: [PostWhereInput!] OR: [PostWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - title: String - title_not: String - title_contains: String - title_not_contains: String - title_starts_with: String - title_not_starts_with: String - title_ends_with: String - title_not_ends_with: String - title_i: String - title_not_i: String - title_contains_i: String - title_not_contains_i: String - title_starts_with_i: String - title_not_starts_with_i: String - title_ends_with_i: String - title_not_ends_with_i: String - title_in: [String] - title_not_in: [String] - status: String - status_not: String - status_in: [String] - status_not_in: [String] - publishDate: String - publishDate_not: String - publishDate_lt: String - publishDate_lte: String - publishDate_gt: String - publishDate_gte: String - publishDate_in: [String] - publishDate_not_in: [String] + NOT: [PostWhereInput!] + id: IDFilter + title: StringNullableFilter + status: StringNullableFilter + publishDate: DateTimeNullableFilter author: UserWhereInput - author_is_null: Boolean - tags_every: TagWhereInput - tags_some: TagWhereInput - tags_none: TagWhereInput + tags: TagManyRelationFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter +} + +input TagManyRelationFilter { + every: TagWhereInput + some: TagWhereInput + none: TagWhereInput } input PostWhereUniqueInput { @@ -247,7 +300,7 @@ type Tag { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] postsCount(where: PostWhereInput! = {}): Int @@ -256,35 +309,10 @@ type Tag { input TagWhereInput { AND: [TagWhereInput!] OR: [TagWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_starts_with: String - name_not_starts_with: String - name_ends_with: String - name_not_ends_with: String - name_i: String - name_not_i: String - name_contains_i: String - name_not_contains_i: String - name_starts_with_i: String - name_not_starts_with_i: String - name_ends_with_i: String - name_not_ends_with_i: String - name_in: [String] - name_not_in: [String] - posts_every: PostWhereInput - posts_some: PostWhereInput - posts_none: PostWhereInput + NOT: [TagWhereInput!] + id: IDFilter + name: StringNullableFilter + posts: PostManyRelationFilter } input TagWhereUniqueInput { @@ -319,70 +347,12 @@ scalar JSON url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" ) -type Mutation { - createUser(data: UserCreateInput!): User - createUsers(data: [UserCreateInput!]!): [User] - updateUser(where: UserWhereUniqueInput!, data: UserUpdateInput!): User - updateUsers(data: [UserUpdateArgs!]!): [User] - deleteUser(where: UserWhereUniqueInput!): User - deleteUsers(where: [UserWhereUniqueInput!]!): [User] - createPost(data: PostCreateInput!): Post - createPosts(data: [PostCreateInput!]!): [Post] - updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post - updatePosts(data: [PostUpdateArgs!]!): [Post] - deletePost(where: PostWhereUniqueInput!): Post - deletePosts(where: [PostWhereUniqueInput!]!): [Post] - createTag(data: TagCreateInput!): Tag - createTags(data: [TagCreateInput!]!): [Tag] - updateTag(where: TagWhereUniqueInput!, data: TagUpdateInput!): Tag - updateTags(data: [TagUpdateArgs!]!): [Tag] - deleteTag(where: TagWhereUniqueInput!): Tag - deleteTags(where: [TagWhereUniqueInput!]!): [Tag] - authenticateUserWithPassword( - email: String! - password: String! - ): UserAuthenticationWithPasswordResult! - createInitialUser( - data: CreateInitialUserInput! - ): UserAuthenticationWithPasswordSuccess! - endSession: Boolean! -} - -union AuthenticatedItem = User - -union UserAuthenticationWithPasswordResult = - UserAuthenticationWithPasswordSuccess - | UserAuthenticationWithPasswordFailure - -type UserAuthenticationWithPasswordSuccess { - sessionToken: String! - item: User! -} - -type UserAuthenticationWithPasswordFailure { - code: PasswordAuthErrorCode! - message: String! -} - -enum PasswordAuthErrorCode { - FAILURE - IDENTITY_NOT_FOUND - SECRET_NOT_SET - MULTIPLE_IDENTITY_MATCHES - SECRET_MISMATCH -} - -input CreateInitialUserInput { - name: String - email: String - password: String -} - type Query { + authenticatedItem: AuthenticatedItem users( where: UserWhereInput! = {} orderBy: [UserOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [User!] user(where: UserWhereUniqueInput!): User @@ -390,7 +360,7 @@ type Query { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] post(where: PostWhereUniqueInput!): Post @@ -398,12 +368,11 @@ type Query { tags( where: TagWhereInput! = {} orderBy: [TagOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Tag!] tag(where: TagWhereUniqueInput!): Tag tagsCount(where: TagWhereInput! = {}): Int - authenticatedItem: AuthenticatedItem keystone: KeystoneMeta! } @@ -478,11 +447,6 @@ enum KeystoneAdminUIFieldMetaItemViewFieldMode { hidden } -enum QueryMode { - default - insensitive -} - type KeystoneAdminUISort { field: String! direction: KeystoneAdminUISortDirection! diff --git a/examples-staging/playground/CHANGELOG.md b/examples-staging/playground/CHANGELOG.md index 2fc651dc077..fe7a35f17b9 100644 --- a/examples-staging/playground/CHANGELOG.md +++ b/examples-staging/playground/CHANGELOG.md @@ -1,5 +1,13 @@ # @keystone-next/example-playground +## 1.0.5 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + ## 1.0.4 ### Patch Changes diff --git a/examples-staging/playground/package.json b/examples-staging/playground/package.json index b81a675af63..b9720b7c057 100644 --- a/examples-staging/playground/package.json +++ b/examples-staging/playground/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-playground", - "version": "1.0.4", + "version": "1.0.5", "private": true, "license": "MIT", "scripts": { @@ -9,8 +9,8 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/examples-staging/playground/schema.graphql b/examples-staging/playground/schema.graphql index f7dd94aea96..67148d43a55 100644 --- a/examples-staging/playground/schema.graphql +++ b/examples-staging/playground/schema.graphql @@ -6,20 +6,48 @@ type Note { input NoteWhereInput { AND: [NoteWhereInput!] OR: [NoteWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] + NOT: [NoteWhereInput!] + id: IDFilter + label: StringNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter } input NoteWhereUniqueInput { @@ -70,7 +98,7 @@ type Query { notes( where: NoteWhereInput! = {} orderBy: [NoteOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Note!] note(where: NoteWhereUniqueInput!): Note diff --git a/examples-staging/roles/CHANGELOG.md b/examples-staging/roles/CHANGELOG.md index 61de99f174e..25431826da1 100644 --- a/examples-staging/roles/CHANGELOG.md +++ b/examples-staging/roles/CHANGELOG.md @@ -1,5 +1,17 @@ # @keystone-next/example-roles +## 4.0.6 + +### Patch Changes + +- [#6310](https://github.com/keystonejs/keystone/pull/6310) [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053) Thanks [@timleslie](https://github.com/timleslie)! - Updated dependencies to use `mergeSchemas` from `@graphql-tools/schema`, rather than its old location in `@graphql-tools/merge`. You might see a reordering of the contents of your `graphql.schema` file. + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + - @keystone-next/auth@31.0.0 + ## 4.0.5 ### Patch Changes diff --git a/examples-staging/roles/access.ts b/examples-staging/roles/access.ts index 25eb86a5195..4f31e1c101a 100644 --- a/examples-staging/roles/access.ts +++ b/examples-staging/roles/access.ts @@ -32,14 +32,14 @@ export const rules = { // Can see all todos that are: assigned to them, or not private return { OR: [ - { assignedTo: { id: session.itemId } }, - { assignedTo_is_null: true, isPrivate: true }, - { isPrivate_not: true }, + { assignedTo: { id: { equals: session.itemId } } }, + { assignedTo: null, isPrivate: { equals: true } }, + { NOT: { isPrivate: { equals: true } } }, ], }; } else { // Can only see their own todos - return { assignedTo: { id: session.itemId } }; + return { assignedTo: { id: { equals: session.itemId } } }; } }, canManageTodos: ({ session }: ListAccessArgs) => { @@ -51,7 +51,7 @@ export const rules = { return true; } else { // Can only manage their own todos - return { assignedTo: { id: session.itemId } }; + return { assignedTo: { id: { equals: session.itemId } } }; } }, canReadPeople: ({ session }: ListAccessArgs) => { @@ -63,7 +63,7 @@ export const rules = { return true; } else { // Can only see yourself - return { id: session.itemId }; + return { id: { equals: session.itemId } }; } }, canUpdatePeople: ({ session }: ListAccessArgs) => { @@ -75,7 +75,7 @@ export const rules = { return true; } else { // Can update yourself - return { id: session.itemId }; + return { id: { equals: session.itemId } }; } }, }; diff --git a/examples-staging/roles/package.json b/examples-staging/roles/package.json index 07f1b9f6494..7734e122ecf 100644 --- a/examples-staging/roles/package.json +++ b/examples-staging/roles/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-roles", - "version": "4.0.5", + "version": "4.0.6", "private": true, "license": "MIT", "scripts": { @@ -9,10 +9,10 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/auth": "^30.0.0", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/types": "^23.0.0", + "@keystone-next/auth": "^31.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0", "next": "^10.2.3", "react": "^17.0.2", "react-dom": "^17.0.2" diff --git a/examples-staging/roles/schema.graphql b/examples-staging/roles/schema.graphql index be44e3407d0..ba8201c3db0 100644 --- a/examples-staging/roles/schema.graphql +++ b/examples-staging/roles/schema.graphql @@ -1,3 +1,62 @@ +input CreateInitialPersonInput { + name: String + email: String + password: String +} + +type Mutation { + createInitialPerson( + data: CreateInitialPersonInput! + ): PersonAuthenticationWithPasswordSuccess! + authenticatePersonWithPassword( + email: String! + password: String! + ): PersonAuthenticationWithPasswordResult! + createTodo(data: TodoCreateInput!): Todo + createTodos(data: [TodoCreateInput!]!): [Todo] + updateTodo(where: TodoWhereUniqueInput!, data: TodoUpdateInput!): Todo + updateTodos(data: [TodoUpdateArgs!]!): [Todo] + deleteTodo(where: TodoWhereUniqueInput!): Todo + deleteTodos(where: [TodoWhereUniqueInput!]!): [Todo] + createPerson(data: PersonCreateInput!): Person + createPeople(data: [PersonCreateInput!]!): [Person] + updatePerson(where: PersonWhereUniqueInput!, data: PersonUpdateInput!): Person + updatePeople(data: [PersonUpdateArgs!]!): [Person] + deletePerson(where: PersonWhereUniqueInput!): Person + deletePeople(where: [PersonWhereUniqueInput!]!): [Person] + createRole(data: RoleCreateInput!): Role + createRoles(data: [RoleCreateInput!]!): [Role] + updateRole(where: RoleWhereUniqueInput!, data: RoleUpdateInput!): Role + updateRoles(data: [RoleUpdateArgs!]!): [Role] + deleteRole(where: RoleWhereUniqueInput!): Role + deleteRoles(where: [RoleWhereUniqueInput!]!): [Role] + endSession: Boolean! +} + +union AuthenticatedItem = Person + +union PersonAuthenticationWithPasswordResult = + PersonAuthenticationWithPasswordSuccess + | PersonAuthenticationWithPasswordFailure + +type PersonAuthenticationWithPasswordSuccess { + sessionToken: String! + item: Person! +} + +type PersonAuthenticationWithPasswordFailure { + code: PasswordAuthErrorCode! + message: String! +} + +enum PasswordAuthErrorCode { + FAILURE + IDENTITY_NOT_FOUND + SECRET_NOT_SET + MULTIPLE_IDENTITY_MATCHES + SECRET_MISMATCH +} + type Todo { id: ID! label: String @@ -9,26 +68,56 @@ type Todo { input TodoWhereInput { AND: [TodoWhereInput!] OR: [TodoWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - isComplete: Boolean - isComplete_not: Boolean - isPrivate: Boolean - isPrivate_not: Boolean + NOT: [TodoWhereInput!] + id: IDFilter + label: StringNullableFilter + isComplete: BooleanNullableFilter + isPrivate: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter } input TodoWhereUniqueInput { @@ -86,7 +175,7 @@ type Person { tasks( where: TodoWhereInput! = {} orderBy: [TodoOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Todo!] tasksCount(where: TodoWhereInput! = {}): Int @@ -99,32 +188,23 @@ type PasswordState { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - password_is_set: Boolean + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + password: PasswordFilter role: RoleWhereInput - role_is_null: Boolean - tasks_every: TodoWhereInput - tasks_some: TodoWhereInput - tasks_none: TodoWhereInput + tasks: TodoManyRelationFilter +} + +input PasswordFilter { + isSet: Boolean! +} + +input TodoManyRelationFilter { + every: TodoWhereInput + some: TodoWhereInput + none: TodoWhereInput } input PersonWhereUniqueInput { @@ -194,7 +274,7 @@ type Role { assignedTo( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] assignedToCount(where: PersonWhereInput! = {}): Int @@ -203,35 +283,22 @@ type Role { input RoleWhereInput { AND: [RoleWhereInput!] OR: [RoleWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - canCreateTodos: Boolean - canCreateTodos_not: Boolean - canManageAllTodos: Boolean - canManageAllTodos_not: Boolean - canSeeOtherPeople: Boolean - canSeeOtherPeople_not: Boolean - canEditOtherPeople: Boolean - canEditOtherPeople_not: Boolean - canManagePeople: Boolean - canManagePeople_not: Boolean - canManageRoles: Boolean - canManageRoles_not: Boolean - assignedTo_every: PersonWhereInput - assignedTo_some: PersonWhereInput - assignedTo_none: PersonWhereInput + NOT: [RoleWhereInput!] + id: IDFilter + name: StringNullableFilter + canCreateTodos: BooleanNullableFilter + canManageAllTodos: BooleanNullableFilter + canSeeOtherPeople: BooleanNullableFilter + canEditOtherPeople: BooleanNullableFilter + canManagePeople: BooleanNullableFilter + canManageRoles: BooleanNullableFilter + assignedTo: PersonManyRelationFilter +} + +input PersonManyRelationFilter { + every: PersonWhereInput + some: PersonWhereInput + none: PersonWhereInput } input RoleWhereUniqueInput { @@ -296,70 +363,12 @@ scalar JSON url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" ) -type Mutation { - createTodo(data: TodoCreateInput!): Todo - createTodos(data: [TodoCreateInput!]!): [Todo] - updateTodo(where: TodoWhereUniqueInput!, data: TodoUpdateInput!): Todo - updateTodos(data: [TodoUpdateArgs!]!): [Todo] - deleteTodo(where: TodoWhereUniqueInput!): Todo - deleteTodos(where: [TodoWhereUniqueInput!]!): [Todo] - createPerson(data: PersonCreateInput!): Person - createPeople(data: [PersonCreateInput!]!): [Person] - updatePerson(where: PersonWhereUniqueInput!, data: PersonUpdateInput!): Person - updatePeople(data: [PersonUpdateArgs!]!): [Person] - deletePerson(where: PersonWhereUniqueInput!): Person - deletePeople(where: [PersonWhereUniqueInput!]!): [Person] - createRole(data: RoleCreateInput!): Role - createRoles(data: [RoleCreateInput!]!): [Role] - updateRole(where: RoleWhereUniqueInput!, data: RoleUpdateInput!): Role - updateRoles(data: [RoleUpdateArgs!]!): [Role] - deleteRole(where: RoleWhereUniqueInput!): Role - deleteRoles(where: [RoleWhereUniqueInput!]!): [Role] - authenticatePersonWithPassword( - email: String! - password: String! - ): PersonAuthenticationWithPasswordResult! - createInitialPerson( - data: CreateInitialPersonInput! - ): PersonAuthenticationWithPasswordSuccess! - endSession: Boolean! -} - -union AuthenticatedItem = Person - -union PersonAuthenticationWithPasswordResult = - PersonAuthenticationWithPasswordSuccess - | PersonAuthenticationWithPasswordFailure - -type PersonAuthenticationWithPasswordSuccess { - sessionToken: String! - item: Person! -} - -type PersonAuthenticationWithPasswordFailure { - code: PasswordAuthErrorCode! - message: String! -} - -enum PasswordAuthErrorCode { - FAILURE - IDENTITY_NOT_FOUND - SECRET_NOT_SET - MULTIPLE_IDENTITY_MATCHES - SECRET_MISMATCH -} - -input CreateInitialPersonInput { - name: String - email: String - password: String -} - type Query { + authenticatedItem: AuthenticatedItem todos( where: TodoWhereInput! = {} orderBy: [TodoOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Todo!] todo(where: TodoWhereUniqueInput!): Todo @@ -367,7 +376,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person @@ -375,12 +384,11 @@ type Query { roles( where: RoleWhereInput! = {} orderBy: [RoleOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Role!] role(where: RoleWhereUniqueInput!): Role rolesCount(where: RoleWhereInput! = {}): Int - authenticatedItem: AuthenticatedItem keystone: KeystoneMeta! } diff --git a/examples-staging/sandbox/CHANGELOG.md b/examples-staging/sandbox/CHANGELOG.md index 6c26440d40d..e4c02593c4f 100644 --- a/examples-staging/sandbox/CHANGELOG.md +++ b/examples-staging/sandbox/CHANGELOG.md @@ -1,5 +1,14 @@ # @keystone-next/example-sandbox +## 3.0.6 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/auth@31.0.0 + ## 3.0.5 ### Patch Changes diff --git a/examples-staging/sandbox/package.json b/examples-staging/sandbox/package.json index faa9b468701..23899c25be7 100644 --- a/examples-staging/sandbox/package.json +++ b/examples-staging/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-sandbox", - "version": "3.0.5", + "version": "3.0.6", "private": true, "license": "MIT", "scripts": { @@ -8,9 +8,9 @@ "sandbox": "yarn && yarn dev" }, "dependencies": { - "@keystone-next/auth": "^30.0.0", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/auth": "^31.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "engines": { "node": "^12.20 || >= 14.13" diff --git a/examples-staging/sandbox/schema.graphql b/examples-staging/sandbox/schema.graphql index 66117bc9d9a..265367bfa79 100644 --- a/examples-staging/sandbox/schema.graphql +++ b/examples-staging/sandbox/schema.graphql @@ -11,48 +11,69 @@ type Todo { input TodoWhereInput { AND: [TodoWhereInput!] OR: [TodoWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TodoWhereInput!] + id: IDFilter + label: StringNullableFilter + isComplete: BooleanNullableFilter assignedTo: UserWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] - createdAt: String - createdAt_not: String - createdAt_lt: String - createdAt_lte: String - createdAt_gt: String - createdAt_gte: String - createdAt_in: [String] - createdAt_not_in: [String] - updatedAt: String - updatedAt_not: String - updatedAt_lt: String - updatedAt_lte: String - updatedAt_gt: String - updatedAt_gte: String - updatedAt_in: [String] - updatedAt_not_in: [String] + finishBy: DateTimeNullableFilter + createdAt: DateTimeNullableFilter + updatedAt: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TodoWhereUniqueInput { @@ -111,7 +132,7 @@ type User { tasks( where: TodoWhereInput! = {} orderBy: [TodoOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Todo!] tasksCount(where: TodoWhereInput! = {}): Int @@ -126,46 +147,24 @@ type PasswordState { input UserWhereInput { AND: [UserWhereInput!] OR: [UserWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - password_is_set: Boolean - tasks_every: TodoWhereInput - tasks_some: TodoWhereInput - tasks_none: TodoWhereInput - createdAt: String - createdAt_not: String - createdAt_lt: String - createdAt_lte: String - createdAt_gt: String - createdAt_gte: String - createdAt_in: [String] - createdAt_not_in: [String] - updatedAt: String - updatedAt_not: String - updatedAt_lt: String - updatedAt_lte: String - updatedAt_gt: String - updatedAt_gte: String - updatedAt_in: [String] - updatedAt_not_in: [String] + NOT: [UserWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + password: PasswordFilter + tasks: TodoManyRelationFilter + createdAt: DateTimeNullableFilter + updatedAt: DateTimeNullableFilter +} + +input PasswordFilter { + isSet: Boolean! +} + +input TodoManyRelationFilter { + every: TodoWhereInput + some: TodoWhereInput + none: TodoWhereInput } input UserWhereUniqueInput { @@ -238,7 +237,7 @@ type Query { todos( where: TodoWhereInput! = {} orderBy: [TodoOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Todo!] todo(where: TodoWhereUniqueInput!): Todo @@ -246,7 +245,7 @@ type Query { users( where: UserWhereInput! = {} orderBy: [UserOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [User!] user(where: UserWhereUniqueInput!): User diff --git a/examples/blog/CHANGELOG.md b/examples/blog/CHANGELOG.md index e6b48d54dff..d15f710f338 100644 --- a/examples/blog/CHANGELOG.md +++ b/examples/blog/CHANGELOG.md @@ -1,5 +1,13 @@ # @keystone-next/example-blog +## 2.0.6 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + ## 2.0.5 ### Patch Changes diff --git a/examples/blog/package.json b/examples/blog/package.json index 047521e8beb..d9bac9e7a8d 100644 --- a/examples/blog/package.json +++ b/examples/blog/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-blog", - "version": "2.0.5", + "version": "2.0.6", "private": true, "license": "MIT", "scripts": { @@ -9,8 +9,8 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/examples/blog/schema.graphql b/examples/blog/schema.graphql index 1cd125ef525..1dd72c9481f 100644 --- a/examples/blog/schema.graphql +++ b/examples/blog/schema.graphql @@ -15,40 +15,70 @@ enum PostStatusType { input PostWhereInput { AND: [PostWhereInput!] OR: [PostWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - title: String - title_not: String - title_contains: String - title_not_contains: String - title_in: [String] - title_not_in: [String] - status: PostStatusType - status_not: PostStatusType - status_in: [PostStatusType] - status_not_in: [PostStatusType] - content: String - content_not: String - content_contains: String - content_not_contains: String - content_in: [String] - content_not_in: [String] - publishDate: String - publishDate_not: String - publishDate_lt: String - publishDate_lte: String - publishDate_gt: String - publishDate_gte: String - publishDate_in: [String] - publishDate_not_in: [String] + NOT: [PostWhereInput!] + id: IDFilter + title: StringNullableFilter + status: PostStatusTypeNullableFilter + content: StringNullableFilter + publishDate: DateTimeNullableFilter author: AuthorWhereInput - author_is_null: Boolean +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input PostStatusTypeNullableFilter { + equals: PostStatusType + in: [PostStatusType!] + notIn: [PostStatusType!] + not: PostStatusTypeNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input PostWhereUniqueInput { @@ -107,7 +137,7 @@ type Author { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] postsCount(where: PostWhereInput! = {}): Int @@ -116,29 +146,17 @@ type Author { input AuthorWhereInput { AND: [AuthorWhereInput!] OR: [AuthorWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - posts_every: PostWhereInput - posts_some: PostWhereInput - posts_none: PostWhereInput + NOT: [AuthorWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + posts: PostManyRelationFilter +} + +input PostManyRelationFilter { + every: PostWhereInput + some: PostWhereInput + none: PostWhereInput } input AuthorWhereUniqueInput { @@ -208,7 +226,7 @@ type Query { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] post(where: PostWhereUniqueInput!): Post @@ -216,7 +234,7 @@ type Query { authors( where: AuthorWhereInput! = {} orderBy: [AuthorOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Author!] author(where: AuthorWhereUniqueInput!): Author diff --git a/examples/custom-admin-ui-logo/CHANGELOG.md b/examples/custom-admin-ui-logo/CHANGELOG.md index fc29b50ebb5..000593008f5 100644 --- a/examples/custom-admin-ui-logo/CHANGELOG.md +++ b/examples/custom-admin-ui-logo/CHANGELOG.md @@ -1,5 +1,14 @@ # @keystone-next/example-custom-admin-ui-logo +## 1.0.1 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + ## 1.0.0 ### Major Changes diff --git a/examples/custom-admin-ui-logo/package.json b/examples/custom-admin-ui-logo/package.json index 6450ae841c8..376a249bed9 100644 --- a/examples/custom-admin-ui-logo/package.json +++ b/examples/custom-admin-ui-logo/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-custom-admin-ui-logo", - "version": "1.0.0", + "version": "1.0.1", "private": true, "license": "MIT", "scripts": { @@ -9,9 +9,9 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/types": "^23.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0", "@keystone-ui/core": "^3.1.0", "next": "^10.2.3", "react": "^17.0.2" diff --git a/examples/custom-admin-ui-logo/schema.graphql b/examples/custom-admin-ui-logo/schema.graphql index 49a0f225167..e194b7cb406 100644 --- a/examples/custom-admin-ui-logo/schema.graphql +++ b/examples/custom-admin-ui-logo/schema.graphql @@ -16,36 +16,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -103,7 +142,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -112,23 +151,16 @@ type Person { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + tasks: TaskManyRelationFilter +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -194,7 +226,7 @@ type Query { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -202,7 +234,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person diff --git a/examples/custom-admin-ui-navigation/CHANGELOG.md b/examples/custom-admin-ui-navigation/CHANGELOG.md index 1aabc24b86e..a9d48b60e5a 100644 --- a/examples/custom-admin-ui-navigation/CHANGELOG.md +++ b/examples/custom-admin-ui-navigation/CHANGELOG.md @@ -1 +1,14 @@ # @keystone-next/example-custom-navigation-component + +## 5.0.0 + +### Major Changes + +- [#6185](https://github.com/keystonejs/keystone/pull/6185) [`bc00c0a17`](https://github.com/keystonejs/keystone/commit/bc00c0a17b41a9ef016ec41b59ac394013be916d) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Initial version of the custom-admin-ui-navigation example. + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 diff --git a/examples/custom-admin-ui-navigation/package.json b/examples/custom-admin-ui-navigation/package.json index bc9c7a10355..461c3c0d424 100644 --- a/examples/custom-admin-ui-navigation/package.json +++ b/examples/custom-admin-ui-navigation/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-custom-admin-ui-navigation", - "version": "4.0.4", + "version": "5.0.0", "private": true, "license": "MIT", "scripts": { @@ -9,9 +9,9 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.1", - "@keystone-next/types": "^23.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0", "react": "^17.0.2" }, "devDependencies": { diff --git a/examples/custom-admin-ui-navigation/schema.graphql b/examples/custom-admin-ui-navigation/schema.graphql index 49a0f225167..e194b7cb406 100644 --- a/examples/custom-admin-ui-navigation/schema.graphql +++ b/examples/custom-admin-ui-navigation/schema.graphql @@ -16,36 +16,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -103,7 +142,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -112,23 +151,16 @@ type Person { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + tasks: TaskManyRelationFilter +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -194,7 +226,7 @@ type Query { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -202,7 +234,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person diff --git a/examples/custom-admin-ui-pages/CHANGELOG.md b/examples/custom-admin-ui-pages/CHANGELOG.md index 88b4c31f841..68e1f42b5a0 100644 --- a/examples/custom-admin-ui-pages/CHANGELOG.md +++ b/examples/custom-admin-ui-pages/CHANGELOG.md @@ -1 +1,16 @@ # @keystone-next/example-custom-admin-ui-pages + +## 1.0.0 + +### Major Changes + +- [#6082](https://github.com/keystonejs/keystone/pull/6082) [`e0b9e8d38`](https://github.com/keystonejs/keystone/commit/e0b9e8d38b0d531bcc921f05435bd1985b47781a) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Initial version of the custom-admin-ui-pages example. + +### Patch Changes + +- [#6264](https://github.com/keystonejs/keystone/pull/6264) [`df10c42a2`](https://github.com/keystonejs/keystone/commit/df10c42a2c4fb4d48060b118de3111fd14cdc412) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Additional content added to example for making the custom-page look more like the Admin UI, as well as adding a route to the custom page to the Admin UI Navigation. + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 diff --git a/examples/custom-admin-ui-pages/README.md b/examples/custom-admin-ui-pages/README.md index 37622aac6b1..59b084aba29 100644 --- a/examples/custom-admin-ui-pages/README.md +++ b/examples/custom-admin-ui-pages/README.md @@ -19,10 +19,44 @@ You can also access a GraphQL Playground at [localhost:3000/api/graphql](http:// 🚀 Congratulations, you're now up and running with Keystone! -## admin/pages +## /admin/pages -This project leverages the `/admin/pages` directory. As elaborated on in the [Custom Pages](https://keystonejs.com/docs/guides/custom-admin-ui-pages) guide, this directory is used to generate additional routes in the Admin UI, a behaviour inherited from `Next.js`. The default export of files in this directory are expected to be **React Components**. +This project leverages the `/admin/pages` directory. As elaborated on in the [Custom Pages](https://keystonejs.com/docs/guides/custom-admin-ui-pages) guide, this directory is used to generate additional pages in the Admin UI, a behaviour inherited from `Next.js`. The default export of files in this directory are expected to be **React Components**. **All other exports are ignored** -**NOTE** The Keystone monorepo leverages a babel config that means we use the old jsx transform (this doesn't have an impact on the code we ship to npm). -This is why there are `import React from 'react'` statements in our examples, this is NOT necessary outside of the Keystone repo (unless you have a babel config with the old jsx transform which is currently the default with @babel/preset-react) as you'll be using Next's babel config which uses the new jsx transform. +**NOTE**: The Keystone monorepo leverages a babel config that means we use the old jsx transform (this doesn't have an impact on the code we ship to npm). +This is why there are `import React from 'react'` statements in our examples, this is NOT necessary outside of the Keystone repo (unless you have a babel config with the old jsx transform which is currently the default with `@babel/preset-react`) as you'll be using Next's babel config which uses the new jsx transform. + +## Custom Navigation + +In order to ensure that the new page is visible and navigable within the Admin UI, this example also adds a custom Navigation component with the +route to the custom page included. For much more detail on adding custom navigation, please see the Custom Admin UI Navigation [guide](https://keystonejs.com/docs/guides/custom-admin-ui-navigation) and [example](../custom-admin-ui-navigation). + +## Layout components + +In order to help us build custom pages that _look_ and _feel_ like part of the Admin UI, Keystone exports the `PageContainer` component from +the `@keystone-next/keystone/admin-ui/components` package. + +### PageContainer + +PageContainer has the following types: + +```typescript +type PageContainerProps = { + header: ReactElement; + children: ReactNode; +}; +``` + +To match the header style applied to all Admin UI standard pages, we use the `Heading` component from `@keystone-ui/core` as an `h3` element. + +```tsx +import { PageContainer } from '@keystone-next/keystone/admin-ui/component'; +import { Heading } from '@keystone-ui/core'; + +export default () => { + return ( + Custom Page}>{/* ... */} + ); +}; +``` diff --git a/examples/custom-admin-ui-pages/admin/components/CustomNavigation.tsx b/examples/custom-admin-ui-pages/admin/components/CustomNavigation.tsx new file mode 100644 index 00000000000..4c30c4d376a --- /dev/null +++ b/examples/custom-admin-ui-pages/admin/components/CustomNavigation.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { + NavItem, + ListNavItems, + NavigationContainer, +} from '@keystone-next/keystone/admin-ui/components'; +import type { NavigationProps } from '@keystone-next/keystone/admin-ui/components'; + +export function CustomNavigation({ lists, authenticatedItem }: NavigationProps) { + return ( + + Dashboard + + Custom Page + + ); +} diff --git a/examples/custom-admin-ui-pages/admin/config.ts b/examples/custom-admin-ui-pages/admin/config.ts new file mode 100644 index 00000000000..12e80c22bb2 --- /dev/null +++ b/examples/custom-admin-ui-pages/admin/config.ts @@ -0,0 +1,5 @@ +import type { AdminConfig } from '@keystone-next/types'; +import { CustomNavigation } from './components/CustomNavigation'; +export const components: AdminConfig['components'] = { + Navigation: CustomNavigation, +}; diff --git a/examples/custom-admin-ui-pages/admin/pages/custom-page.tsx b/examples/custom-admin-ui-pages/admin/pages/custom-page.tsx index f109f136375..4e14dbb36cf 100644 --- a/examples/custom-admin-ui-pages/admin/pages/custom-page.tsx +++ b/examples/custom-admin-ui-pages/admin/pages/custom-page.tsx @@ -1,34 +1,27 @@ /** @jsx jsx */ -import { Fragment } from 'react'; -import { jsx } from '@keystone-ui/core'; +import { PageContainer } from '@keystone-next/keystone/admin-ui/components'; +import { jsx, Heading } from '@keystone-ui/core'; // Please note that while this capability is driven by Next.js's pages directory // We do not currently support any of the auxillary methods that Next.js provides i.e. `getStaticProps` // Presently the only export from the directory that is supported is the page component itself. export default function CustomPage() { return ( - -

    Custom Page}> +

    -

    - Hello this is a custom page -

    -

    - This is a custom page added to the Admin UI, leveraging @keystone-ui/core -

    -
    - + This is a custom Admin UI Page + +

    + It can be accessed via the route /custom-page +

    + ); } diff --git a/examples/custom-admin-ui-pages/package.json b/examples/custom-admin-ui-pages/package.json index 574a7a771e8..c89af45ea77 100644 --- a/examples/custom-admin-ui-pages/package.json +++ b/examples/custom-admin-ui-pages/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-custom-admin-ui-pages", - "version": "0.0.1", + "version": "1.0.0", "private": true, "license": "MIT", "scripts": { @@ -9,11 +9,10 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/types": "^23.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0", "@keystone-ui/core": "^3.1.0", - "next": "^10.2.3", "react": "^17.0.2" }, "devDependencies": { diff --git a/examples/custom-admin-ui-pages/schema.graphql b/examples/custom-admin-ui-pages/schema.graphql index 49a0f225167..e194b7cb406 100644 --- a/examples/custom-admin-ui-pages/schema.graphql +++ b/examples/custom-admin-ui-pages/schema.graphql @@ -16,36 +16,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -103,7 +142,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -112,23 +151,16 @@ type Person { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + tasks: TaskManyRelationFilter +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -194,7 +226,7 @@ type Query { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -202,7 +234,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person diff --git a/examples/custom-field-view/CHANGELOG.md b/examples/custom-field-view/CHANGELOG.md index 23caa3ea685..372a08a14be 100644 --- a/examples/custom-field-view/CHANGELOG.md +++ b/examples/custom-field-view/CHANGELOG.md @@ -1,5 +1,14 @@ # @keystone-next/example-custom-field-view +## 1.0.2 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + ## 1.0.1 ### Patch Changes diff --git a/examples/custom-field-view/package.json b/examples/custom-field-view/package.json index c2882d1aa7d..cdf9cf19396 100644 --- a/examples/custom-field-view/package.json +++ b/examples/custom-field-view/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-custom-field-view", - "version": "1.0.1", + "version": "1.0.2", "private": true, "license": "MIT", "scripts": { @@ -10,9 +10,9 @@ }, "dependencies": { "@emotion/css": "^11.1.3", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/types": "^23.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.1.1", "@keystone-ui/fields": "^4.1.2", diff --git a/examples/custom-field-view/schema.graphql b/examples/custom-field-view/schema.graphql index 049042c59fb..8f3770570e0 100644 --- a/examples/custom-field-view/schema.graphql +++ b/examples/custom-field-view/schema.graphql @@ -17,36 +17,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -106,7 +145,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -115,23 +154,16 @@ type Person { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + tasks: TaskManyRelationFilter +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -197,7 +229,7 @@ type Query { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -205,7 +237,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person diff --git a/examples/custom-field/CHANGELOG.md b/examples/custom-field/CHANGELOG.md index a5efaa3027f..5e7867dc6a5 100644 --- a/examples/custom-field/CHANGELOG.md +++ b/examples/custom-field/CHANGELOG.md @@ -1,5 +1,14 @@ # @keystone-next/example-custom-field +## 0.0.3 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + ## 0.0.2 ### Patch Changes diff --git a/examples/custom-field/package.json b/examples/custom-field/package.json index 79513877337..b8e4b695650 100644 --- a/examples/custom-field/package.json +++ b/examples/custom-field/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-custom-field", - "version": "0.0.2", + "version": "0.0.3", "private": true, "license": "MIT", "scripts": { @@ -9,9 +9,9 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/types": "^23.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0", "@keystone-ui/fields": "^4.1.2", "react": "^17.0.2" }, diff --git a/examples/custom-field/schema.graphql b/examples/custom-field/schema.graphql index bce900c79cd..9a94c244dfd 100644 --- a/examples/custom-field/schema.graphql +++ b/examples/custom-field/schema.graphql @@ -16,48 +16,70 @@ enum PostStatusType { input PostWhereInput { AND: [PostWhereInput!] OR: [PostWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - title: String - title_not: String - title_contains: String - title_not_contains: String - title_in: [String] - title_not_in: [String] - status: PostStatusType - status_not: PostStatusType - status_in: [PostStatusType] - status_not_in: [PostStatusType] - content: String - content_not: String - content_contains: String - content_not_contains: String - content_in: [String] - content_not_in: [String] - rating: Int - rating_not: Int - rating_lt: Int - rating_lte: Int - rating_gt: Int - rating_gte: Int - rating_in: [Int] - rating_not_in: [Int] - publishDate: String - publishDate_not: String - publishDate_lt: String - publishDate_lte: String - publishDate_gt: String - publishDate_gte: String - publishDate_in: [String] - publishDate_not_in: [String] + NOT: [PostWhereInput!] + id: IDFilter + title: StringNullableFilter + status: PostStatusTypeNullableFilter + content: StringNullableFilter + publishDate: DateTimeNullableFilter author: AuthorWhereInput - author_is_null: Boolean +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input PostStatusTypeNullableFilter { + equals: PostStatusType + in: [PostStatusType!] + notIn: [PostStatusType!] + not: PostStatusTypeNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input PostWhereUniqueInput { @@ -119,7 +141,7 @@ type Author { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] postsCount(where: PostWhereInput! = {}): Int @@ -128,29 +150,17 @@ type Author { input AuthorWhereInput { AND: [AuthorWhereInput!] OR: [AuthorWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - posts_every: PostWhereInput - posts_some: PostWhereInput - posts_none: PostWhereInput + NOT: [AuthorWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + posts: PostManyRelationFilter +} + +input PostManyRelationFilter { + every: PostWhereInput + some: PostWhereInput + none: PostWhereInput } input AuthorWhereUniqueInput { @@ -220,7 +230,7 @@ type Query { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] post(where: PostWhereUniqueInput!): Post @@ -228,7 +238,7 @@ type Query { authors( where: AuthorWhereInput! = {} orderBy: [AuthorOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Author!] author(where: AuthorWhereUniqueInput!): Author diff --git a/examples/default-values/CHANGELOG.md b/examples/default-values/CHANGELOG.md index 28487e9dc40..40fe398fb9a 100644 --- a/examples/default-values/CHANGELOG.md +++ b/examples/default-values/CHANGELOG.md @@ -1,5 +1,13 @@ # @keystone-next/example-default-values +## 1.0.6 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + ## 1.0.5 ### Patch Changes diff --git a/examples/default-values/package.json b/examples/default-values/package.json index 608ca218f02..af3ba2e7b19 100644 --- a/examples/default-values/package.json +++ b/examples/default-values/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-default-values", - "version": "1.0.5", + "version": "1.0.6", "private": true, "license": "MIT", "scripts": { @@ -9,8 +9,8 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/examples/default-values/schema.graphql b/examples/default-values/schema.graphql index 49a0f225167..e194b7cb406 100644 --- a/examples/default-values/schema.graphql +++ b/examples/default-values/schema.graphql @@ -16,36 +16,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -103,7 +142,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -112,23 +151,16 @@ type Person { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + tasks: TaskManyRelationFilter +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -194,7 +226,7 @@ type Query { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -202,7 +234,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person diff --git a/examples/default-values/schema.ts b/examples/default-values/schema.ts index f3f71a39b9e..945e8f259f5 100644 --- a/examples/default-values/schema.ts +++ b/examples/default-values/schema.ts @@ -30,7 +30,7 @@ export const lists = createSchema({ // Dynamic default: Find an anonymous user and assign the task to them defaultValue: async ({ context }) => { const anonymous = await context.lists.Person.findMany({ - where: { name: 'Anonymous' }, + where: { name: { equals: 'Anonymous' } }, }); if (anonymous.length > 0) { return { connect: { id: anonymous[0].id } }; diff --git a/examples/document-field/CHANGELOG.md b/examples/document-field/CHANGELOG.md index 52ab3591275..894c979f97a 100644 --- a/examples/document-field/CHANGELOG.md +++ b/examples/document-field/CHANGELOG.md @@ -1,5 +1,14 @@ # @keystone-next/example-document-field +## 1.1.2 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`e985aa010`](https://github.com/keystonejs/keystone/commit/e985aa0104d30a779f21ec05d80e6b98ece87dfb), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`69f47bfed`](https://github.com/keystonejs/keystone/commit/69f47bfed1eaa1269cfdc42071268a914bd4aa17), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/fields-document@8.0.0 + ## 1.1.1 ### Patch Changes diff --git a/examples/document-field/package.json b/examples/document-field/package.json index e9684469656..03ffa30eeb0 100644 --- a/examples/document-field/package.json +++ b/examples/document-field/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-document-field", - "version": "1.1.1", + "version": "1.1.2", "private": true, "license": "MIT", "scripts": { @@ -11,9 +11,9 @@ }, "dependencies": { "@keystone-next/document-renderer": "^4.0.0", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/fields-document": "^7.0.3", - "@keystone-next/keystone": "^23.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/fields-document": "^8.0.0", + "@keystone-next/keystone": "^24.0.0", "@preconstruct/next": "^3.0.0", "next": "^10.2.3", "react": "^17.0.2" diff --git a/examples/document-field/schema.graphql b/examples/document-field/schema.graphql index 0c1e7ab5741..af2cccf7c54 100644 --- a/examples/document-field/schema.graphql +++ b/examples/document-field/schema.graphql @@ -20,40 +20,70 @@ type Post_content_DocumentField { input PostWhereInput { AND: [PostWhereInput!] OR: [PostWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - title: String - title_not: String - title_contains: String - title_not_contains: String - title_in: [String] - title_not_in: [String] - slug: String - slug_not: String - slug_contains: String - slug_not_contains: String - slug_in: [String] - slug_not_in: [String] - status: PostStatusType - status_not: PostStatusType - status_in: [PostStatusType] - status_not_in: [PostStatusType] - publishDate: String - publishDate_not: String - publishDate_lt: String - publishDate_lte: String - publishDate_gt: String - publishDate_gte: String - publishDate_in: [String] - publishDate_not_in: [String] + NOT: [PostWhereInput!] + id: IDFilter + title: StringNullableFilter + slug: StringNullableFilter + status: PostStatusTypeNullableFilter + publishDate: DateTimeNullableFilter author: AuthorWhereInput - author_is_null: Boolean +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input PostStatusTypeNullableFilter { + equals: PostStatusType + in: [PostStatusType!] + notIn: [PostStatusType!] + not: PostStatusTypeNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input PostWhereUniqueInput { @@ -115,7 +145,7 @@ type Author { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] postsCount(where: PostWhereInput! = {}): Int @@ -129,29 +159,17 @@ type Author_bio_DocumentField { input AuthorWhereInput { AND: [AuthorWhereInput!] OR: [AuthorWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - posts_every: PostWhereInput - posts_some: PostWhereInput - posts_none: PostWhereInput + NOT: [AuthorWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + posts: PostManyRelationFilter +} + +input PostManyRelationFilter { + every: PostWhereInput + some: PostWhereInput + none: PostWhereInput } input AuthorWhereUniqueInput { @@ -223,7 +241,7 @@ type Query { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] post(where: PostWhereUniqueInput!): Post @@ -231,7 +249,7 @@ type Query { authors( where: AuthorWhereInput! = {} orderBy: [AuthorOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Author!] author(where: AuthorWhereUniqueInput!): Author diff --git a/examples/extend-graphql-schema/CHANGELOG.md b/examples/extend-graphql-schema/CHANGELOG.md index 24c56f59629..0982ae9ed25 100644 --- a/examples/extend-graphql-schema/CHANGELOG.md +++ b/examples/extend-graphql-schema/CHANGELOG.md @@ -1,5 +1,15 @@ # @keystone-next/example-extend-graphql-schema +## 1.0.6 + +### Patch Changes + +- [#6310](https://github.com/keystonejs/keystone/pull/6310) [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053) Thanks [@timleslie](https://github.com/timleslie)! - Updated dependencies to use `mergeSchemas` from `@graphql-tools/schema`, rather than its old location in `@graphql-tools/merge`. You might see a reordering of the contents of your `graphql.schema` file. + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + ## 1.0.5 ### Patch Changes diff --git a/examples/extend-graphql-schema/README.md b/examples/extend-graphql-schema/README.md index 82ffce5897d..5047e37b0d4 100644 --- a/examples/extend-graphql-schema/README.md +++ b/examples/extend-graphql-schema/README.md @@ -105,7 +105,7 @@ We add a custom type to our schema using `type Statisics` in the `typeDefs`, and }); const { posts } = await context.lists.Author.findOne({ where: { id }, - query: 'posts(first: 1, orderBy: { publishDate: desc }) { id }', + query: 'posts(take: 1, orderBy: { publishDate: desc }) { id }', }); return { draft, published, latestPostId: posts ? posts[0].id : null }; }, diff --git a/examples/extend-graphql-schema/custom-schema.ts b/examples/extend-graphql-schema/custom-schema.ts index 42a9f9482f1..b29ddd7c49a 100644 --- a/examples/extend-graphql-schema/custom-schema.ts +++ b/examples/extend-graphql-schema/custom-schema.ts @@ -47,19 +47,19 @@ export const extendGraphqlSchema = graphQLSchemaExtension({ // If you accidentally use `context.lists.Post` here you can expect problems // when accessing the fields in your GraphQL client. return context.db.lists.Post.findMany({ - where: { author: { id }, publishDate_gt: cutoff }, + where: { author: { id: { equals: id } }, publishDate: { gt: cutoff } }, }); }, stats: async (root, { id }, context) => { const draft = await context.lists.Post.count({ - where: { author: { id }, status: 'draft' }, + where: { author: { id: { equals: id } }, status: { equals: 'draft' } }, }); const published = await context.lists.Post.count({ - where: { author: { id }, status: 'published' }, + where: { author: { id: { equals: id } }, status: { equals: 'published' } }, }); const { posts } = await context.lists.Author.findOne({ where: { id }, - query: 'posts(first: 1, orderBy: { publishDate: desc }) { id }', + query: 'posts(take: 1, orderBy: { publishDate: desc }) { id }', }); return { draft, published, latestPostId: posts ? posts[0].id : null }; }, diff --git a/examples/extend-graphql-schema/package.json b/examples/extend-graphql-schema/package.json index de95d078b43..e6dd1fab575 100644 --- a/examples/extend-graphql-schema/package.json +++ b/examples/extend-graphql-schema/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-extend-graphql-schema", - "version": "1.0.5", + "version": "1.0.6", "private": true, "license": "MIT", "scripts": { @@ -9,8 +9,8 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/examples/extend-graphql-schema/schema.graphql b/examples/extend-graphql-schema/schema.graphql index 65fa2e55582..5268dc2c516 100644 --- a/examples/extend-graphql-schema/schema.graphql +++ b/examples/extend-graphql-schema/schema.graphql @@ -1,3 +1,31 @@ +type Mutation { + """ + Publish a post + """ + publishPost(id: ID!): Post + createPost(data: PostCreateInput!): Post + createPosts(data: [PostCreateInput!]!): [Post] + updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post + updatePosts(data: [PostUpdateArgs!]!): [Post] + deletePost(where: PostWhereUniqueInput!): Post + deletePosts(where: [PostWhereUniqueInput!]!): [Post] + createAuthor(data: AuthorCreateInput!): Author + createAuthors(data: [AuthorCreateInput!]!): [Author] + updateAuthor(where: AuthorWhereUniqueInput!, data: AuthorUpdateInput!): Author + updateAuthors(data: [AuthorUpdateArgs!]!): [Author] + deleteAuthor(where: AuthorWhereUniqueInput!): Author + deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author] +} + +""" + A custom type to represent statistics for a user +""" +type Statistics { + draft: Int + published: Int + latest: Post +} + type Post { id: ID! title: String @@ -15,40 +43,70 @@ enum PostStatusType { input PostWhereInput { AND: [PostWhereInput!] OR: [PostWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - title: String - title_not: String - title_contains: String - title_not_contains: String - title_in: [String] - title_not_in: [String] - status: PostStatusType - status_not: PostStatusType - status_in: [PostStatusType] - status_not_in: [PostStatusType] - content: String - content_not: String - content_contains: String - content_not_contains: String - content_in: [String] - content_not_in: [String] - publishDate: String - publishDate_not: String - publishDate_lt: String - publishDate_lte: String - publishDate_gt: String - publishDate_gte: String - publishDate_in: [String] - publishDate_not_in: [String] + NOT: [PostWhereInput!] + id: IDFilter + title: StringNullableFilter + status: PostStatusTypeNullableFilter + content: StringNullableFilter + publishDate: DateTimeNullableFilter author: AuthorWhereInput - author_is_null: Boolean +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input PostStatusTypeNullableFilter { + equals: PostStatusType + in: [PostStatusType!] + notIn: [PostStatusType!] + not: PostStatusTypeNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input PostWhereUniqueInput { @@ -107,7 +165,7 @@ type Author { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] postsCount(where: PostWhereInput! = {}): Int @@ -116,29 +174,17 @@ type Author { input AuthorWhereInput { AND: [AuthorWhereInput!] OR: [AuthorWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - posts_every: PostWhereInput - posts_some: PostWhereInput - posts_none: PostWhereInput + NOT: [AuthorWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + posts: PostManyRelationFilter +} + +input PostManyRelationFilter { + every: PostWhereInput + some: PostWhereInput + none: PostWhereInput } input AuthorWhereUniqueInput { @@ -189,40 +235,20 @@ scalar JSON url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" ) -type Mutation { - createPost(data: PostCreateInput!): Post - createPosts(data: [PostCreateInput!]!): [Post] - updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post - updatePosts(data: [PostUpdateArgs!]!): [Post] - deletePost(where: PostWhereUniqueInput!): Post - deletePosts(where: [PostWhereUniqueInput!]!): [Post] - createAuthor(data: AuthorCreateInput!): Author - createAuthors(data: [AuthorCreateInput!]!): [Author] - updateAuthor(where: AuthorWhereUniqueInput!, data: AuthorUpdateInput!): Author - updateAuthors(data: [AuthorUpdateArgs!]!): [Author] - deleteAuthor(where: AuthorWhereUniqueInput!): Author - deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author] - +type Query { """ - Publish a post + Return all posts for a user from the last days """ - publishPost(id: ID!): Post -} - -""" - A custom type to represent statistics for a user -""" -type Statistics { - draft: Int - published: Int - latest: Post -} + recentPosts(id: ID!, days: Int! = 7): [Post] -type Query { + """ + Compute statistics for a user + """ + stats(id: ID!): Statistics posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] post(where: PostWhereUniqueInput!): Post @@ -230,21 +256,11 @@ type Query { authors( where: AuthorWhereInput! = {} orderBy: [AuthorOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Author!] author(where: AuthorWhereUniqueInput!): Author authorsCount(where: AuthorWhereInput! = {}): Int - - """ - Return all posts for a user from the last days - """ - recentPosts(id: ID!, days: Int! = 7): [Post] - - """ - Compute statistics for a user - """ - stats(id: ID!): Statistics keystone: KeystoneMeta! } diff --git a/examples/json/CHANGELOG.md b/examples/json/CHANGELOG.md index fd6d222ba2b..02b93900684 100644 --- a/examples/json/CHANGELOG.md +++ b/examples/json/CHANGELOG.md @@ -1,5 +1,13 @@ # @keystone-next/example-json +## 4.0.7 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + ## 4.0.6 ### Patch Changes diff --git a/examples/json/package.json b/examples/json/package.json index d244ea7b2bd..683425f4541 100644 --- a/examples/json/package.json +++ b/examples/json/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-json-field", - "version": "4.0.6", + "version": "4.0.7", "private": true, "license": "MIT", "scripts": { @@ -9,8 +9,8 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/examples/json/schema.graphql b/examples/json/schema.graphql index 32862af1084..d474f50471c 100644 --- a/examples/json/schema.graphql +++ b/examples/json/schema.graphql @@ -9,24 +9,55 @@ type Package { input PackageWhereInput { AND: [PackageWhereInput!] OR: [PackageWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - isPrivate: Boolean - isPrivate_not: Boolean + NOT: [PackageWhereInput!] + id: IDFilter + label: StringNullableFilter + isPrivate: BooleanNullableFilter ownedBy: PersonWhereInput - ownedBy_is_null: Boolean +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter } input PackageWhereUniqueInput { @@ -80,7 +111,7 @@ type Person { packages( where: PackageWhereInput! = {} orderBy: [PackageOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Package!] packagesCount(where: PackageWhereInput! = {}): Int @@ -89,23 +120,16 @@ type Person { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - packages_every: PackageWhereInput - packages_some: PackageWhereInput - packages_none: PackageWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + packages: PackageManyRelationFilter +} + +input PackageManyRelationFilter { + every: PackageWhereInput + some: PackageWhereInput + none: PackageWhereInput } input PersonWhereUniqueInput { @@ -174,7 +198,7 @@ type Query { packages( where: PackageWhereInput! = {} orderBy: [PackageOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Package!] package(where: PackageWhereUniqueInput!): Package @@ -182,7 +206,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person diff --git a/examples/task-manager/CHANGELOG.md b/examples/task-manager/CHANGELOG.md index b47181e5bc2..7ffa163a889 100644 --- a/examples/task-manager/CHANGELOG.md +++ b/examples/task-manager/CHANGELOG.md @@ -1,5 +1,13 @@ # @keystone-next/example-task-manager +## 4.0.6 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + ## 4.0.5 ### Patch Changes diff --git a/examples/task-manager/package.json b/examples/task-manager/package.json index 3a63680b98d..8680a007e74 100644 --- a/examples/task-manager/package.json +++ b/examples/task-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-task-manager", - "version": "4.0.5", + "version": "4.0.6", "private": true, "license": "MIT", "scripts": { @@ -9,8 +9,8 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/examples/task-manager/schema.graphql b/examples/task-manager/schema.graphql index 49a0f225167..e194b7cb406 100644 --- a/examples/task-manager/schema.graphql +++ b/examples/task-manager/schema.graphql @@ -16,36 +16,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -103,7 +142,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -112,23 +151,16 @@ type Person { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + tasks: TaskManyRelationFilter +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -194,7 +226,7 @@ type Query { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -202,7 +234,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person diff --git a/examples/testing/CHANGELOG.md b/examples/testing/CHANGELOG.md index d858f6edfc1..2b4114cecb1 100644 --- a/examples/testing/CHANGELOG.md +++ b/examples/testing/CHANGELOG.md @@ -1,5 +1,20 @@ # @keystone-next/example-testing +## 0.0.6 + +### Patch Changes + +- [#6218](https://github.com/keystonejs/keystone/pull/6218) [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2) Thanks [@timleslie](https://github.com/timleslie)! - Added more details to validation failure error messages. + +* [#6310](https://github.com/keystonejs/keystone/pull/6310) [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053) Thanks [@timleslie](https://github.com/timleslie)! - Updated dependencies to use `mergeSchemas` from `@graphql-tools/schema`, rather than its old location in `@graphql-tools/merge`. You might see a reordering of the contents of your `graphql.schema` file. + +* Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + - @keystone-next/auth@31.0.0 + - @keystone-next/testing@1.1.1 + ## 0.0.5 ### Patch Changes diff --git a/examples/testing/package.json b/examples/testing/package.json index 5baaa7a8735..e094b5328f9 100644 --- a/examples/testing/package.json +++ b/examples/testing/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-testing", - "version": "0.0.5", + "version": "0.0.6", "private": true, "license": "MIT", "scripts": { @@ -10,14 +10,14 @@ "test": "jest --runInBand --testTimeout=60000" }, "dependencies": { - "@babel/core": "^7.14.8", - "@babel/preset-env": "^7.14.9", - "@babel/preset-typescript": "^7.14.5", - "@keystone-next/auth": "^30.0.0", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/testing": "^1.1.0", - "@keystone-next/types": "^23.0.0" + "@babel/core": "^7.15.0", + "@babel/preset-env": "^7.15.0", + "@babel/preset-typescript": "^7.15.0", + "@keystone-next/auth": "^31.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/testing": "^1.1.1", + "@keystone-next/types": "^24.0.0" }, "devDependencies": { "babel-jest": "^27.0.6", diff --git a/examples/testing/schema.graphql b/examples/testing/schema.graphql index 8a707c22288..c6a5ddc8586 100644 --- a/examples/testing/schema.graphql +++ b/examples/testing/schema.graphql @@ -1,3 +1,56 @@ +input CreateInitialPersonInput { + name: String + email: String + password: String +} + +type Mutation { + createInitialPerson( + data: CreateInitialPersonInput! + ): PersonAuthenticationWithPasswordSuccess! + authenticatePersonWithPassword( + email: String! + password: String! + ): PersonAuthenticationWithPasswordResult! + createTask(data: TaskCreateInput!): Task + createTasks(data: [TaskCreateInput!]!): [Task] + updateTask(where: TaskWhereUniqueInput!, data: TaskUpdateInput!): Task + updateTasks(data: [TaskUpdateArgs!]!): [Task] + deleteTask(where: TaskWhereUniqueInput!): Task + deleteTasks(where: [TaskWhereUniqueInput!]!): [Task] + createPerson(data: PersonCreateInput!): Person + createPeople(data: [PersonCreateInput!]!): [Person] + updatePerson(where: PersonWhereUniqueInput!, data: PersonUpdateInput!): Person + updatePeople(data: [PersonUpdateArgs!]!): [Person] + deletePerson(where: PersonWhereUniqueInput!): Person + deletePeople(where: [PersonWhereUniqueInput!]!): [Person] + endSession: Boolean! +} + +union AuthenticatedItem = Person + +union PersonAuthenticationWithPasswordResult = + PersonAuthenticationWithPasswordSuccess + | PersonAuthenticationWithPasswordFailure + +type PersonAuthenticationWithPasswordSuccess { + sessionToken: String! + item: Person! +} + +type PersonAuthenticationWithPasswordFailure { + code: PasswordAuthErrorCode! + message: String! +} + +enum PasswordAuthErrorCode { + FAILURE + IDENTITY_NOT_FOUND + SECRET_NOT_SET + MULTIPLE_IDENTITY_MATCHES + SECRET_MISMATCH +} + type Task { id: ID! label: String @@ -16,36 +69,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -105,7 +197,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -118,30 +210,22 @@ type PasswordState { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - password_is_set: Boolean - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + password: PasswordFilter + tasks: TaskManyRelationFilter +} + +input PasswordFilter { + isSet: Boolean! +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -194,64 +278,12 @@ scalar JSON url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" ) -type Mutation { - createTask(data: TaskCreateInput!): Task - createTasks(data: [TaskCreateInput!]!): [Task] - updateTask(where: TaskWhereUniqueInput!, data: TaskUpdateInput!): Task - updateTasks(data: [TaskUpdateArgs!]!): [Task] - deleteTask(where: TaskWhereUniqueInput!): Task - deleteTasks(where: [TaskWhereUniqueInput!]!): [Task] - createPerson(data: PersonCreateInput!): Person - createPeople(data: [PersonCreateInput!]!): [Person] - updatePerson(where: PersonWhereUniqueInput!, data: PersonUpdateInput!): Person - updatePeople(data: [PersonUpdateArgs!]!): [Person] - deletePerson(where: PersonWhereUniqueInput!): Person - deletePeople(where: [PersonWhereUniqueInput!]!): [Person] - authenticatePersonWithPassword( - email: String! - password: String! - ): PersonAuthenticationWithPasswordResult! - createInitialPerson( - data: CreateInitialPersonInput! - ): PersonAuthenticationWithPasswordSuccess! - endSession: Boolean! -} - -union AuthenticatedItem = Person - -union PersonAuthenticationWithPasswordResult = - PersonAuthenticationWithPasswordSuccess - | PersonAuthenticationWithPasswordFailure - -type PersonAuthenticationWithPasswordSuccess { - sessionToken: String! - item: Person! -} - -type PersonAuthenticationWithPasswordFailure { - code: PasswordAuthErrorCode! - message: String! -} - -enum PasswordAuthErrorCode { - FAILURE - IDENTITY_NOT_FOUND - SECRET_NOT_SET - MULTIPLE_IDENTITY_MATCHES - SECRET_MISMATCH -} - -input CreateInitialPersonInput { - name: String - email: String - password: String -} - type Query { + authenticatedItem: AuthenticatedItem tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -259,12 +291,11 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person peopleCount(where: PersonWhereInput! = {}): Int - authenticatedItem: AuthenticatedItem keystone: KeystoneMeta! } diff --git a/examples/virtual-field/CHANGELOG.md b/examples/virtual-field/CHANGELOG.md index 7a25ade4858..87b027c8237 100644 --- a/examples/virtual-field/CHANGELOG.md +++ b/examples/virtual-field/CHANGELOG.md @@ -1,5 +1,14 @@ # @keystone-next/example-virtual-field +## 0.1.4 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + ## 0.1.3 ### Patch Changes diff --git a/examples/virtual-field/README.md b/examples/virtual-field/README.md index 71b53de5e52..fd452f6068f 100644 --- a/examples/virtual-field/README.md +++ b/examples/virtual-field/README.md @@ -108,7 +108,7 @@ relatedPosts: virtual({ // this could have some logic to get posts that are actually related to this one somehow // this is a just a naive "get the three latest posts that aren't this one" return context.db.lists.Post.findMany({ - first: 3, + take: 3, where: { id_not: item.id, status: 'published' }, orderBy: [{ publishDate: 'desc' }], }); diff --git a/examples/virtual-field/package.json b/examples/virtual-field/package.json index c457d83ce4b..6744cd3dc4e 100644 --- a/examples/virtual-field/package.json +++ b/examples/virtual-field/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-virtual-field", - "version": "0.1.3", + "version": "0.1.4", "private": true, "license": "MIT", "scripts": { @@ -9,9 +9,9 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/types": "^23.0.0" + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/examples/virtual-field/schema.graphql b/examples/virtual-field/schema.graphql index 70b0feee8a5..2a1713840d0 100644 --- a/examples/virtual-field/schema.graphql +++ b/examples/virtual-field/schema.graphql @@ -25,40 +25,70 @@ type PostCounts { input PostWhereInput { AND: [PostWhereInput!] OR: [PostWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - title: String - title_not: String - title_contains: String - title_not_contains: String - title_in: [String] - title_not_in: [String] - status: PostStatusType - status_not: PostStatusType - status_in: [PostStatusType] - status_not_in: [PostStatusType] - content: String - content_not: String - content_contains: String - content_not_contains: String - content_in: [String] - content_not_in: [String] - publishDate: String - publishDate_not: String - publishDate_lt: String - publishDate_lte: String - publishDate_gt: String - publishDate_gte: String - publishDate_in: [String] - publishDate_not_in: [String] + NOT: [PostWhereInput!] + id: IDFilter + title: StringNullableFilter + status: PostStatusTypeNullableFilter + content: StringNullableFilter + publishDate: DateTimeNullableFilter author: AuthorWhereInput - author_is_null: Boolean +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input PostStatusTypeNullableFilter { + equals: PostStatusType + in: [PostStatusType!] + notIn: [PostStatusType!] + not: PostStatusTypeNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input PostWhereUniqueInput { @@ -117,7 +147,7 @@ type Author { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] postsCount(where: PostWhereInput! = {}): Int @@ -127,29 +157,17 @@ type Author { input AuthorWhereInput { AND: [AuthorWhereInput!] OR: [AuthorWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - posts_every: PostWhereInput - posts_some: PostWhereInput - posts_none: PostWhereInput + NOT: [AuthorWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + posts: PostManyRelationFilter +} + +input PostManyRelationFilter { + every: PostWhereInput + some: PostWhereInput + none: PostWhereInput } input AuthorWhereUniqueInput { @@ -219,7 +237,7 @@ type Query { posts( where: PostWhereInput! = {} orderBy: [PostOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Post!] post(where: PostWhereUniqueInput!): Post @@ -227,7 +245,7 @@ type Query { authors( where: AuthorWhereInput! = {} orderBy: [AuthorOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Author!] author(where: AuthorWhereUniqueInput!): Author diff --git a/examples/virtual-field/schema.ts b/examples/virtual-field/schema.ts index c96eb7ead8b..3dbba311fae 100644 --- a/examples/virtual-field/schema.ts +++ b/examples/virtual-field/schema.ts @@ -102,7 +102,7 @@ export const lists = createSchema({ where: { id: item.id.toString() }, query: `posts( orderBy: { publishDate: desc } - first: 1 + take: 1 ) { id }`, }); if (posts.length > 0) { diff --git a/examples/with-auth/CHANGELOG.md b/examples/with-auth/CHANGELOG.md index e582c7673a6..e179b27cebf 100644 --- a/examples/with-auth/CHANGELOG.md +++ b/examples/with-auth/CHANGELOG.md @@ -1,5 +1,16 @@ # @keystone-next/example-with-auth +## 2.0.7 + +### Patch Changes + +- [#6310](https://github.com/keystonejs/keystone/pull/6310) [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053) Thanks [@timleslie](https://github.com/timleslie)! - Updated dependencies to use `mergeSchemas` from `@graphql-tools/schema`, rather than its old location in `@graphql-tools/merge`. You might see a reordering of the contents of your `graphql.schema` file. + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/auth@31.0.0 + ## 2.0.6 ### Patch Changes diff --git a/examples/with-auth/package.json b/examples/with-auth/package.json index e9caf46bbde..a9cefb11b89 100644 --- a/examples/with-auth/package.json +++ b/examples/with-auth/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/example-with-auth", - "version": "2.0.6", + "version": "2.0.7", "private": true, "license": "MIT", "scripts": { @@ -9,9 +9,9 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/auth": "^30.0.0", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/auth": "^31.0.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/examples/with-auth/schema.graphql b/examples/with-auth/schema.graphql index 8a707c22288..c6a5ddc8586 100644 --- a/examples/with-auth/schema.graphql +++ b/examples/with-auth/schema.graphql @@ -1,3 +1,56 @@ +input CreateInitialPersonInput { + name: String + email: String + password: String +} + +type Mutation { + createInitialPerson( + data: CreateInitialPersonInput! + ): PersonAuthenticationWithPasswordSuccess! + authenticatePersonWithPassword( + email: String! + password: String! + ): PersonAuthenticationWithPasswordResult! + createTask(data: TaskCreateInput!): Task + createTasks(data: [TaskCreateInput!]!): [Task] + updateTask(where: TaskWhereUniqueInput!, data: TaskUpdateInput!): Task + updateTasks(data: [TaskUpdateArgs!]!): [Task] + deleteTask(where: TaskWhereUniqueInput!): Task + deleteTasks(where: [TaskWhereUniqueInput!]!): [Task] + createPerson(data: PersonCreateInput!): Person + createPeople(data: [PersonCreateInput!]!): [Person] + updatePerson(where: PersonWhereUniqueInput!, data: PersonUpdateInput!): Person + updatePeople(data: [PersonUpdateArgs!]!): [Person] + deletePerson(where: PersonWhereUniqueInput!): Person + deletePeople(where: [PersonWhereUniqueInput!]!): [Person] + endSession: Boolean! +} + +union AuthenticatedItem = Person + +union PersonAuthenticationWithPasswordResult = + PersonAuthenticationWithPasswordSuccess + | PersonAuthenticationWithPasswordFailure + +type PersonAuthenticationWithPasswordSuccess { + sessionToken: String! + item: Person! +} + +type PersonAuthenticationWithPasswordFailure { + code: PasswordAuthErrorCode! + message: String! +} + +enum PasswordAuthErrorCode { + FAILURE + IDENTITY_NOT_FOUND + SECRET_NOT_SET + MULTIPLE_IDENTITY_MATCHES + SECRET_MISMATCH +} + type Task { id: ID! label: String @@ -16,36 +69,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -105,7 +197,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -118,30 +210,22 @@ type PasswordState { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - email: String - email_not: String - email_contains: String - email_not_contains: String - email_in: [String] - email_not_in: [String] - password_is_set: Boolean - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + email: StringNullableFilter + password: PasswordFilter + tasks: TaskManyRelationFilter +} + +input PasswordFilter { + isSet: Boolean! +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -194,64 +278,12 @@ scalar JSON url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" ) -type Mutation { - createTask(data: TaskCreateInput!): Task - createTasks(data: [TaskCreateInput!]!): [Task] - updateTask(where: TaskWhereUniqueInput!, data: TaskUpdateInput!): Task - updateTasks(data: [TaskUpdateArgs!]!): [Task] - deleteTask(where: TaskWhereUniqueInput!): Task - deleteTasks(where: [TaskWhereUniqueInput!]!): [Task] - createPerson(data: PersonCreateInput!): Person - createPeople(data: [PersonCreateInput!]!): [Person] - updatePerson(where: PersonWhereUniqueInput!, data: PersonUpdateInput!): Person - updatePeople(data: [PersonUpdateArgs!]!): [Person] - deletePerson(where: PersonWhereUniqueInput!): Person - deletePeople(where: [PersonWhereUniqueInput!]!): [Person] - authenticatePersonWithPassword( - email: String! - password: String! - ): PersonAuthenticationWithPasswordResult! - createInitialPerson( - data: CreateInitialPersonInput! - ): PersonAuthenticationWithPasswordSuccess! - endSession: Boolean! -} - -union AuthenticatedItem = Person - -union PersonAuthenticationWithPasswordResult = - PersonAuthenticationWithPasswordSuccess - | PersonAuthenticationWithPasswordFailure - -type PersonAuthenticationWithPasswordSuccess { - sessionToken: String! - item: Person! -} - -type PersonAuthenticationWithPasswordFailure { - code: PasswordAuthErrorCode! - message: String! -} - -enum PasswordAuthErrorCode { - FAILURE - IDENTITY_NOT_FOUND - SECRET_NOT_SET - MULTIPLE_IDENTITY_MATCHES - SECRET_MISMATCH -} - -input CreateInitialPersonInput { - name: String - email: String - password: String -} - type Query { + authenticatedItem: AuthenticatedItem tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -259,12 +291,11 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person peopleCount(where: PersonWhereInput! = {}): Int - authenticatedItem: AuthenticatedItem keystone: KeystoneMeta! } diff --git a/package.json b/package.json index 7cf3046e78c..0cbf8275d20 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "lint:prettier": "prettier --list-different \"**/*.{js,json,ts,tsx}\"", "lint:markdown": "remark . --frail --quiet", "lint:types": "tsc", - "lint": "yarn lint:prettier && yarn lint:eslint && yarn lint:markdown && yarn lint:types", + "lint": "yarn lint:prettier && yarn lint:eslint && yarn lint:markdown && yarn lint:types && yarn lint:filters", "test": "yarn lint && yarn test:unit", "test:unit": "cross-env DISABLE_LOGGING=true NODE_ENV=test jest --no-watchman --runInBand --logHeapUsage", "test:unit:debug": "cross-env NODE_ENV=test node --inspect-brk `which jest` --runInBand", @@ -35,17 +35,19 @@ "update": "manypkg upgrade", "no-cypress-install": "cross-env CYPRESS_INSTALL_BINARY=0 yarn", "postinstall-examples": "for d in `find examples -type d -maxdepth 1 -mindepth 1`; do cd $d; yarn keystone-next postinstall --fix; cd ../..; done; for d in `find examples-staging -type d -maxdepth 1 -mindepth 1`; do cd $d; yarn keystone-next postinstall --fix; cd ../..; done; for d in `find tests/test-projects -type d -maxdepth 1 -mindepth 1`; do cd $d; yarn keystone-next postinstall --fix; cd ../..; done", - "lint:examples": "for d in `find examples -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done; for d in `find examples-staging -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done; for d in `find tests/test-projects -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done" + "lint:examples": "for d in `find examples -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done; for d in `find examples-staging -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done; for d in `find tests/test-projects -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../../..; done", + "generate-filters": "cd prisma-utils && yarn generate", + "lint:filters": "cd prisma-utils && yarn verify" }, "dependencies": { - "@babel/core": "^7.14.8", + "@babel/core": "^7.15.0", "@babel/plugin-proposal-class-properties": "^7.14.5", "@babel/plugin-proposal-object-rest-spread": "^7.14.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.14.5", - "@babel/preset-env": "^7.14.9", + "@babel/plugin-transform-runtime": "^7.15.0", + "@babel/preset-env": "^7.15.0", "@babel/preset-react": "^7.14.5", - "@babel/preset-typescript": "^7.14.5", + "@babel/preset-typescript": "^7.15.0", "@changesets/changelog-github": "^0.4.0", "@changesets/cli": "^2.16.0", "@jest/test-sequencer": "^27.0.6", @@ -53,15 +55,15 @@ "@preconstruct/cli": "2.1.0", "@preconstruct/eslint-plugin-format-js-tag": "^0.1.0", "@testing-library/jest-dom": "^5.14.1", - "@types/jest": "^26.0.24", + "@types/jest": "^27.0.1", "@types/node-fetch": "^2.5.12", - "@typescript-eslint/eslint-plugin": "^4.29.0", - "@typescript-eslint/parser": "^4.29.0", + "@typescript-eslint/eslint-plugin": "^4.29.2", + "@typescript-eslint/parser": "^4.29.2", "chalk-cli": "^4.1.0", "cross-env": "^7.0.3", "eslint": "^7.32.0", "eslint-plugin-cypress": "^2.11.3", - "eslint-plugin-import": "^2.23.3", + "eslint-plugin-import": "^2.24.0", "eslint-plugin-jest": "^24.4.0", "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^4.2.0", @@ -117,7 +119,8 @@ "tests/api-tests", "tests/benchmarks", "tests/examples-smoke-tests", - "tests/test-projects/*" + "tests/test-projects/*", + "prisma-utils" ], "nohoist": [ "**/cypress-multi-reporters" @@ -126,7 +129,8 @@ "preconstruct": { "packages": [ "packages/*", - "design-system/packages/*" + "design-system/packages/*", + "prisma-utils" ], "distFilenameStrategy": "unscoped-package-name" }, diff --git a/packages/admin-ui-utils/CHANGELOG.md b/packages/admin-ui-utils/CHANGELOG.md index b196a6d926c..5acd3fcae8d 100644 --- a/packages/admin-ui-utils/CHANGELOG.md +++ b/packages/admin-ui-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @keystone-next/admin-ui-utils +## 5.0.6 + +### Patch Changes + +- Updated dependencies [[`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/types@24.0.0 + ## 5.0.5 ### Patch Changes diff --git a/packages/admin-ui-utils/package.json b/packages/admin-ui-utils/package.json index 23e2be5e864..9a0ba81f77b 100644 --- a/packages/admin-ui-utils/package.json +++ b/packages/admin-ui-utils/package.json @@ -1,15 +1,15 @@ { "name": "@keystone-next/admin-ui-utils", - "version": "5.0.5", + "version": "5.0.6", "main": "dist/admin-ui-utils.cjs.js", "module": "dist/admin-ui-utils.esm.js", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@emotion/weak-memoize": "^0.2.5", - "@keystone-next/types": "^23.0.0", + "@keystone-next/types": "^24.0.0", "@keystone-ui/core": "^3.1.1", - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "fast-deep-equal": "^3.1.3", "graphql": "^15.5.1" }, diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index 996a127bd09..a37f9e59522 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,36 @@ # @keystone-next/auth +## 31.0.0 + +### Major Changes + +- [#6211](https://github.com/keystonejs/keystone/pull/6211) [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The update mutations now accept `where` unique inputs instead of only an `id` and the `where` and `data` arguments are non-null. + + If you have a list called `Item`, the update mutations now look like this: + + ```graphql + type Mutation { + updateItem(where: ItemWhereUniqueInput!, data: ItemUpdateInput!): Item + updateItems(data: [ItemUpdateArgs!]!): [Item] + } + + input ItemUpdateArgs { + where: ItemWhereUniqueInput! + data: ItemUpdateInput! + } + ``` + +### Patch Changes + +- [#6310](https://github.com/keystonejs/keystone/pull/6310) [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053) Thanks [@timleslie](https://github.com/timleslie)! - Updated dependencies to use `mergeSchemas` from `@graphql-tools/schema`, rather than its old location in `@graphql-tools/merge`. You might see a reordering of the contents of your `graphql.schema` file. + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`c2bb6a9a5`](https://github.com/keystonejs/keystone/commit/c2bb6a9a596fc52a3c61ec5d91c79758e417e61d), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + - @keystone-ui/notice@4.0.1 + - @keystone-next/admin-ui-utils@5.0.6 + ## 30.0.0 ### Major Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index d02646024d2..1021aa7ca09 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/auth", - "version": "30.0.0", + "version": "31.0.0", "license": "MIT", "main": "dist/auth.cjs.js", "module": "dist/auth.esm.js", @@ -8,25 +8,25 @@ "node": "^12.20 || >= 14.13" }, "dependencies": { - "@babel/runtime": "^7.14.8", - "@graphql-tools/merge": "^6.2.16", - "@keystone-next/admin-ui-utils": "^5.0.5", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/types": "^23.0.0", + "@babel/runtime": "^7.15.3", + "@graphql-tools/schema": "^8.1.1", + "@keystone-next/admin-ui-utils": "^5.0.6", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/types": "^24.0.0", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.1.1", "@keystone-ui/fields": "^4.1.1", - "@keystone-ui/notice": "^4.0.0", + "@keystone-ui/notice": "^4.0.1", "cross-fetch": "^3.1.4", "fast-deep-equal": "^3.1.3", "graphql": "^15.5.1" }, "devDependencies": { - "@keystone-next/keystone": "^23.0.0", + "@keystone-next/keystone": "^24.0.0", "react": "^17.0.2" }, "peerDependencies": { - "@keystone-next/keystone": "^23.0.0", + "@keystone-next/keystone": "^24.0.0", "react": "^17.0.2" }, "preconstruct": { diff --git a/packages/auth/src/schema.ts b/packages/auth/src/schema.ts index bc78ec3fd19..64cbef1e917 100644 --- a/packages/auth/src/schema.ts +++ b/packages/auth/src/schema.ts @@ -1,4 +1,4 @@ -import { mergeSchemas } from '@graphql-tools/merge'; +import { mergeSchemas } from '@graphql-tools/schema'; import { ExtendGraphqlSchema } from '@keystone-next/types'; import { diff --git a/packages/cloudinary/CHANGELOG.md b/packages/cloudinary/CHANGELOG.md index bdc6d3709d8..2ccbf371910 100644 --- a/packages/cloudinary/CHANGELOG.md +++ b/packages/cloudinary/CHANGELOG.md @@ -1,5 +1,13 @@ # @keystone-next/cloudinary +## 6.0.6 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/types@24.0.0 + ## 6.0.5 ### Patch Changes diff --git a/packages/cloudinary/package.json b/packages/cloudinary/package.json index 85814f1e951..5b96740dc23 100644 --- a/packages/cloudinary/package.json +++ b/packages/cloudinary/package.json @@ -1,18 +1,18 @@ { "name": "@keystone-next/cloudinary", - "version": "6.0.5", + "version": "6.0.6", "license": "MIT", "main": "dist/cloudinary.cjs.js", "module": "dist/cloudinary.esm.js", "dependencies": { - "@babel/runtime": "^7.14.8", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/types": "^23.0.0", + "@babel/runtime": "^7.15.3", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/types": "^24.0.0", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.1.1", "@keystone-ui/fields": "^4.1.2", "@keystone-ui/pill": "^5.0.0", - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "cloudinary": "^1.26.3", "cuid": "^2.1.8", "graphql-upload": "^12.0.0", diff --git a/packages/fields-document/CHANGELOG.md b/packages/fields-document/CHANGELOG.md index f76d4cb0624..dae1a7275d3 100644 --- a/packages/fields-document/CHANGELOG.md +++ b/packages/fields-document/CHANGELOG.md @@ -1,5 +1,40 @@ # @keystone-next/fields-document +## 8.0.0 + +### Major Changes + +- [#6095](https://github.com/keystonejs/keystone/pull/6095) [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Updated filters to be nested instead of flattened and add top-level `NOT` operator. See the [Query Filter API docs](https://keystonejs.com/docs/apis/filters) and the upgrade guide for more information. + + ```graphql + query { + posts(where: { title: { contains: "Something" } }) { + title + content + } + } + ``` + +### Patch Changes + +- [#6250](https://github.com/keystonejs/keystone/pull/6250) [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450) Thanks [@timleslie](https://github.com/timleslie)! - Updated internal type definitions. + +* [#6318](https://github.com/keystonejs/keystone/pull/6318) [`e985aa010`](https://github.com/keystonejs/keystone/commit/e985aa0104d30a779f21ec05d80e6b98ece87dfb) Thanks [@raveling](https://github.com/raveling)! - Updated the document editor's expanded view so that you can click on any of the empty space below the content to focus the editor + +- [#6207](https://github.com/keystonejs/keystone/pull/6207) [`69f47bfed`](https://github.com/keystonejs/keystone/commit/69f47bfed1eaa1269cfdc42071268a914bd4aa17) Thanks [@timleslie](https://github.com/timleslie)! - Suppressed error logging during tests. + +* [#6197](https://github.com/keystonejs/keystone/pull/6197) [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The generated CRUD queries, and some of the input types, in the GraphQL API have been renamed. + + If you have a list called `Item`, the query for multiple values, `allItems` will be renamed to `items`. The query for a single value, `Item`, will be renamed to `item`. + + Also, the input type used in the `updateItems` mutation has been renamed from `ItemsUpdateInput` to `ItemUpdateArgs`. + +* Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + - @keystone-next/admin-ui-utils@5.0.6 + ## 7.0.3 ### Patch Changes diff --git a/packages/fields-document/package.json b/packages/fields-document/package.json index fa2373d04ca..5484772ac8f 100644 --- a/packages/fields-document/package.json +++ b/packages/fields-document/package.json @@ -1,7 +1,7 @@ { "name": "@keystone-next/fields-document", "description": "KeystoneJS Document Field Type", - "version": "7.0.3", + "version": "8.0.0", "main": "dist/fields-document.cjs.js", "module": "dist/fields-document.esm.js", "files": [ @@ -19,20 +19,20 @@ ] }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@braintree/sanitize-url": "^5.0.2", "@emotion/weak-memoize": "^0.2.5", - "@keystone-next/admin-ui-utils": "^5.0.5", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/types": "^23.0.0", + "@keystone-next/admin-ui-utils": "^5.0.6", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.1.1", "@keystone-ui/fields": "^4.1.1", "@keystone-ui/icons": "^4.0.0", "@keystone-ui/popover": "^4.0.1", "@keystone-ui/tooltip": "^4.0.1", - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "apply-ref": "^1.0.0", "fp-ts": "^2.11.1", "graphql": "^15.5.1", diff --git a/packages/fields-document/src/DocumentEditor/index.tsx b/packages/fields-document/src/DocumentEditor/index.tsx index 1a698698273..f68c89d7fef 100644 --- a/packages/fields-document/src/DocumentEditor/index.tsx +++ b/packages/fields-document/src/DocumentEditor/index.tsx @@ -226,7 +226,7 @@ export function DocumentEditor({ documentFeatures: DocumentFeatures; }) { const isShiftPressedRef = useKeyDownRef('Shift'); - const { colors } = useTheme(); + const { colors, spacing } = useTheme(); const [expanded, setExpanded] = useState(false); const editor = useMemo( () => createDocumentEditor(documentFeatures, componentBlocks, relationships, isShiftPressedRef), @@ -235,7 +235,11 @@ export function DocumentEditor({ return (
    + + { // for debugging false && @@ -345,9 +359,11 @@ export function DocumentEditorProvider({ export function DocumentEditorEditable({ autoFocus, readOnly, + className, }: { autoFocus?: boolean; readOnly?: boolean; + className?: string; }) { const editor = useSlate(); const componentBlocks = useContext(ComponentBlockContext); @@ -401,6 +417,7 @@ export function DocumentEditorEditable({ readOnly={readOnly} renderElement={renderElement} renderLeaf={renderLeaf} + className={className} /> ); } @@ -429,7 +446,9 @@ function Debugger() { const orderedListStyles = ['lower-roman', 'decimal', 'lower-alpha']; const unorderedListStyles = ['square', 'disc', 'circle']; -let styles: any = {}; +let styles: any = { + flex: 1, +}; let listDepth = 10; diff --git a/packages/fields-document/src/relationship-data.tsx b/packages/fields-document/src/relationship-data.tsx index 553ab970fab..ede96091c09 100644 --- a/packages/fields-document/src/relationship-data.tsx +++ b/packages/fields-document/src/relationship-data.tsx @@ -27,7 +27,7 @@ export function addRelationshipData( let val = await graphQLAPI.run({ query: `query($ids: [ID!]!) {items:${ gqlNames(relationship.listKey).listQueryName - }(where: {id_in:$ids}) {${idFieldAlias}:id ${labelFieldAlias}:${labelField}\n${ + }(where: { id: { in: $ids } }) {${idFieldAlias}:id ${labelFieldAlias}:${labelField}\n${ relationship.selection || '' }}}`, variables: { ids }, diff --git a/packages/fields/CHANGELOG.md b/packages/fields/CHANGELOG.md index 22b049863d6..cafa88f5de0 100644 --- a/packages/fields/CHANGELOG.md +++ b/packages/fields/CHANGELOG.md @@ -1,5 +1,122 @@ # @keystone-next/fields +## 14.0.0 + +### Major Changes + +- [#6280](https://github.com/keystonejs/keystone/pull/6280) [`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `gqlType` option to `autoIncrement` field type. The field type will now always be represented with an `Int` in GraphQL + +* [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `_ListKeyMeta` and `_toManyRelationshipFieldMeta` fields. You should use `listKeyCount` and `toManyRelationshipFieldCount` instead + +- [#6266](https://github.com/keystonejs/keystone/pull/6266) [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Renamed `first` argument in find many queries to `take` to align with Prisma. + + ```graphql + type Query { + users( + where: UserWhereInput! = {} + orderBy: [UserOrderByInput!]! = [] + # previously was first: Int + take: Int + skip: Int! = 0 + ): [User!] + # ... + } + + type User { + # ... + posts( + where: PostWhereInput! = {} + orderBy: [PostOrderByInput!]! = [] + # previously was first: Int + take: Int + skip: Int! = 0 + ): [Post!] + # ... + } + ``` + +* [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `search` argument from the GraphQL API for finding many items, Lists/DB API and to-many relationship fields. You should use `contains` filters instead. + +- [#6095](https://github.com/keystonejs/keystone/pull/6095) [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Updated filters to be nested instead of flattened and add top-level `NOT` operator. See the [Query Filter API docs](https://keystonejs.com/docs/apis/filters) and the upgrade guide for more information. + + ```graphql + query { + posts(where: { title: { contains: "Something" } }) { + title + content + } + } + ``` + +* [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `sortBy` argument from the GraphQL API for finding many items, Lists/DB API and to-many relationship fields. You should use `orderBy` instead. + +- [#6217](https://github.com/keystonejs/keystone/pull/6217) [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - `disconnectAll` has been renamed to `disconnect` in to-one relationship inputs and the old `disconnect` field has been removed. There are also seperate input types for create and update where the input for create doesn't have `disconnect`. It's also now required that if you provide a to-one relationship input, you must provide exactly one field to the input. + + If you have a list called `Item`, the to-one relationship inputs now look like this: + + ```graphql + input ItemRelateToOneForCreateInput { + create: ItemCreateInput + connect: ItemWhereUniqueInput + } + input ItemRelateToOneForUpdateInput { + create: ItemCreateInput + connect: ItemWhereUniqueInput + disconnect: Boolean + } + ``` + +* [#6224](https://github.com/keystonejs/keystone/pull/6224) [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - `disconnectAll` has been replaced by `set` in to-many relationship inputs, the equivalent to `disconnectAll: true` is now `set: []`. There are also seperate input types for create and update where the input for create doesn't have `disconnect` or `set`. The inputs in the lists in the input field are now also non-null. + + If you have a list called `Item`, the to-many relationship inputs now look like this: + + ```graphql + input ItemRelateToManyForCreateInput { + create: [ItemCreateInput!] + connect: [ItemWhereUniqueInput!] + } + input ItemRelateToManyForUpdateInput { + disconnect: [ItemWhereUniqueInput!] + set: [ItemWhereUniqueInput!] + create: [ItemCreateInput!] + connect: [ItemWhereUniqueInput!] + } + ``` + +- [#6211](https://github.com/keystonejs/keystone/pull/6211) [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The update mutations now accept `where` unique inputs instead of only an `id` and the `where` and `data` arguments are non-null. + + If you have a list called `Item`, the update mutations now look like this: + + ```graphql + type Mutation { + updateItem(where: ItemWhereUniqueInput!, data: ItemUpdateInput!): Item + updateItems(data: [ItemUpdateArgs!]!): [Item] + } + + input ItemUpdateArgs { + where: ItemWhereUniqueInput! + data: ItemUpdateInput! + } + ``` + +### Patch Changes + +- [#6237](https://github.com/keystonejs/keystone/pull/6237) [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Updated timestamp field to default time to 00:00 when no time is selected. + +* [#6197](https://github.com/keystonejs/keystone/pull/6197) [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The generated CRUD queries, and some of the input types, in the GraphQL API have been renamed. + + If you have a list called `Item`, the query for multiple values, `allItems` will be renamed to `items`. The query for a single value, `Item`, will be renamed to `item`. + + Also, the input type used in the `updateItems` mutation has been renamed from `ItemsUpdateInput` to `ItemUpdateArgs`. + +* Updated dependencies [[`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`c2bb6a9a5`](https://github.com/keystonejs/keystone/commit/c2bb6a9a596fc52a3c61ec5d91c79758e417e61d), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`6cd7ab78e`](https://github.com/keystonejs/keystone/commit/6cd7ab78e018fa0ffaddc1258426d23da19cd854), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/keystone@24.0.0 + - @keystone-next/types@24.0.0 + - @keystone-ui/toast@4.0.2 + - @keystone-ui/segmented-control@4.0.2 + - @keystone-next/admin-ui-utils@5.0.6 + - @keystone-next/utils@1.0.4 + ## 13.0.0 ### Major Changes diff --git a/packages/fields/package.json b/packages/fields/package.json index 3252816db45..27336167293 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/fields", - "version": "13.0.0", + "version": "14.0.0", "license": "MIT", "main": "dist/fields.cjs.js", "module": "dist/fields.esm.js", @@ -11,11 +11,11 @@ "mime": "^2.5.2" }, "dependencies": { - "@babel/runtime": "^7.14.8", - "@keystone-next/admin-ui-utils": "^5.0.5", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/types": "^23.0.0", - "@keystone-next/utils": "^1.0.3", + "@babel/runtime": "^7.15.3", + "@keystone-next/admin-ui-utils": "^5.0.6", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/types": "^24.0.0", + "@keystone-next/utils": "^1.0.4", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.1.1", "@keystone-ui/fields": "^4.1.2", @@ -23,11 +23,11 @@ "@keystone-ui/loading": "^4.0.0", "@keystone-ui/modals": "^4.0.0", "@keystone-ui/pill": "^5.0.0", - "@keystone-ui/segmented-control": "^4.0.1", - "@keystone-ui/toast": "^4.0.1", + "@keystone-ui/segmented-control": "^4.0.2", + "@keystone-ui/toast": "^4.0.2", "@keystone-ui/tooltip": "^4.0.1", "@types/bcryptjs": "^2.4.2", - "@types/react": "^17.0.15", + "@types/react": "^17.0.18", "bcryptjs": "^2.4.3", "bytes": "^3.1.0", "copy-to-clipboard": "^3.3.1", diff --git a/packages/fields/src/tests/test-fixtures.ts b/packages/fields/src/tests/test-fixtures.ts index b83e5225be1..0c72dadf71c 100644 --- a/packages/fields/src/tests/test-fixtures.ts +++ b/packages/fields/src/tests/test-fixtures.ts @@ -63,20 +63,20 @@ export const filterTests = (withKeystone: any) => { ); test( - 'Filter: id', + 'Filter: equals', withKeystone(async ({ context }: { context: KeystoneContext }) => { const IDs = await getIDs(context); const id = IDs['person2']; - return match(context, { id }, [{ id: IDs['person2'], name: 'person2' }]); + return match(context, { id: { equals: id } }, [{ id: IDs['person2'], name: 'person2' }]); }) ); test( - 'Filter: id_not', + 'Filter: not equals', withKeystone(async ({ context }: { context: KeystoneContext }) => { const IDs = await getIDs(context); const id = IDs['person2']; - return match(context, { id_not: id }, [ + return match(context, { id: { not: { equals: id } } }, [ { id: IDs['person1'], name: 'person1' }, { id: IDs['person3'], name: 'person3' }, { id: IDs['person4'], name: 'person4' }, @@ -85,12 +85,12 @@ export const filterTests = (withKeystone: any) => { ); test( - 'Filter: id_in', + 'Filter: in', withKeystone(async ({ context }: { context: KeystoneContext }) => { const IDs = await getIDs(context); const id2 = IDs['person2']; const id3 = IDs['person3']; - return match(context, { id_in: [id2, id3] }, [ + return match(context, { id: { in: [id2, id3] } }, [ { id: IDs['person2'], name: 'person2' }, { id: IDs['person3'], name: 'person3' }, ]); @@ -98,27 +98,27 @@ export const filterTests = (withKeystone: any) => { ); test( - 'Filter: id_in - empty list', + 'Filter: in - empty list', withKeystone(({ context }: { context: KeystoneContext }) => { - return match(context, { id_in: [] }, []); + return match(context, { id: { in: [] } }, []); }) ); test( - 'Filter: id_in - missing id', + 'Filter: in - missing id', withKeystone(({ context }: { context: KeystoneContext }) => { const fakeID = 'cdafasdfasd'; - return match(context, { id_in: [fakeID] }, []); + return match(context, { id: { in: [fakeID] } }, []); }) ); test( - 'Filter: id_not_in', + 'Filter: not in', withKeystone(async ({ context }: { context: KeystoneContext }) => { const IDs = await getIDs(context); const id2 = IDs['person2']; const id3 = IDs['person3']; - return match(context, { id_not_in: [id2, id3] }, [ + return match(context, { id: { not: { in: [id2, id3] } } }, [ { id: IDs['person1'], name: 'person1' }, { id: IDs['person4'], name: 'person4' }, ]); @@ -126,10 +126,10 @@ export const filterTests = (withKeystone: any) => { ); test( - 'Filter: id_not_in - empty list', + 'Filter: not in - empty list', withKeystone(async ({ context }: { context: KeystoneContext }) => { const IDs = await getIDs(context); - return match(context, { id_not_in: [] }, [ + return match(context, { id: { not: { in: [] } } }, [ { id: IDs['person1'], name: 'person1' }, { id: IDs['person2'], name: 'person2' }, { id: IDs['person3'], name: 'person3' }, @@ -139,11 +139,11 @@ export const filterTests = (withKeystone: any) => { ); test( - 'Filter: id_not_in - missing id', + 'Filter: not in - missing id', withKeystone(async ({ context }: { context: KeystoneContext }) => { const IDs = await getIDs(context); const fakeID = 'cdafasdfasd'; - return match(context, { id_not_in: [fakeID] }, [ + return match(context, { id: { not: { in: [fakeID] } } }, [ { id: IDs['person1'], name: 'person1' }, { id: IDs['person2'], name: 'person2' }, { id: IDs['person3'], name: 'person3' }, diff --git a/packages/fields/src/types/autoIncrement/index.ts b/packages/fields/src/types/autoIncrement/index.ts index ceb6ef68e0f..a4484ec6a65 100644 --- a/packages/fields/src/types/autoIncrement/index.ts +++ b/packages/fields/src/types/autoIncrement/index.ts @@ -7,6 +7,7 @@ import { legacyFilters, orderDirectionEnum, schema, + filters, } from '@keystone-next/types'; import { resolveView } from '../../resolve-view'; import { getIndexType } from '../../get-index-type'; @@ -17,7 +18,6 @@ export type AutoIncrementFieldConfig = {}): FieldTypeFunc => meta => { - const type = meta.fieldKey === 'id' || gqlType === 'ID' ? schema.ID : schema.Int; - const __legacy = { - isRequired, - defaultValue, - filters: { - fields: { - ...legacyFilters.fields.equalityInputFields(meta.fieldKey, type), - ...legacyFilters.fields.orderingInputFields(meta.fieldKey, type), - ...legacyFilters.fields.inInputFields(meta.fieldKey, type), - }, - impls: { - ...equalityConditions(meta.fieldKey, x => Number(x) || -1), - ...legacyFilters.impls.orderingConditions(meta.fieldKey, x => Number(x) || -1), - ...inConditions(meta.fieldKey, x => x.map((xx: any) => Number(xx) || -1)), - }, - }, - }; - if (meta.fieldKey === 'id') { - return fieldType({ - kind: 'scalar', - mode: 'required', - scalar: 'Int', - default: { kind: 'autoincrement' }, - })({ - ...config, - input: { - // TODO: fix the fact that TS did not catch that a resolver is needed here - uniqueWhere: { - arg: schema.arg({ type }), - resolve(value) { - return Number(value); - }, - }, - orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, - }, - output: schema.field({ - type: schema.nonNull(schema.ID), - resolve({ value }) { - return value.toString(); - }, - }), - views: resolveView('integer/views'), - __legacy, - }); - } - const inputResolver = (val: number | string | null | undefined) => { - if (val == null) { - return val; - } - return Number(val); - }; return fieldType({ kind: 'scalar', mode: 'optional', @@ -90,20 +38,35 @@ export const autoIncrement = })({ ...config, input: { - uniqueWhere: isUnique ? { arg: schema.arg({ type }), resolve: x => Number(x) } : undefined, - create: { arg: schema.arg({ type }), resolve: inputResolver }, - update: { arg: schema.arg({ type }), resolve: inputResolver }, + where: { + arg: schema.arg({ + type: filters[meta.provider].Int.optional, + }), + resolve: filters.resolveCommon, + }, + uniqueWhere: isUnique ? { arg: schema.arg({ type: schema.Int }) } : undefined, + create: { arg: schema.arg({ type: schema.Int }) }, + update: { arg: schema.arg({ type: schema.Int }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, }, - output: schema.field({ - type, - resolve({ value }) { - if (value === null) return null; - return type === schema.ID ? value.toString() : value; - }, - }), + output: schema.field({ type: schema.Int }), views: resolveView('integer/views'), - __legacy, + __legacy: { + isRequired, + defaultValue, + filters: { + fields: { + ...legacyFilters.fields.equalityInputFields(meta.fieldKey, schema.Int), + ...legacyFilters.fields.orderingInputFields(meta.fieldKey, schema.Int), + ...legacyFilters.fields.inInputFields(meta.fieldKey, schema.Int), + }, + impls: { + ...equalityConditions(meta.fieldKey, x => Number(x) || -1), + ...legacyFilters.impls.orderingConditions(meta.fieldKey, x => Number(x) || -1), + ...inConditions(meta.fieldKey, x => x.map((xx: any) => Number(xx) || -1)), + }, + }, + }, }); }; diff --git a/packages/fields/src/types/autoIncrement/tests/test-fixtures.ts b/packages/fields/src/types/autoIncrement/tests/test-fixtures.ts index 80796704315..c4659c1f360 100644 --- a/packages/fields/src/types/autoIncrement/tests/test-fixtures.ts +++ b/packages/fields/src/types/autoIncrement/tests/test-fixtures.ts @@ -2,13 +2,10 @@ import { KeystoneContext } from '@keystone-next/types'; import { text } from '../../text'; import { autoIncrement } from '..'; -type MatrixValue = typeof testMatrix[number]; - export const name = 'AutoIncrement'; export const typeFunction = autoIncrement; -export const testMatrix = ['ID', 'Int'] as const; -export const exampleValue = (matrixValue: MatrixValue) => (matrixValue === 'ID' ? '35' : 35); -export const exampleValue2 = (matrixValue: MatrixValue) => (matrixValue === 'ID' ? '36' : 36); +export const exampleValue = () => 35; +export const exampleValue2 = () => 36; export const supportsUnique = true; export const fieldName = 'orderNumber'; export const skipCreateTest = false; @@ -16,21 +13,9 @@ export const skipUpdateTest = true; export const unSupportedAdapterList = ['sqlite']; -// Be default, `AutoIncrement` are read-only. But for `isRequired` test purpose, we need to bypass these restrictions. -export const fieldConfig = (matrixValue: MatrixValue) => ({ - gqlType: matrixValue, - access: { create: true, update: true }, -}); - -export const getTestFields = (matrixValue: MatrixValue) => ({ +export const getTestFields = () => ({ name: text(), - orderNumber: autoIncrement({ - // The gqlType argument is not currently available on the type. - // This will be reviewed when we do our full field type API review - // @ts-ignore - gqlType: matrixValue, - access: { create: true }, - }), + orderNumber: autoIncrement(), }); export const initItems = () => { @@ -45,32 +30,20 @@ export const initItems = () => { ]; }; -export const storedValues = (matrixValue: MatrixValue) => - matrixValue === 'ID' - ? [ - { name: 'product1', orderNumber: '1' }, - { name: 'product2', orderNumber: '2' }, - { name: 'product3', orderNumber: '3' }, - { name: 'product4', orderNumber: '4' }, - { name: 'product5', orderNumber: '5' }, - { name: 'product6', orderNumber: '6' }, - { name: 'product7', orderNumber: '7' }, - ] - : [ - { name: 'product1', orderNumber: 1 }, - { name: 'product2', orderNumber: 2 }, - { name: 'product3', orderNumber: 3 }, - { name: 'product4', orderNumber: 4 }, - { name: 'product5', orderNumber: 5 }, - { name: 'product6', orderNumber: 6 }, - { name: 'product7', orderNumber: 7 }, - ]; +export const storedValues = () => [ + { name: 'product1', orderNumber: 1 }, + { name: 'product2', orderNumber: 2 }, + { name: 'product3', orderNumber: 3 }, + { name: 'product4', orderNumber: 4 }, + { name: 'product5', orderNumber: 5 }, + { name: 'product6', orderNumber: 6 }, + { name: 'product7', orderNumber: 7 }, +]; export const supportedFilters = () => []; -export const filterTests = (withKeystone: (arg: any) => any, matrixValue: MatrixValue) => { - const _storedValues = storedValues(matrixValue); - const _f = matrixValue === 'ID' ? (x: any) => x.toString() : (x: any) => x; +export const filterTests = (withKeystone: (arg: any) => any) => { + const _storedValues = storedValues(); const match = async (context: KeystoneContext, where: Record, expected: any[]) => expect( await context.lists.Test.findMany({ @@ -81,93 +54,79 @@ export const filterTests = (withKeystone: (arg: any) => any, matrixValue: Matrix ).toEqual(expected.map(i => _storedValues[i])); test( - 'Filter: orderNumber', - withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber: _f(1) }, [0]) - ) - ); - - test( - 'Filter: orderNumber_not', - withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_not: _f(1) }, [1, 2, 3, 4, 5, 6]) - ) - ); - - test( - 'Filter: orderNumber_not null', + 'Filter: equals', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_not: null }, [0, 1, 2, 3, 4, 5, 6]) + match(context, { orderNumber: { equals: 1 } }, [0]) ) ); test( - 'Filter: orderNumber_lt', + 'Filter: not', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_lt: _f(2) }, [0]) + match(context, { orderNumber: { not: { equals: 1 } } }, [1, 2, 3, 4, 5, 6]) ) ); test( - 'Filter: orderNumber_lte', + 'Filter: not null', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_lte: _f(2) }, [0, 1]) + match(context, { orderNumber: { not: null } }, [0, 1, 2, 3, 4, 5, 6]) ) ); test( - 'Filter: orderNumber_gt', + 'Filter: lt', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_gt: _f(2) }, [2, 3, 4, 5, 6]) + match(context, { orderNumber: { lt: 2 } }, [0]) ) ); test( - 'Filter: orderNumber_gte', + 'Filter: lte', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_gte: _f(2) }, [1, 2, 3, 4, 5, 6]) + match(context, { orderNumber: { lte: 2 } }, [0, 1]) ) ); test( - 'Filter: orderNumber_in (empty list)', + 'Filter: gt', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_in: [] }, []) + match(context, { orderNumber: { gt: 2 } }, [2, 3, 4, 5, 6]) ) ); test( - 'Filter: orderNumber_not_in (empty list)', + 'Filter: gte', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_not_in: [] }, [0, 1, 2, 3, 4, 5, 6]) + match(context, { orderNumber: { gte: 2 } }, [1, 2, 3, 4, 5, 6]) ) ); test( - 'Filter: orderNumber_in', + 'Filter: in (empty list)', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_in: ([1, 2, 3] as const).map(_f) }, [0, 1, 2]) + match(context, { orderNumber: { in: [] } }, []) ) ); test( - 'Filter: orderNumber_not_in', + 'Filter: not in (empty list)', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_not_in: [1, 2, 3].map(_f) }, [3, 4, 5, 6]) + match(context, { orderNumber: { notIn: [] } }, [0, 1, 2, 3, 4, 5, 6]) ) ); test( - 'Filter: orderNumber_in null', + 'Filter: in', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_in: [null] }, []) + match(context, { orderNumber: { in: [1, 2, 3] } }, [0, 1, 2]) ) ); test( - 'Filter: orderNumber_not_in null', + 'Filter: not in', withKeystone(({ context }: { context: KeystoneContext }) => - match(context, { orderNumber_not_in: [null] }, [0, 1, 2, 3, 4, 5, 6]) + match(context, { orderNumber: { notIn: [1, 2, 3] } }, [3, 4, 5, 6]) ) ); }; diff --git a/packages/fields/src/types/checkbox/index.ts b/packages/fields/src/types/checkbox/index.ts index 1e320de29a6..dd2b36fb891 100644 --- a/packages/fields/src/types/checkbox/index.ts +++ b/packages/fields/src/types/checkbox/index.ts @@ -7,6 +7,7 @@ import { legacyFilters, orderDirectionEnum, schema, + filters, } from '@keystone-next/types'; import { resolveView } from '../../resolve-view'; @@ -30,6 +31,10 @@ export const checkbox = return fieldType({ kind: 'scalar', mode: 'optional', scalar: 'Boolean' })({ ...config, input: { + where: { + arg: schema.arg({ type: filters[meta.provider].Boolean.optional }), + resolve: filters.resolveCommon, + }, create: { arg: schema.arg({ type: schema.Boolean }) }, update: { arg: schema.arg({ type: schema.Boolean }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, diff --git a/packages/fields/src/types/checkbox/views/index.tsx b/packages/fields/src/types/checkbox/views/index.tsx index 010f2f4a200..0b40d55766e 100644 --- a/packages/fields/src/types/checkbox/views/index.tsx +++ b/packages/fields/src/types/checkbox/views/index.tsx @@ -72,9 +72,8 @@ export const controller = (config: FieldControllerConfig): CheckboxController => Filter() { return null; }, - graphql({ type, value }) { - const key = type === 'is' ? `${config.path}` : `${config.path}_${type}`; - return { [key]: value }; + graphql({ type }) { + return { [config.path]: { equals: type === 'is' } }; }, Label({ label }) { return label.toLowerCase(); diff --git a/packages/fields/src/types/decimal/index.ts b/packages/fields/src/types/decimal/index.ts index a4b234cdda0..fdfdcaa1abf 100644 --- a/packages/fields/src/types/decimal/index.ts +++ b/packages/fields/src/types/decimal/index.ts @@ -8,6 +8,7 @@ import { Decimal, legacyFilters, FieldDefaultValue, + filters, } from '@keystone-next/types'; import { resolveView } from '../../resolve-view'; import { getIndexType } from '../../get-index-type'; @@ -66,6 +67,10 @@ export const decimal = })({ ...config, input: { + where: { + arg: schema.arg({ type: filters[meta.provider].Decimal.optional }), + resolve: filters.resolveCommon, + }, create: { arg: schema.arg({ type: schema.String }), resolve(val) { diff --git a/packages/fields/src/types/decimal/views/index.tsx b/packages/fields/src/types/decimal/views/index.tsx index 2d135af2d15..7e8b1cea9b8 100644 --- a/packages/fields/src/types/decimal/views/index.tsx +++ b/packages/fields/src/types/decimal/views/index.tsx @@ -70,14 +70,16 @@ export const controller = (config: Config): FieldController => { }, graphql: ({ type, value }) => { - const key = type === 'is' ? config.path : `${config.path}_${type}`; const valueWithoutWhitespace = value.replace(/\s/g, ''); - - return { - [key]: ['in', 'not_in'].includes(type) - ? valueWithoutWhitespace.split(',').map(i => i) - : valueWithoutWhitespace, - }; + const parsed = + type === 'in' || type === 'not_in' + ? valueWithoutWhitespace.split(',') + : valueWithoutWhitespace; + if (type === 'not') { + return { [config.path]: { not: { equals: parsed } } }; + } + const key = type === 'is' ? 'equals' : type === 'not_in' ? 'notIn' : type; + return { [config.path]: { [key]: parsed } }; }, Label({ label, value, type }) { let renderedValue = value; diff --git a/packages/fields/src/types/float/index.ts b/packages/fields/src/types/float/index.ts index c1baa891d85..a7522d3d017 100644 --- a/packages/fields/src/types/float/index.ts +++ b/packages/fields/src/types/float/index.ts @@ -7,6 +7,7 @@ import { orderDirectionEnum, legacyFilters, FieldDefaultValue, + filters, } from '@keystone-next/types'; import { resolveView } from '../../resolve-view'; import { getIndexType } from '../../get-index-type'; @@ -36,6 +37,10 @@ export const float = })({ ...config, input: { + where: { + arg: schema.arg({ type: filters[meta.provider].Float.optional }), + resolve: filters.resolveCommon, + }, create: { arg: schema.arg({ type: schema.Float }) }, update: { arg: schema.arg({ type: schema.Float }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, diff --git a/packages/fields/src/types/float/views/index.tsx b/packages/fields/src/types/float/views/index.tsx index 42c845075e1..8dd79257d11 100644 --- a/packages/fields/src/types/float/views/index.tsx +++ b/packages/fields/src/types/float/views/index.tsx @@ -68,14 +68,16 @@ export const controller = (config: FieldControllerConfig): FieldController { - const key = type === 'is' ? config.path : `${config.path}_${type}`; const valueWithoutWhitespace = value.replace(/\s/g, ''); - - return { - [key]: ['in', 'not_in'].includes(type) - ? valueWithoutWhitespace.split(',').map(i => parseFloat(i)) - : parseFloat(valueWithoutWhitespace), - }; + const parsed = + type === 'in' || type === 'not_in' + ? valueWithoutWhitespace.split(',').map(x => parseFloat(x)) + : parseFloat(valueWithoutWhitespace); + if (type === 'not') { + return { [config.path]: { not: { equals: parsed } } }; + } + const key = type === 'is' ? 'equals' : type === 'not_in' ? 'notIn' : type; + return { [config.path]: { [key]: parsed } }; }, Label({ label, value, type }) { let renderedValue = value; diff --git a/packages/fields/src/types/integer/index.ts b/packages/fields/src/types/integer/index.ts index 744bc9d849a..96c07d9af88 100644 --- a/packages/fields/src/types/integer/index.ts +++ b/packages/fields/src/types/integer/index.ts @@ -7,6 +7,7 @@ import { legacyFilters, orderDirectionEnum, schema, + filters, } from '@keystone-next/types'; import { resolveView } from '../../resolve-view'; import { getIndexType } from '../../get-index-type'; @@ -37,6 +38,10 @@ export const integer = ...config, input: { uniqueWhere: isUnique ? { arg: schema.arg({ type: schema.Int }) } : undefined, + where: { + arg: schema.arg({ type: filters[meta.provider].Int.optional }), + resolve: filters.resolveCommon, + }, create: { arg: schema.arg({ type: schema.Int }) }, update: { arg: schema.arg({ type: schema.Int }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, diff --git a/packages/fields/src/types/integer/views/index.tsx b/packages/fields/src/types/integer/views/index.tsx index f05f3427065..c1de20b4f05 100644 --- a/packages/fields/src/types/integer/views/index.tsx +++ b/packages/fields/src/types/integer/views/index.tsx @@ -72,14 +72,16 @@ export const controller = (config: FieldControllerConfig): FieldController { - const key = type === 'is' ? config.path : `${config.path}_${type}`; const valueWithoutWhitespace = value.replace(/\s/g, ''); - - return { - [key]: ['in', 'not_in'].includes(type) - ? valueWithoutWhitespace.split(',').map(i => parseInt(i)) - : parseInt(valueWithoutWhitespace), - }; + const parsed = + type === 'in' || type === 'not_in' + ? valueWithoutWhitespace.split(',').map(x => parseInt(x)) + : parseInt(valueWithoutWhitespace); + if (type === 'not') { + return { [config.path]: { not: { equals: parsed } } }; + } + const key = type === 'is' ? 'equals' : type === 'not_in' ? 'notIn' : type; + return { [config.path]: { [key]: parsed } }; }, Label({ label, value, type }) { let renderedValue = value; diff --git a/packages/fields/src/types/password/index.ts b/packages/fields/src/types/password/index.ts index 3f3a10017d3..3bfbc35dc9e 100644 --- a/packages/fields/src/types/password/index.ts +++ b/packages/fields/src/types/password/index.ts @@ -34,6 +34,13 @@ const PasswordState = schema.object<{ isSet: boolean }>()({ }, }); +const PasswordFilter = schema.inputObject({ + name: 'PasswordFilter', + fields: { + isSet: schema.arg({ type: schema.nonNull(schema.Boolean) }), + }, +}); + const bcryptHashRegex = /^\$2[aby]?\$\d{1,2}\$[.\/A-Za-z0-9]{53}$/; export const password = @@ -89,6 +96,20 @@ export const password = })({ ...config, input: { + where: { + arg: schema.arg({ type: PasswordFilter }), + resolve(val) { + if (val === null) { + throw new Error('Password filters cannot be set to null'); + } + if (val.isSet) { + return { + not: null, + }; + } + return null; + }, + }, create: { arg: schema.arg({ type: schema.String }), resolve: inputResolver, diff --git a/packages/fields/src/types/password/views/index.tsx b/packages/fields/src/types/password/views/index.tsx index 0c1ecbfdb5a..7b8b29a3aad 100644 --- a/packages/fields/src/types/password/views/index.tsx +++ b/packages/fields/src/types/password/views/index.tsx @@ -200,8 +200,8 @@ export const controller = ( /> ); }, - graphql: ({ type, value }) => { - return { [`${config.path}_${type}`]: value }; + graphql: ({ value }) => { + return { [config.path]: { isSet: value } }; }, Label({ value }) { return value ? 'is set' : 'is not set'; diff --git a/packages/fields/src/types/relationship/index.ts b/packages/fields/src/types/relationship/index.ts index 65014067a40..6d937dcf8f1 100644 --- a/packages/fields/src/types/relationship/index.ts +++ b/packages/fields/src/types/relationship/index.ts @@ -153,6 +153,12 @@ export const relationship = })({ ...commonConfig, input: { + where: { + arg: schema.arg({ type: listTypes.relateTo.many.where }), + resolve(value, context, resolve) { + return resolve(value); + }, + }, create: { arg: schema.arg({ type: listTypes.relateTo.many.create, @@ -217,6 +223,12 @@ export const relationship = })({ ...commonConfig, input: { + where: { + arg: schema.arg({ type: listTypes.where }), + resolve(value, context, resolve) { + return resolve(value); + }, + }, create: { arg: schema.arg({ type: listTypes.relateTo.one.create }), async resolve(value, context, resolve) { diff --git a/packages/fields/src/types/relationship/tests/implementation.test.ts b/packages/fields/src/types/relationship/tests/implementation.test.ts index ea93d65957f..54de9fd8d69 100644 --- a/packages/fields/src/types/relationship/tests/implementation.test.ts +++ b/packages/fields/src/types/relationship/tests/implementation.test.ts @@ -123,7 +123,7 @@ describe('Type Generation', () => { expect(printType(schema.getType('Test')!)).toMatchInlineSnapshot(` "type Test { id: ID! - foo(where: ZipWhereInput! = {}, orderBy: [ZipOrderByInput!]! = [], first: Int, skip: Int! = 0): [Zip!] + foo(where: ZipWhereInput! = {}, orderBy: [ZipOrderByInput!]! = [], take: Int, skip: Int! = 0): [Zip!] fooCount(where: ZipWhereInput! = {}): Int }" `); @@ -135,7 +135,7 @@ describe('Type Generation', () => { expect(printType(schema.getType('Test')!)).toMatchInlineSnapshot(` "type Test { id: ID! - foo(where: ZipWhereInput! = {}, orderBy: [ZipOrderByInput!]! = [], first: Int, skip: Int! = 0): [Zip!] + foo(where: ZipWhereInput! = {}, orderBy: [ZipOrderByInput!]! = [], take: Int, skip: Int! = 0): [Zip!] }" `); }); diff --git a/packages/fields/src/types/relationship/views/RelationshipSelect.tsx b/packages/fields/src/types/relationship/views/RelationshipSelect.tsx index 44446e7bdd1..6f0a6980c26 100644 --- a/packages/fields/src/types/relationship/views/RelationshipSelect.tsx +++ b/packages/fields/src/types/relationship/views/RelationshipSelect.tsx @@ -105,10 +105,10 @@ export const RelationshipSelect = ({ const QUERY: TypedDocumentNode< { items: { [idField]: string; [labelField]: string | null }[]; count: number }, - { where: Record; first: number; skip: number } + { where: Record; take: number; skip: number } > = gql` - query RelationshipSelect($where: ${list.gqlNames.whereInputName}!, $first: Int!, $skip: Int!) { - items: ${list.gqlNames.listQueryName}(where: $where, first: $first, skip: $skip) { + query RelationshipSelect($where: ${list.gqlNames.whereInputName}!, $take: Int!, $skip: Int!) { + items: ${list.gqlNames.listQueryName}(where: $where, take: $take, skip: $skip) { ${idField}: id ${labelField}: ${list.labelField} ${extraSelection} @@ -121,7 +121,7 @@ export const RelationshipSelect = ({ const { data, error, loading, fetchMore } = useQuery(QUERY, { fetchPolicy: 'network-only', - variables: { where, first: initialItemsToLoad, skip: 0 }, + variables: { where, take: initialItemsToLoad, skip: 0 }, }); const count = data?.count || 0; @@ -146,10 +146,10 @@ export const RelationshipSelect = ({ if (!loading && isIntersecting && options.length < count) { const QUERY: TypedDocumentNode< { items: { [idField]: string; [labelField]: string | null }[] }, - { where: Record; first: number; skip: number } + { where: Record; take: number; skip: number } > = gql` - query RelationshipSelectMore($where: ${list.gqlNames.whereInputName}!, $first: Int!, $skip: Int!) { - items: ${list.gqlNames.listQueryName}(where: $where, first: $first, skip: $skip) { + query RelationshipSelectMore($where: ${list.gqlNames.whereInputName}!, $take: Int!, $skip: Int!) { + items: ${list.gqlNames.listQueryName}(where: $where, take: $take, skip: $skip) { ${labelField}: ${list.labelField} ${idField}: id ${extraSelection} @@ -160,7 +160,7 @@ export const RelationshipSelect = ({ query: QUERY, variables: { where, - first: subsequentItemsToLoad, + take: subsequentItemsToLoad, skip: data!.items.length, }, updateQuery: (prev, { fetchMoreResult }) => { diff --git a/packages/fields/src/types/select/index.ts b/packages/fields/src/types/select/index.ts index 78130da1f6b..8ec53f754e8 100644 --- a/packages/fields/src/types/select/index.ts +++ b/packages/fields/src/types/select/index.ts @@ -8,6 +8,7 @@ import { legacyFilters, orderDirectionEnum, schema, + filters, } from '@keystone-next/types'; // @ts-ignore import inflection from 'inflection'; @@ -68,6 +69,10 @@ export const select = })({ ...commonConfig, input: { + where: { + arg: schema.arg({ type: filters[meta.provider].Int.optional }), + resolve: filters.resolveCommon, + }, create: { arg: schema.arg({ type: schema.Int }) }, update: { arg: schema.arg({ type: schema.Int }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, @@ -96,6 +101,10 @@ export const select = )({ ...commonConfig, input: { + where: { + arg: schema.arg({ type: filters[meta.provider].enum(graphQLType).optional }), + resolve: filters.resolveCommon, + }, create: { arg: schema.arg({ type: graphQLType }) }, update: { arg: schema.arg({ type: graphQLType }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, @@ -109,6 +118,10 @@ export const select = return fieldType({ kind: 'scalar', scalar: 'String', mode: 'optional', index })({ ...commonConfig, input: { + where: { + arg: schema.arg({ type: filters[meta.provider].String.optional }), + resolve: filters.resolveString, + }, create: { arg: schema.arg({ type: schema.String }) }, update: { arg: schema.arg({ type: schema.String }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, diff --git a/packages/fields/src/types/select/views/index.tsx b/packages/fields/src/types/select/views/index.tsx index 6fccd3a0bc0..ca3c939064f 100644 --- a/packages/fields/src/types/select/views/index.tsx +++ b/packages/fields/src/types/select/views/index.tsx @@ -116,31 +116,9 @@ export const controller = ( /> ); }, - - graphql: ({ type, value: options }) => { - const inverted = type === 'not_matches'; - - if (!options.length) { - return { - [`${config.path}${inverted ? '_not' : ''}`]: null, - }; - } - - const isMulti = options.length > 1; - - let key = config.path; - if (isMulti && inverted) { - key = `${config.path}_not_in`; - } else if (isMulti) { - key = `${config.path}_in`; - } else if (inverted) { - key = `${config.path}_not`; - } - - const value = isMulti ? options.map(x => t(x.value)) : t(options[0].value); - - return { [key]: value }; - }, + graphql: ({ type, value: options }) => ({ + [config.path]: { [type === 'not_matches' ? 'notIn' : 'in']: options.map(x => t(x.value)) }, + }), Label({ type, value }) { if (!value.length) { return type === 'not_matches' ? `is set` : `has no value`; diff --git a/packages/fields/src/types/text/index.ts b/packages/fields/src/types/text/index.ts index 8fe1e69f746..ecedaf0a5b4 100644 --- a/packages/fields/src/types/text/index.ts +++ b/packages/fields/src/types/text/index.ts @@ -7,6 +7,7 @@ import { orderDirectionEnum, FieldTypeFunc, legacyFilters, + filters, } from '@keystone-next/types'; import { resolveView } from '../../resolve-view'; import { getIndexType } from '../../get-index-type'; @@ -40,6 +41,10 @@ export const text = ...config, input: { uniqueWhere: isUnique ? { arg: schema.arg({ type: schema.String }) } : undefined, + where: { + arg: schema.arg({ type: filters[meta.provider].String.optional }), + resolve: filters.resolveString, + }, create: { arg: schema.arg({ type: schema.String }) }, update: { arg: schema.arg({ type: schema.String }) }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, @@ -47,7 +52,10 @@ export const text = output: schema.field({ type: schema.String }), views: resolveView('text/views'), getAdminMeta() { - return { displayMode: config.ui?.displayMode ?? 'input' }; + return { + displayMode: config.ui?.displayMode ?? 'input', + shouldUseModeInsensitive: meta.provider === 'postgresql', + }; }, __legacy: { filters: { diff --git a/packages/fields/src/types/text/views/index.tsx b/packages/fields/src/types/text/views/index.tsx index c9fd5cc0245..1405b86df73 100644 --- a/packages/fields/src/types/text/views/index.tsx +++ b/packages/fields/src/types/text/views/index.tsx @@ -50,7 +50,10 @@ export const CardValue: CardValueComponent = ({ item, field }) => { ); }; -type Config = FieldControllerConfig<{ displayMode: 'input' | 'textarea' }>; +type Config = FieldControllerConfig<{ + displayMode: 'input' | 'textarea'; + shouldUseModeInsensitive: boolean; +}>; export const controller = ( config: Config @@ -80,13 +83,25 @@ export const controller = ( }, graphql: ({ type, value }) => { - const key = type === 'is_i' ? `${config.path}_i` : `${config.path}_${type}`; - return { [key]: value }; + const isNot = type.startsWith('not_'); + const key = + type === 'is_i' || type === 'not_i' + ? 'equals' + : type + .replace(/_i$/, '') + .replace('not_', '') + .replace(/_([a-z])/g, (_, char: string) => char.toUpperCase()); + const filter = { [key]: value }; + return { + [config.path]: { + ...(isNot ? { not: filter } : filter), + mode: config.fieldMeta.shouldUseModeInsensitive ? 'insensitive' : undefined, + }, + }; }, Label({ label, value }) { return `${label.toLowerCase()}: "${value}"`; }, - // FIXME: Not all of these options will work with prisma_sqlite types: { contains_i: { label: 'Contains', diff --git a/packages/fields/src/types/timestamp/index.ts b/packages/fields/src/types/timestamp/index.ts index 81df675377d..b5203d908ab 100644 --- a/packages/fields/src/types/timestamp/index.ts +++ b/packages/fields/src/types/timestamp/index.ts @@ -7,6 +7,7 @@ import { orderDirectionEnum, legacyFilters, FieldDefaultValue, + filters, } from '@keystone-next/types'; import { resolveView } from '../../resolve-view'; import { getIndexType } from '../../get-index-type'; @@ -42,6 +43,10 @@ export const timestamp = })({ ...config, input: { + where: { + arg: schema.arg({ type: filters[meta.provider].DateTime.optional }), + resolve: filters.resolveCommon, + }, create: { arg: schema.arg({ type: schema.String }), resolve: inputResolver }, update: { arg: schema.arg({ type: schema.String }), resolve: inputResolver }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, diff --git a/packages/keystone/CHANGELOG.md b/packages/keystone/CHANGELOG.md index eb2919d6459..718e85be1cd 100644 --- a/packages/keystone/CHANGELOG.md +++ b/packages/keystone/CHANGELOG.md @@ -1,5 +1,170 @@ # @keystone-next/keystone +## 24.0.0 + +### Major Changes + +- [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `_ListKeyMeta` and `_toManyRelationshipFieldMeta` fields. You should use `listKeyCount` and `toManyRelationshipFieldCount` instead + +* [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed all arguments from `context.lists.List.count` and `context.db.lists.List.count` except for `where`. + +- [#6266](https://github.com/keystonejs/keystone/pull/6266) [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Renamed `first` argument in find many queries to `take` to align with Prisma. + + ```graphql + type Query { + users( + where: UserWhereInput! = {} + orderBy: [UserOrderByInput!]! = [] + # previously was first: Int + take: Int + skip: Int! = 0 + ): [User!] + # ... + } + + type User { + # ... + posts( + where: PostWhereInput! = {} + orderBy: [PostOrderByInput!]! = [] + # previously was first: Int + take: Int + skip: Int! = 0 + ): [Post!] + # ... + } + ``` + +* [#6208](https://github.com/keystonejs/keystone/pull/6208) [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The create one mutation now requires a non-null `data` argument and the create many mutation accepts a list of `ItemCreateInput` directly instead of being nested inside of an object with the `ItemCreateInput` in a `data` field. + + If you have a list called `Item`, `createItem` now looks like `createItem(data: ItemCreateInput!): Item` and `createItems` now looks like `createItems(data: [ItemCreateInput!]!): [Item]`. + +- [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `search` argument from the GraphQL API for finding many items, Lists/DB API and to-many relationship fields. You should use `contains` filters instead. + +* [#6095](https://github.com/keystonejs/keystone/pull/6095) [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Updated filters to be nested instead of flattened and add top-level `NOT` operator. See the [Query Filter API docs](https://keystonejs.com/docs/apis/filters) and the upgrade guide for more information. + + ```graphql + query { + posts(where: { title: { contains: "Something" } }) { + title + content + } + } + ``` + +- [#6198](https://github.com/keystonejs/keystone/pull/6198) [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b) Thanks [@timleslie](https://github.com/timleslie)! - Removed the `uid` and `name` properties from the errors returned by the GraphQL API. + +* [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `sortBy` argument from the GraphQL API for finding many items, Lists/DB API and to-many relationship fields. You should use `orderBy` instead. + +- [#6312](https://github.com/keystonejs/keystone/pull/6312) [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Updated `@graphql-ts/schema`. The second type parameter of `schema.Arg` exported from `@keystone-next/types` is now a boolean that defines whether or not the arg has a default value to make it easier to define circular input objects. + +* [#6217](https://github.com/keystonejs/keystone/pull/6217) [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - `disconnectAll` has been renamed to `disconnect` in to-one relationship inputs and the old `disconnect` field has been removed. There are also seperate input types for create and update where the input for create doesn't have `disconnect`. It's also now required that if you provide a to-one relationship input, you must provide exactly one field to the input. + + If you have a list called `Item`, the to-one relationship inputs now look like this: + + ```graphql + input ItemRelateToOneForCreateInput { + create: ItemCreateInput + connect: ItemWhereUniqueInput + } + input ItemRelateToOneForUpdateInput { + create: ItemCreateInput + connect: ItemWhereUniqueInput + disconnect: Boolean + } + ``` + +- [#6224](https://github.com/keystonejs/keystone/pull/6224) [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - `disconnectAll` has been replaced by `set` in to-many relationship inputs, the equivalent to `disconnectAll: true` is now `set: []`. There are also seperate input types for create and update where the input for create doesn't have `disconnect` or `set`. The inputs in the lists in the input field are now also non-null. + + If you have a list called `Item`, the to-many relationship inputs now look like this: + + ```graphql + input ItemRelateToManyForCreateInput { + create: [ItemCreateInput!] + connect: [ItemWhereUniqueInput!] + } + input ItemRelateToManyForUpdateInput { + disconnect: [ItemWhereUniqueInput!] + set: [ItemWhereUniqueInput!] + create: [ItemCreateInput!] + connect: [ItemWhereUniqueInput!] + } + ``` + +* [#6197](https://github.com/keystonejs/keystone/pull/6197) [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The generated CRUD queries, and some of the input types, in the GraphQL API have been renamed. + + If you have a list called `Item`, the query for multiple values, `allItems` will be renamed to `items`. The query for a single value, `Item`, will be renamed to `item`. + + Also, the input type used in the `updateItems` mutation has been renamed from `ItemsUpdateInput` to `ItemUpdateArgs`. + +- [#6211](https://github.com/keystonejs/keystone/pull/6211) [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The update mutations now accept `where` unique inputs instead of only an `id` and the `where` and `data` arguments are non-null. + + If you have a list called `Item`, the update mutations now look like this: + + ```graphql + type Mutation { + updateItem(where: ItemWhereUniqueInput!, data: ItemUpdateInput!): Item + updateItems(data: [ItemUpdateArgs!]!): [Item] + } + + input ItemUpdateArgs { + where: ItemWhereUniqueInput! + data: ItemUpdateInput! + } + ``` + +* [#6206](https://github.com/keystonejs/keystone/pull/6206) [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The delete mutations now accept `where` unique inputs instead of only an `id`. + + If you have a list called `Item`, `deleteItem` now looks like `deleteItem(where: ItemWhereUniqueInput!): Item` and `deleteItems` now looks like `deleteItems(where: [ItemWhereUniqueInput!]!): [Item]` + +### Minor Changes + +- [#6276](https://github.com/keystonejs/keystone/pull/6276) [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb) Thanks [@gautamsi](https://github.com/gautamsi)! - Added option for `Bearer` token auth when using session. + +* [#6267](https://github.com/keystonejs/keystone/pull/6267) [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590) Thanks [@timleslie](https://github.com/timleslie)! - Added `config.graphql.debug` option, which can be used to control whether debug information such as stack traces are included in the errors returned by the GraphQL API. + +### Patch Changes + +- [#6317](https://github.com/keystonejs/keystone/pull/6317) [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18) Thanks [@timleslie](https://github.com/timleslie)! - Separated the resolving of non-relationship field from relationship fields in create/update inputs to allow for better error handling. + +* [#6250](https://github.com/keystonejs/keystone/pull/6250) [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450) Thanks [@timleslie](https://github.com/timleslie)! - Updated internal type definitions. + +- [#6334](https://github.com/keystonejs/keystone/pull/6334) [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Resolved bug with visually hidden elements in ListView checkboxes expanding to fill the whole body on click of elements near the bottom of the screen. + +* [#6219](https://github.com/keystonejs/keystone/pull/6219) [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271) Thanks [@timleslie](https://github.com/timleslie)! - Removed unused code path in Admin UI error display. + +- [#6269](https://github.com/keystonejs/keystone/pull/6269) [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Added ignoreBuildErrors flag to next-config.js file, to negate false positive errors in keystone builds with imported components. + +* [#6218](https://github.com/keystonejs/keystone/pull/6218) [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2) Thanks [@timleslie](https://github.com/timleslie)! - Added more details to validation failure error messages. + +- [#6316](https://github.com/keystonejs/keystone/pull/6316) [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4) Thanks [@timleslie](https://github.com/timleslie)! - Updated handling of errors in `resolveInput` hooks to provide developers with appropriate debug information. + +* [#6310](https://github.com/keystonejs/keystone/pull/6310) [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053) Thanks [@timleslie](https://github.com/timleslie)! - Updated dependencies to use `mergeSchemas` from `@graphql-tools/schema`, rather than its old location in `@graphql-tools/merge`. You might see a reordering of the contents of your `graphql.schema` file. + +- [#6292](https://github.com/keystonejs/keystone/pull/6292) [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Prisma dependencies to `2.29.1`. + +* [#6263](https://github.com/keystonejs/keystone/pull/6263) [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524) Thanks [@timleslie](https://github.com/timleslie)! - Made the original stacktraces for before/after hooks available on `error.extension.errors`. + +- [#6259](https://github.com/keystonejs/keystone/pull/6259) [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Bumped @apollo/client dependency to ^3.4.5, the update resolves the following useQuery [issue](https://github.com/keystonejs/keystone/issues/6254). + +* [#6239](https://github.com/keystonejs/keystone/pull/6239) [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae) Thanks [@timleslie](https://github.com/timleslie)! - Added more details to before/after change/delete hook error messages. + +- [#6248](https://github.com/keystonejs/keystone/pull/6248) [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e) Thanks [@timleslie](https://github.com/timleslie)! - Removed unused dependency `@graphql-tools/schema`. + +* [#6203](https://github.com/keystonejs/keystone/pull/6203) [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Prisma dependencies to `2.28.0`. + +- [#6296](https://github.com/keystonejs/keystone/pull/6296) [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7) Thanks [@gwyneplaine](https://github.com/gwyneplaine)! - Fixed delete success notifications in the Admin UI appearing on failed deletes in List view and Item view. + +* [#6200](https://github.com/keystonejs/keystone/pull/6200) [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e) Thanks [@timleslie](https://github.com/timleslie)! - Updated internal error handling to use the `apollo-server-errors` package instead of `apollo-errors`. + +* Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`c2bb6a9a5`](https://github.com/keystonejs/keystone/commit/c2bb6a9a596fc52a3c61ec5d91c79758e417e61d), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/types@24.0.0 + - @keystone-ui/notice@4.0.1 + - @keystone-ui/toast@4.0.2 + - @keystone-next/admin-ui-utils@5.0.6 + - @keystone-next/utils@1.0.4 + ## 23.0.3 ### Patch Changes diff --git a/packages/keystone/package.json b/packages/keystone/package.json index 5c70c4c0abc..d2be3d4829e 100644 --- a/packages/keystone/package.json +++ b/packages/keystone/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/keystone", - "version": "23.0.3", + "version": "24.0.0", "license": "MIT", "main": "dist/keystone.cjs.js", "module": "dist/keystone.esm.js", @@ -22,33 +22,33 @@ "keystone-next": "bin/cli.js" }, "dependencies": { - "@apollo/client": "3.3.21", - "@babel/core": "^7.14.8", - "@babel/plugin-transform-modules-commonjs": "^7.14.5", - "@babel/runtime": "^7.14.8", + "@apollo/client": "^3.4.8", + "@babel/core": "^7.15.0", + "@babel/plugin-transform-modules-commonjs": "^7.15.0", + "@babel/runtime": "^7.15.3", "@emotion/hash": "^0.8.0", - "@graphql-tools/merge": "^6.2.16", + "@graphql-tools/schema": "^8.1.1", "@hapi/iron": "^6.0.0", - "@keystone-next/admin-ui-utils": "^5.0.5", - "@keystone-next/fields": "^13.0.0", - "@keystone-next/types": "^23.0.0", - "@keystone-next/utils": "^1.0.3", + "@keystone-next/admin-ui-utils": "^5.0.6", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/types": "^24.0.0", + "@keystone-next/utils": "^1.0.4", "@keystone-ui/button": "^5.0.0", "@keystone-ui/core": "^3.1.1", "@keystone-ui/fields": "^4.1.2", "@keystone-ui/icons": "^4.0.0", "@keystone-ui/loading": "^4.0.0", "@keystone-ui/modals": "^4.0.0", - "@keystone-ui/notice": "^4.0.0", + "@keystone-ui/notice": "^4.0.1", "@keystone-ui/options": "^4.0.1", "@keystone-ui/pill": "^5.0.0", "@keystone-ui/popover": "^4.0.2", - "@keystone-ui/toast": "^4.0.1", + "@keystone-ui/toast": "^4.0.2", "@keystone-ui/tooltip": "^4.0.1", "@preconstruct/next": "^3.0.0", - "@prisma/client": "2.28.0", - "@prisma/migrate": "2.28.0", - "@prisma/sdk": "2.28.0", + "@prisma/client": "2.29.1", + "@prisma/migrate": "2.29.1", + "@prisma/sdk": "2.29.1", "@sindresorhus/slugify": "^1.1.2", "@types/apollo-upload-client": "14.1.0", "@types/babel__core": "^7.1.15", @@ -94,7 +94,7 @@ "pirates": "^4.0.1", "pluralize": "^8.0.0", "prettier": "^2.3.2", - "prisma": "2.28.0", + "prisma": "2.29.1", "prompts": "^2.4.1", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx index 53d48f340f5..cba2c258087 100644 --- a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx +++ b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx @@ -53,13 +53,18 @@ export const controller = ( }, graphql: ({ type, value }) => { - const key = type === 'is' ? config.path : `${config.path}_${type}`; + if (type === 'not') { + return { [config.path]: { not: { equals: value } } }; + } const valueWithoutWhitespace = value.replace(/\s/g, ''); + const key = type === 'is' ? 'equals' : type === 'not_in' ? 'notIn' : type; return { - [key]: ['in', 'not_in'].includes(type) - ? valueWithoutWhitespace.split(',') - : valueWithoutWhitespace, + [config.path]: { + [key]: ['in', 'not_in'].includes(type) + ? valueWithoutWhitespace.split(',') + : valueWithoutWhitespace, + }, }; }, Label({ label, value, type }) { diff --git a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/next-config.ts b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/next-config.ts index e7c0984a39f..db51999f8e2 100644 --- a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/next-config.ts +++ b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/next-config.ts @@ -3,6 +3,9 @@ import Path from 'path'; import withPreconstruct from '@preconstruct/next'; export const config = withPreconstruct({ + typescript: { + ignoreBuildErrors: true, + }, webpack(config: any, { isServer }: any) { config.resolve.alias = { ...config.resolve.alias, diff --git a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx index 5ea383f38be..a49288a34c7 100644 --- a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx +++ b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx @@ -253,17 +253,19 @@ function DeleteButton({ confirm: { label: 'Delete', action: async () => { - await deleteItem().catch(err => { - toasts.addToast({ - title: 'Failed to delete item', + try { + await deleteItem(); + } catch (err) { + return toasts.addToast({ + title: `Failed to delete ${list.singular} item: ${itemLabel}`, message: err.message, tone: 'negative', }); - }); + } router.push(`/${list.path}`); - toasts.addToast({ + return toasts.addToast({ title: itemLabel, - message: 'Deleted successfully', + message: `Deleted ${list.singular} item successfully`, tone: 'positive', }); }, diff --git a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.tsx b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.tsx index e3b977c8737..a3f1e6203ca 100644 --- a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.tsx +++ b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.tsx @@ -176,12 +176,12 @@ const ListPage = ({ listKey }: ListPageProps) => { }) .join('\n'); return gql` - query ($where: ${list.gqlNames.whereInputName}, $first: Int!, $skip: Int!, $orderBy: [${ + query ($where: ${list.gqlNames.whereInputName}, $take: Int!, $skip: Int!, $orderBy: [${ list.gqlNames.listOrderName }!]) { items: ${ list.gqlNames.listQueryName - }(where: $where,first: $first, skip: $skip, orderBy: $orderBy) { + }(where: $where,take: $take, skip: $skip, orderBy: $orderBy) { ${ // TODO: maybe namespace all the fields instead of doing this selectedFields.has('id') ? '' : 'id' @@ -197,7 +197,7 @@ const ListPage = ({ listKey }: ListPageProps) => { errorPolicy: 'all', variables: { where: filters.where, - first: pageSize, + take: pageSize, skip: (currentPage - 1) * pageSize, orderBy: sort ? [{ [sort.field]: sort.direction.toLowerCase() }] : undefined, }, @@ -244,7 +244,6 @@ const ListPage = ({ listKey }: ListPageProps) => { const theme = useTheme(); const showCreate = !(metaQuery.data?.keystone.adminMeta.list?.hideCreate ?? true) || null; - return ( }> {metaQuery.error ? ( @@ -436,14 +435,18 @@ function DeleteManyButton({ useMemo( () => gql` - mutation($where: [${list.gqlNames.whereUniqueInputName}!]!) { - ${list.gqlNames.deleteManyMutationName}(where: $where) { - id - } - } + mutation($where: [${list.gqlNames.whereUniqueInputName}!]!) { + ${list.gqlNames.deleteManyMutationName}(where: $where) { + id + ${list.labelField} + } + } `, [list] - ) + ), + { + errorPolicy: 'all', + } ); const [isOpen, setIsOpen] = useState(false); const toasts = useToasts(); @@ -467,20 +470,75 @@ function DeleteManyButton({ confirm: { label: 'Delete', action: async () => { - await deleteItems({ + const { data, errors } = await deleteItems({ variables: { where: [...selectedItems].map(id => ({ id })) }, - }).catch(err => { + }); + /* + Data returns an array where successful deletions are item objects + and unsuccessful deletions are null values. + Run a reduce to count success and failure as well as + to generate the success message to be passed to the success toast + */ + const { successfulItems, unsuccessfulItems, successMessage } = data[ + list.gqlNames.deleteManyMutationName + ].reduce( + ( + acc: { + successfulItems: number; + unsuccessfulItems: number; + successMessage: string; + }, + curr: any + ) => { + if (curr) { + acc.successfulItems++; + acc.successMessage = + acc.successMessage === '' + ? (acc.successMessage += curr.label) + : (acc.successMessage += `, ${curr.label}`); + } else { + acc.unsuccessfulItems++; + } + return acc; + }, + { successfulItems: 0, unsuccessfulItems: 0, successMessage: '' } as { + successfulItems: number; + unsuccessfulItems: number; + successMessage: string; + } + ); + + // If there are errors + if (errors?.length) { + // Find out how many items failed to delete. + // Reduce error messages down to unique instances, and append to the toast as a message. toasts.addToast({ - title: 'Failed to delete items', - message: err.message, tone: 'negative', + title: `Failed to delete ${unsuccessfulItems} of ${ + data[list.gqlNames.deleteManyMutationName].length + } ${list.plural}`, + message: errors + .reduce((acc, error) => { + if (acc.indexOf(error.message) < 0) { + acc.push(error.message); + } + return acc; + }, [] as string[]) + .join('\n'), }); - }); - toasts.addToast({ - title: 'Deleted items successfully', - tone: 'positive', - }); - refetch(); + } + + if (successfulItems) { + toasts.addToast({ + tone: 'positive', + title: `Deleted ${successfulItems} of ${ + data[list.gqlNames.deleteManyMutationName].length + } ${list.plural} successfully`, + message: successMessage, + }); + } + + return refetch(); }, }, cancel: { @@ -523,7 +581,6 @@ function ListTable({ const { query } = useRouter(); const shouldShowLinkIcon = !list.fields[selectedFields.keys().next().value].views.Cell.supportsLinkTo; - return ( @@ -614,7 +671,7 @@ function ListTable({ minHeight: 38, alignItems: 'center', justifyContent: 'start', - // cursor: 'pointer', + position: 'relative', }} > ) => { padding: spacing.small, textAlign: 'left', position: 'sticky', + zIndex: 1, top: 0, }} {...props} diff --git a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/next-graphql.ts b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/next-graphql.ts index 0a032bfe62b..78ec432b4c4 100644 --- a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/next-graphql.ts +++ b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/next-graphql.ts @@ -15,7 +15,7 @@ export function nextGraphQLAPIRoute(keystoneConfig: KeystoneConfig, prismaClient graphQLSchema, createContext: keystone.createContext, sessionStrategy: initializedKeystoneConfig.session, - apolloConfig: initializedKeystoneConfig.graphql?.apolloConfig, + graphqlConfig: initializedKeystoneConfig.graphql, connectionPromise: keystone.connect(), }); diff --git a/packages/keystone/src/admin-ui/system/getAdminMetaSchema.ts b/packages/keystone/src/admin-ui/system/getAdminMetaSchema.ts index fcc65b63eda..29bf7753c32 100644 --- a/packages/keystone/src/admin-ui/system/getAdminMetaSchema.ts +++ b/packages/keystone/src/admin-ui/system/getAdminMetaSchema.ts @@ -1,4 +1,4 @@ -import { JSONValue, schema as schemaAPIFromTypesPkg } from '@keystone-next/types'; +import { JSONValue, QueryMode, schema as schemaAPIFromTypesPkg } from '@keystone-next/types'; import { KeystoneContext, KeystoneConfig, @@ -6,7 +6,7 @@ import { ListMetaRootVal, FieldMetaRootVal, } from '@keystone-next/types'; -import { GraphQLSchema, GraphQLObjectType, assertScalarType } from 'graphql'; +import { GraphQLSchema, GraphQLObjectType, assertScalarType, assertEnumType } from 'graphql'; import { InitialisedList } from '../../lib/core/types-for-lists'; const schema = { @@ -35,6 +35,10 @@ export function getAdminMetaSchema({ const jsonScalar = jsonScalarType ? schema.scalar(assertScalarType(jsonScalarType)) : schemaAPIFromTypesPkg.JSON; + const queryModeEnumType = graphQLSchema.getType('QueryMode'); + const queryModeEnum = queryModeEnumType + ? { ...QueryMode, graphQLType: assertEnumType(graphQLSchema.getType('QueryMode')) } + : QueryMode; const KeystoneAdminUIFieldMeta = schema.object()({ name: 'KeystoneAdminUIFieldMeta', @@ -153,10 +157,7 @@ export function getAdminMetaSchema({ }), }), search: schema.field({ - type: schema.enum({ - name: 'QueryMode', - values: schema.enumValues(['default', 'insensitive']), - }), + type: queryModeEnum, }), }, }); diff --git a/packages/keystone/src/admin-ui/templates/api.ts b/packages/keystone/src/admin-ui/templates/api.ts index 01d75117313..c779651e582 100644 --- a/packages/keystone/src/admin-ui/templates/api.ts +++ b/packages/keystone/src/admin-ui/templates/api.ts @@ -9,7 +9,7 @@ const apolloServer = createApolloServerMicro({ graphQLSchema, createContext, sessionStrategy: initializedKeystoneConfig.session ? initializedKeystoneConfig.session() : undefined, - apolloConfig: initializedKeystoneConfig.graphql?.apolloConfig, + graphqlConfig: initializedKeystoneConfig.graphql, connectionPromise: keystone.connect(), }); diff --git a/packages/keystone/src/lib/core/graphql-errors.ts b/packages/keystone/src/lib/core/graphql-errors.ts index cb760fd554d..6587864b984 100644 --- a/packages/keystone/src/lib/core/graphql-errors.ts +++ b/packages/keystone/src/lib/core/graphql-errors.ts @@ -7,9 +7,14 @@ export const validationFailureError = (messages: string[]) => { return new ApolloError(`You provided invalid data for this operation.\n${s}`); }; -export const extensionError = (extension: string, messages: string[]) => { - const s = messages.map(m => ` - ${m}`).join('\n'); - return new ApolloError(`An error occured while running "${extension}".\n${s}`); +export const extensionError = (extension: string, things: { error: Error; tag: string }[]) => { + const s = things.map(t => ` - ${t.tag}: ${t.error.message}`).join('\n'); + return new ApolloError( + `An error occured while running "${extension}".\n${s}`, + 'INTERNAL_SERVER_ERROR', + // Make the original stack traces available. + { debug: things.map(t => ({ stacktrace: t.error.stack, message: t.error.message })) } + ); }; // FIXME: In an upcoming PR we will use these args to construct a better diff --git a/packages/keystone/src/lib/core/mutations/access-control.ts b/packages/keystone/src/lib/core/mutations/access-control.ts index 51efe776857..0756a934f12 100644 --- a/packages/keystone/src/lib/core/mutations/access-control.ts +++ b/packages/keystone/src/lib/core/mutations/access-control.ts @@ -35,7 +35,7 @@ export async function getAccessControlledItemForDelete( // List access: pass 2 let where: PrismaFilter = mapUniqueWhereToWhere(list, uniqueWhere); if (typeof access === 'object') { - where = { AND: [where, await resolveWhereInput(access, list)] }; + where = { AND: [where, await resolveWhereInput(access, list, context)] }; } const item = await runWithPrisma(context, list, model => model.findFirst({ where })); if (item === null) { @@ -78,7 +78,9 @@ export async function getAccessControlledItemForUpdate( where: accessControl === true ? uniqueWhereInWhereForm - : { AND: [uniqueWhereInWhereForm, await resolveWhereInput(accessControl, list)] }, + : { + AND: [uniqueWhereInWhereForm, await resolveWhereInput(accessControl, list, context)], + }, }) ); if (!item) { diff --git a/packages/keystone/src/lib/core/mutations/create-update.ts b/packages/keystone/src/lib/core/mutations/create-update.ts index eda831a0393..289d34a9821 100644 --- a/packages/keystone/src/lib/core/mutations/create-update.ts +++ b/packages/keystone/src/lib/core/mutations/create-update.ts @@ -9,6 +9,7 @@ import { runWithPrisma, } from '../utils'; import { resolveUniqueWhereInput, UniqueInputFilter } from '../where-inputs'; +import { extensionError } from '../graphql-errors'; import { resolveRelateToManyForCreateInput, resolveRelateToManyForUpdateInput, @@ -179,21 +180,32 @@ async function getResolvedData( ); } - // Apply field type input resolvers + // Apply non-relationship field type input resolvers resolvedData = Object.fromEntries( await promiseAllRejectWithAllErrors( Object.entries(list.fields).map(async ([fieldKey, field]) => { const inputResolver = field.input?.[operation]?.resolve; let input = resolvedData[fieldKey]; - if (inputResolver) { + if (inputResolver && field.dbField.kind !== 'relation') { + input = await inputResolver(input, context, undefined); + } + return [fieldKey, input] as const; + }) + ) + ); + + // Apply relationship field type input resolvers + resolvedData = Object.fromEntries( + await promiseAllRejectWithAllErrors( + Object.entries(list.fields).map(async ([fieldKey, field]) => { + const inputResolver = field.input?.[operation]?.resolve; + let input = resolvedData[fieldKey]; + if (inputResolver && field.dbField.kind === 'relation') { input = await inputResolver( input, context, + // This third argument only applies to relationship fields (() => { - // This third argument only applies to relationship fields - if (field.dbField.kind !== 'relation') { - return undefined; - } if (input === undefined) { // No-op: This is what we want return () => undefined; @@ -228,23 +240,37 @@ async function getResolvedData( ); // Resolve input hooks - resolvedData = Object.fromEntries( - await promiseAllRejectWithAllErrors( - Object.entries(list.fields).map(async ([fieldKey, field]) => { - if (field.hooks.resolveInput === undefined) { - return [fieldKey, resolvedData[fieldKey]]; - } - const value = await field.hooks.resolveInput({ + const hookName = 'resolveInput'; + // Field hooks + let _resolvedData: Record = {}; + const fieldsErrors: { error: Error; tag: string }[] = []; + for (const [fieldPath, field] of Object.entries(list.fields)) { + if (field.hooks.resolveInput === undefined) { + _resolvedData[fieldPath] = resolvedData[fieldPath]; + } else { + try { + _resolvedData[fieldPath] = await field.hooks.resolveInput({ ...hookArgs, resolvedData, - fieldPath: fieldKey, + fieldPath, }); - return [fieldKey, value]; - }) - ) - ); + } catch (error) { + fieldsErrors.push({ error, tag: `${list.listKey}.${fieldPath}` }); + } + } + } + if (fieldsErrors.length) { + throw extensionError(hookName, fieldsErrors); + } + resolvedData = _resolvedData; + + // List hooks if (list.hooks.resolveInput) { - resolvedData = (await list.hooks.resolveInput({ ...hookArgs, resolvedData })) as any; + try { + resolvedData = (await list.hooks.resolveInput({ ...hookArgs, resolvedData })) as any; + } catch (error) { + throw extensionError(hookName, [{ error, tag: list.listKey }]); + } } return resolvedData; diff --git a/packages/keystone/src/lib/core/mutations/hooks.ts b/packages/keystone/src/lib/core/mutations/hooks.ts index 38cff0a8d7c..eeac512823f 100644 --- a/packages/keystone/src/lib/core/mutations/hooks.ts +++ b/packages/keystone/src/lib/core/mutations/hooks.ts @@ -31,14 +31,13 @@ export async function runSideEffectOnlyHook< } // Field hooks - const fieldsErrors = []; + const fieldsErrors: { error: Error; tag: string }[] = []; for (const [fieldPath, field] of Object.entries(list.fields)) { if (shouldRunFieldLevelHook(fieldPath)) { try { - // @ts-ignore await field.hooks[hookName]?.({ fieldPath, ...args }); - } catch (err) { - fieldsErrors.push(`${list.listKey}.${fieldPath}: ${err.message}`); + } catch (error) { + fieldsErrors.push({ error, tag: `${list.listKey}.${fieldPath}` }); } } } @@ -49,7 +48,7 @@ export async function runSideEffectOnlyHook< // List hooks try { await list.hooks[hookName]?.(args); - } catch (err) { - throw extensionError(hookName, [`${list.listKey}: ${err.message}`]); + } catch (error) { + throw extensionError(hookName, [{ error, tag: list.listKey }]); } } diff --git a/packages/keystone/src/lib/core/queries/resolvers.ts b/packages/keystone/src/lib/core/queries/resolvers.ts index 35e421b4e26..21a9225146c 100644 --- a/packages/keystone/src/lib/core/queries/resolvers.ts +++ b/packages/keystone/src/lib/core/queries/resolvers.ts @@ -59,7 +59,7 @@ export async function accessControlledFilter( // Merge declarative access control if (typeof access === 'object') { - resolvedWhere = { AND: [resolvedWhere, await resolveWhereInput(access, list)] }; + resolvedWhere = { AND: [resolvedWhere, await resolveWhereInput(access, list, context)] }; } return resolvedWhere; @@ -86,7 +86,7 @@ export async function findOne( } export async function findMany( - { where, first, skip, orderBy: rawOrderBy }: FindManyArgsValue, + { where, take, skip, orderBy: rawOrderBy }: FindManyArgsValue, list: InitialisedList, context: KeystoneContext, info: GraphQLResolveInfo, @@ -94,16 +94,16 @@ export async function findMany( ): Promise { const orderBy = await resolveOrderBy(rawOrderBy, list, context); - applyEarlyMaxResults(first, list); + applyEarlyMaxResults(take, list); - let resolvedWhere = await resolveWhereInput(where || {}, list); + let resolvedWhere = await resolveWhereInput(where, list, context); resolvedWhere = await accessControlledFilter(list, context, resolvedWhere); const results = await runWithPrisma(context, list, model => model.findMany({ where: extraFilter === undefined ? resolvedWhere : { AND: [resolvedWhere, extraFilter] }, orderBy, - take: first ?? undefined, + take: take ?? undefined, skip, }) ); @@ -166,7 +166,7 @@ export async function count( info: GraphQLResolveInfo, extraFilter?: PrismaFilter ) { - let resolvedWhere = await resolveWhereInput(where || {}, list); + let resolvedWhere = await resolveWhereInput(where, list, context); resolvedWhere = await accessControlledFilter(list, context, resolvedWhere); const count = await runWithPrisma(context, list, model => @@ -186,16 +186,16 @@ export async function count( return count; } -function applyEarlyMaxResults(_first: number | null | undefined, list: InitialisedList) { - const first = _first ?? Infinity; +function applyEarlyMaxResults(_take: number | null | undefined, list: InitialisedList) { + const take = _take ?? Infinity; // We want to help devs by failing fast and noisily if limits are violated. // Unfortunately, we can't always be sure of intent. - // E.g., if the query has a "first: 10", is it bad if more results could come back? + // E.g., if the query has a "take: 10", is it bad if more results could come back? // Maybe yes, or maybe the dev is just paginating posts. // But we can be sure there's a problem in two cases: - // * The query explicitly has a "first" that exceeds the limit - // * The query has no "first", and has more results than the limit - if (first < Infinity && first > list.maxResults) { + // * The query explicitly has a "take" that exceeds the limit + // * The query has no "take", and has more results than the limit + if (take < Infinity && take > list.maxResults) { throw limitsExceededError({ list: list.listKey, type: 'maxResults', limit: list.maxResults }); } } diff --git a/packages/keystone/src/lib/core/types-for-lists.ts b/packages/keystone/src/lib/core/types-for-lists.ts index 18ab3c6b78d..d4408407ebc 100644 --- a/packages/keystone/src/lib/core/types-for-lists.ts +++ b/packages/keystone/src/lib/core/types-for-lists.ts @@ -22,7 +22,6 @@ import { } from './access-control'; import { getNamesFromList } from './utils'; import { ResolvedDBField, resolveRelationships } from './resolve-relationships'; -import { InputFilter, PrismaFilter, resolveWhereInput } from './where-inputs'; import { outputTypeField } from './queries/output-field'; import { assertFieldsValid } from './field-assertions'; @@ -37,7 +36,6 @@ export type InitialisedList = { /** This will include the opposites to one-sided relationships */ resolvedDbFields: Record; pluralGraphQLName: string; - filterImpls: Record PrismaFilter>; types: TypesForList; access: ResolvedListAccessControl; hooks: ListHooks; @@ -111,9 +109,12 @@ export function initialiseLists( { AND: schema.arg({ type: schema.list(schema.nonNull(where)) }), OR: schema.arg({ type: schema.list(schema.nonNull(where)) }), + NOT: schema.arg({ type: schema.list(schema.nonNull(where)) }), }, - ...Object.values(fields).map(field => - field.access.read === false ? {} : field.__legacy?.filters?.fields ?? {} + ...Object.entries(fields).map( + ([fieldKey, field]) => + field.input?.where?.arg && + field.access.read !== false && { [fieldKey]: field.input?.where?.arg } ) ); }, @@ -165,7 +166,7 @@ export function initialiseLists( defaultValue: [], }), // TODO: non-nullable when max results is specified in the list with the default of max results - first: schema.arg({ type: schema.Int }), + take: schema.arg({ type: schema.Int }), skip: schema.arg({ type: schema.nonNull(schema.Int), defaultValue: 0 }), }; @@ -234,7 +235,18 @@ export function initialiseLists( update, findManyArgs, relateTo: { - many: { create: relateToManyForCreate, update: relateToManyForUpdate }, + many: { + where: schema.inputObject({ + name: `${listKey}ManyRelationFilter`, + fields: { + every: schema.arg({ type: where }), + some: schema.arg({ type: where }), + none: schema.arg({ type: where }), + }, + }), + create: relateToManyForCreate, + update: relateToManyForUpdate, + }, one: { create: relateToOneForCreate, update: relateToOneForUpdate }, }, }, @@ -304,27 +316,6 @@ export function initialiseLists( ...listInfos[listKey], ...listsWithResolvedDBFields[listKey], hooks: list.hooks || {}, - filterImpls: Object.assign( - {}, - ...Object.values(list.fields).map(field => { - if (field.dbField.kind === 'relation' && field.__legacy?.filters) { - const foreignListKey = field.dbField.list; - return Object.fromEntries( - Object.entries(field.__legacy.filters.impls).map(([key, resolve]) => { - return [ - key, - (val: any) => - resolve(val, foreignListWhereInput => - resolveWhereInput(foreignListWhereInput, lists[foreignListKey]) - ), - ]; - }) - ); - } else { - return field.__legacy?.filters?.impls ?? {}; - } - }) - ), cacheHint: (() => { const cacheHint = listsConfig[listKey].graphql?.cacheHint; if (cacheHint === undefined) { diff --git a/packages/keystone/src/lib/core/where-inputs.ts b/packages/keystone/src/lib/core/where-inputs.ts index 3228f89e657..e6bfab810c6 100644 --- a/packages/keystone/src/lib/core/where-inputs.ts +++ b/packages/keystone/src/lib/core/where-inputs.ts @@ -1,5 +1,6 @@ -import { KeystoneContext } from '@keystone-next/types'; +import { DBField, KeystoneContext } from '@keystone-next/types'; import { InitialisedList } from './types-for-lists'; +import { getDBFieldKeyForFieldOnMultiField } from './utils'; export type InputFilter = Record & { _____?: 'input filter'; @@ -47,20 +48,91 @@ export async function resolveUniqueWhereInput( export async function resolveWhereInput( inputFilter: InputFilter, - list: InitialisedList + list: InitialisedList, + context: KeystoneContext ): Promise { return { AND: await Promise.all( Object.entries(inputFilter).map(async ([fieldKey, value]) => { - if (fieldKey === 'OR' || fieldKey === 'AND') { + if (fieldKey === 'OR' || fieldKey === 'AND' || fieldKey === 'NOT') { return { [fieldKey]: await Promise.all( - value.map((value: any) => resolveWhereInput(value, list)) + value.map((value: any) => resolveWhereInput(value, list, context)) ), }; } - return list.filterImpls[fieldKey](value); + const field = list.fields[fieldKey]; + // we know if there are filters in the input object with the key of a field, the field must have defined a where input so this non null assertion is okay + const where = field.input!.where!; + const dbField = field.dbField; + const ret = where.resolve + ? await where.resolve( + value, + context, + (() => { + if (field.dbField.kind !== 'relation') { + return undefined as any; + } + const foreignList = field.dbField.list; + const whereResolver = (val: any) => + resolveWhereInput(val, list.lists[foreignList], context); + if (field.dbField.mode === 'many') { + return async () => { + if (value === null) { + throw new Error('A many relation filter cannot be set to null'); + } + return Object.fromEntries( + await Promise.all( + Object.entries(value).map(async ([key, val]) => { + if (val === null) { + throw new Error( + `The key ${key} in a many relation filter cannot be set to null` + ); + } + return [key, await whereResolver(val as any)]; + }) + ) + ); + }; + } + return (val: any) => { + if (val === null) { + return null; + } + return whereResolver(val); + }; + })() + ) + : value; + if (ret === null) { + if (field.dbField.kind === 'multi') { + throw new Error('multi db fields cannot return null from where input resolvers'); + } + return { [fieldKey]: null }; + } + return handleOperators(fieldKey, dbField, ret); }) ), }; } + +function handleOperators(fieldKey: string, dbField: DBField, { AND, OR, NOT, ...rest }: any) { + return { + AND: AND?.map((value: any) => handleOperators(fieldKey, dbField, value)), + OR: OR?.map((value: any) => handleOperators(fieldKey, dbField, value)), + NOT: NOT?.map((value: any) => handleOperators(fieldKey, dbField, value)), + ...nestWithAppropiateField(fieldKey, dbField, rest), + }; +} + +function nestWithAppropiateField(fieldKey: string, dbField: DBField, value: any) { + if (dbField.kind === 'multi') { + return Object.fromEntries( + Object.entries(value).map(([key, val]) => [ + getDBFieldKeyForFieldOnMultiField(fieldKey, key), + val, + ]) + ); + } + return { [fieldKey]: value }; +} diff --git a/packages/keystone/src/lib/id-field.ts b/packages/keystone/src/lib/id-field.ts index 39065785c9d..5f3fd34136f 100644 --- a/packages/keystone/src/lib/id-field.ts +++ b/packages/keystone/src/lib/id-field.ts @@ -43,6 +43,63 @@ const idParsers = { }, }; +const nonCircularFields = { + equals: schema.arg({ type: schema.ID }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.ID)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.ID)) }), + lt: schema.arg({ type: schema.ID }), + lte: schema.arg({ type: schema.ID }), + gt: schema.arg({ type: schema.ID }), + gte: schema.arg({ type: schema.ID }), +}; + +type IDFilterType = schema.InputObjectType< + typeof nonCircularFields & { + not: schema.Arg; + } +>; + +const IDFilter: IDFilterType = schema.inputObject({ + name: 'IDFilter', + fields: () => ({ + ...nonCircularFields, + not: schema.arg({ type: IDFilter }), + }), +}); + +const filterArg = schema.arg({ type: IDFilter }); + +function resolveVal( + input: Exclude, undefined>, + kind: IdFieldConfig['kind'] +): any { + if (input === null) { + throw new Error('id filter cannot be null'); + } + const idParser = idParsers[kind]; + const obj: any = {}; + for (const key of ['equals', 'gt', 'gte', 'lt', 'lte'] as const) { + const val = input[key]; + if (val !== undefined) { + const parsed = idParser(val); + obj[key] = parsed; + } + } + for (const key of ['in', 'notIn'] as const) { + const val = input[key]; + if (val !== undefined) { + if (val === null) { + throw new Error(`${key} id filter cannot be null`); + } + obj[key] = val.map(x => idParser(x)); + } + } + if (input.not !== undefined) { + obj.not = resolveVal(input.not, kind); + } + return obj; +} + export const idFieldType = (config: IdFieldConfig): FieldTypeFunc => meta => { @@ -55,6 +112,12 @@ export const idFieldType = default: { kind: config.kind }, })({ input: { + where: { + arg: filterArg, + resolve(val) { + return resolveVal(val, config.kind); + }, + }, uniqueWhere: { arg: schema.arg({ type: schema.ID }), resolve: parseVal }, orderBy: { arg: schema.arg({ type: orderDirectionEnum }) }, }, diff --git a/packages/keystone/src/lib/server/createApolloServer.ts b/packages/keystone/src/lib/server/createApolloServer.ts index 70cce699e4d..af4d51b3985 100644 --- a/packages/keystone/src/lib/server/createApolloServer.ts +++ b/packages/keystone/src/lib/server/createApolloServer.ts @@ -1,22 +1,22 @@ import type { IncomingMessage, ServerResponse } from 'http'; -import { GraphQLSchema } from 'graphql'; +import { GraphQLError, GraphQLSchema } from 'graphql'; import { ApolloServer as ApolloServerMicro } from 'apollo-server-micro'; import { ApolloServer as ApolloServerExpress } from 'apollo-server-express'; import type { Config } from 'apollo-server-express'; -import type { CreateContext, SessionStrategy } from '@keystone-next/types'; +import type { CreateContext, GraphQLConfig, SessionStrategy } from '@keystone-next/types'; import { createSessionContext } from '../../session'; export const createApolloServerMicro = ({ graphQLSchema, createContext, sessionStrategy, - apolloConfig, + graphqlConfig, connectionPromise, }: { graphQLSchema: GraphQLSchema; createContext: CreateContext; sessionStrategy?: SessionStrategy; - apolloConfig?: Config; + graphqlConfig?: GraphQLConfig; connectionPromise: Promise; }) => { const context = async ({ req, res }: { req: IncomingMessage; res: ServerResponse }) => { @@ -28,7 +28,7 @@ export const createApolloServerMicro = ({ req, }); }; - const serverConfig = _createApolloServerConfig({ graphQLSchema, apolloConfig }); + const serverConfig = _createApolloServerConfig({ graphQLSchema, graphqlConfig }); return new ApolloServerMicro({ ...serverConfig, context }); }; @@ -36,12 +36,12 @@ export const createApolloServerExpress = ({ graphQLSchema, createContext, sessionStrategy, - apolloConfig, + graphqlConfig, }: { graphQLSchema: GraphQLSchema; createContext: CreateContext; sessionStrategy?: SessionStrategy; - apolloConfig?: Config; + graphqlConfig?: GraphQLConfig; }) => { const context = async ({ req, res }: { req: IncomingMessage; res: ServerResponse }) => createContext({ @@ -50,18 +50,19 @@ export const createApolloServerExpress = ({ : undefined, req, }); - const serverConfig = _createApolloServerConfig({ graphQLSchema, apolloConfig }); + const serverConfig = _createApolloServerConfig({ graphQLSchema, graphqlConfig }); return new ApolloServerExpress({ ...serverConfig, context }); }; const _createApolloServerConfig = ({ graphQLSchema, - apolloConfig, + graphqlConfig, }: { graphQLSchema: GraphQLSchema; - apolloConfig?: Config; + graphqlConfig?: GraphQLConfig; }) => { // Playground config, is /api/graphql available? + const apolloConfig = graphqlConfig?.apolloConfig; const pp = apolloConfig?.playground; let playground: Config['playground']; const settings = { 'request.credentials': 'same-origin' }; @@ -86,6 +87,7 @@ const _createApolloServerConfig = ({ return { uploads: false, schema: graphQLSchema, + debug: graphqlConfig?.debug, // If undefined, use Apollo default of NODE_ENV !== 'production' // FIXME: support for apollo studio tracing // ...(process.env.ENGINE_API_KEY || process.env.APOLLO_KEY // ? { tracing: true } @@ -97,6 +99,7 @@ const _createApolloServerConfig = ({ // tracing: dev, // }), ...apolloConfig, + formatError: formatError(graphqlConfig), // Carefully inject the playground playground, // FIXME: Support for file handling configuration @@ -104,3 +107,24 @@ const _createApolloServerConfig = ({ // maxFiles: 5, }; }; + +const formatError = (graphqlConfig: GraphQLConfig | undefined) => { + return (err: GraphQLError) => { + let debug = graphqlConfig?.debug; + if (debug === undefined) { + debug = process.env.NODE_ENV !== 'production'; + } + + if (!debug && err.extensions) { + // Strip out any `debug` extensions + delete err.extensions.debug; + delete err.extensions.exception; + } + + if (graphqlConfig?.apolloConfig?.formatError) { + return graphqlConfig.apolloConfig.formatError(err); + } else { + return err; + } + }; +}; diff --git a/packages/keystone/src/lib/server/createExpressServer.ts b/packages/keystone/src/lib/server/createExpressServer.ts index f4342538093..e94fe7c1297 100644 --- a/packages/keystone/src/lib/server/createExpressServer.ts +++ b/packages/keystone/src/lib/server/createExpressServer.ts @@ -1,9 +1,13 @@ -import type { Config } from 'apollo-server-express'; import cors, { CorsOptions } from 'cors'; import express from 'express'; import { GraphQLSchema } from 'graphql'; import { graphqlUploadExpress } from 'graphql-upload'; -import type { KeystoneConfig, CreateContext, SessionStrategy } from '@keystone-next/types'; +import type { + KeystoneConfig, + CreateContext, + SessionStrategy, + GraphQLConfig, +} from '@keystone-next/types'; import { createAdminUIServer } from '../../admin-ui/system'; import { createApolloServerExpress } from './createApolloServer'; import { addHealthCheck } from './addHealthCheck'; @@ -16,20 +20,20 @@ const addApolloServer = ({ graphQLSchema, createContext, sessionStrategy, - apolloConfig, + graphqlConfig, }: { server: express.Express; config: KeystoneConfig; graphQLSchema: GraphQLSchema; createContext: CreateContext; sessionStrategy?: SessionStrategy; - apolloConfig?: Config; + graphqlConfig?: GraphQLConfig; }) => { const apolloServer = createApolloServerExpress({ graphQLSchema, createContext, sessionStrategy, - apolloConfig, + graphqlConfig, }); const maxFileSize = config.server?.maxFileSize || DEFAULT_MAX_FILE_SIZE; @@ -68,7 +72,7 @@ export const createExpressServer = async ( graphQLSchema, createContext, sessionStrategy: config.session, - apolloConfig: config.graphql?.apolloConfig, + graphqlConfig: config.graphql, }); if (config.ui?.isDisabled) { diff --git a/packages/keystone/src/schema/schema.ts b/packages/keystone/src/schema/schema.ts index c3694e83b19..1c84166a776 100644 --- a/packages/keystone/src/schema/schema.ts +++ b/packages/keystone/src/schema/schema.ts @@ -1,5 +1,5 @@ import type { GraphQLSchema } from 'graphql'; -import { mergeSchemas } from '@graphql-tools/merge'; +import { mergeSchemas } from '@graphql-tools/schema'; import type { BaseFields, diff --git a/packages/keystone/src/scripts/tests/__snapshots__/artifacts.test.ts.snap b/packages/keystone/src/scripts/tests/__snapshots__/artifacts.test.ts.snap index d82fd130fba..40457901eb3 100644 --- a/packages/keystone/src/scripts/tests/__snapshots__/artifacts.test.ts.snap +++ b/packages/keystone/src/scripts/tests/__snapshots__/artifacts.test.ts.snap @@ -19,28 +19,48 @@ type Scalars = { export type TodoWhereInput = { readonly AND?: ReadonlyArray | TodoWhereInput | null; readonly OR?: ReadonlyArray | TodoWhereInput | null; - readonly id?: Scalars['ID'] | null; - readonly id_not?: Scalars['ID'] | null; - readonly id_lt?: Scalars['ID'] | null; - readonly id_lte?: Scalars['ID'] | null; - readonly id_gt?: Scalars['ID'] | null; - readonly id_gte?: Scalars['ID'] | null; - readonly id_in?: ReadonlyArray | Scalars['ID'] | null; - readonly id_not_in?: ReadonlyArray | Scalars['ID'] | null; - readonly title?: Scalars['String'] | null; - readonly title_not?: Scalars['String'] | null; - readonly title_contains?: Scalars['String'] | null; - readonly title_not_contains?: Scalars['String'] | null; - readonly title_in?: - | ReadonlyArray - | Scalars['String'] - | null - | null; - readonly title_not_in?: - | ReadonlyArray - | Scalars['String'] - | null - | null; + readonly NOT?: ReadonlyArray | TodoWhereInput | null; + readonly id?: IDFilter | null; + readonly title?: StringNullableFilter | null; +}; + +export type IDFilter = { + readonly equals?: Scalars['ID'] | null; + readonly in?: ReadonlyArray | Scalars['ID'] | null; + readonly notIn?: ReadonlyArray | Scalars['ID'] | null; + readonly lt?: Scalars['ID'] | null; + readonly lte?: Scalars['ID'] | null; + readonly gt?: Scalars['ID'] | null; + readonly gte?: Scalars['ID'] | null; + readonly not?: IDFilter | null; +}; + +export type StringNullableFilter = { + readonly equals?: Scalars['String'] | null; + readonly in?: ReadonlyArray | Scalars['String'] | null; + readonly notIn?: ReadonlyArray | Scalars['String'] | null; + readonly lt?: Scalars['String'] | null; + readonly lte?: Scalars['String'] | null; + readonly gt?: Scalars['String'] | null; + readonly gte?: Scalars['String'] | null; + readonly contains?: Scalars['String'] | null; + readonly startsWith?: Scalars['String'] | null; + readonly endsWith?: Scalars['String'] | null; + readonly not?: NestedStringNullableFilter | null; +}; + +export type NestedStringNullableFilter = { + readonly equals?: Scalars['String'] | null; + readonly in?: ReadonlyArray | Scalars['String'] | null; + readonly notIn?: ReadonlyArray | Scalars['String'] | null; + readonly lt?: Scalars['String'] | null; + readonly lte?: Scalars['String'] | null; + readonly gt?: Scalars['String'] | null; + readonly gte?: Scalars['String'] | null; + readonly contains?: Scalars['String'] | null; + readonly startsWith?: Scalars['String'] | null; + readonly endsWith?: Scalars['String'] | null; + readonly not?: NestedStringNullableFilter | null; }; export type TodoWhereUniqueInput = { @@ -94,7 +114,7 @@ export type TodoListTypeInfo = { listQuery: { readonly where?: TodoWhereInput; readonly orderBy?: ReadonlyArray | TodoOrderByInput; - readonly first?: Scalars['Int'] | null; + readonly take?: Scalars['Int'] | null; readonly skip?: Scalars['Int']; }; }; diff --git a/packages/keystone/src/scripts/tests/build.test.ts b/packages/keystone/src/scripts/tests/build.test.ts index 97908231a12..46c5b16a45e 100644 --- a/packages/keystone/src/scripts/tests/build.test.ts +++ b/packages/keystone/src/scripts/tests/build.test.ts @@ -77,7 +77,7 @@ test('build works with typescript without the user defining a babel config', asy ✨ Generating Keystone config code ✨ Building Admin UI info - Using webpack 4. Reason: custom webpack configuration in next.config.js https://nextjs.org/docs/messages/webpack5 - info - Checking validity of types... + info - Skipping validation of types... info - Creating an optimized production build... info - Compiled successfully info - Collecting page data... diff --git a/packages/keystone/src/scripts/tests/fixtures/basic-project/schema.graphql b/packages/keystone/src/scripts/tests/fixtures/basic-project/schema.graphql index 2f3cfe2ec31..b0e148762fd 100644 --- a/packages/keystone/src/scripts/tests/fixtures/basic-project/schema.graphql +++ b/packages/keystone/src/scripts/tests/fixtures/basic-project/schema.graphql @@ -6,20 +6,48 @@ type Todo { input TodoWhereInput { AND: [TodoWhereInput!] OR: [TodoWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - title: String - title_not: String - title_contains: String - title_not_contains: String - title_in: [String] - title_not_in: [String] + NOT: [TodoWhereInput!] + id: IDFilter + title: StringNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter } input TodoWhereUniqueInput { @@ -70,7 +98,7 @@ type Query { todos( where: TodoWhereInput! = {} orderBy: [TodoOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Todo!] todo(where: TodoWhereUniqueInput!): Todo diff --git a/packages/keystone/src/session/index.ts b/packages/keystone/src/session/index.ts index 3f16ecd60f2..9843ebe8c9b 100644 --- a/packages/keystone/src/session/index.ts +++ b/packages/keystone/src/session/index.ts @@ -87,11 +87,12 @@ export function statelessSessions({ } return { async get({ req }) { - if (!req.headers.cookie) return; - let cookies = cookie.parse(req.headers.cookie); - if (!cookies[TOKEN_NAME]) return; + const cookies = cookie.parse(req.headers.cookie || ''); + const bearer = req.headers.authorization?.replace('Bearer ', ''); + const token = bearer || cookies[TOKEN_NAME]; + if (!token) return; try { - return await Iron.unseal(cookies[TOKEN_NAME], secret, ironOptions); + return await Iron.unseal(token, secret, ironOptions); } catch (err) {} }, async end({ res }) { diff --git a/packages/session-store-redis/package.json b/packages/session-store-redis/package.json index 201aa83296d..32396318b80 100644 --- a/packages/session-store-redis/package.json +++ b/packages/session-store-redis/package.json @@ -8,7 +8,7 @@ "node": "^12.20 || >= 14.13" }, "dependencies": { - "@babel/runtime": "^7.14.8", + "@babel/runtime": "^7.15.3", "@types/redis": "^2.8.31" }, "devDependencies": { diff --git a/packages/testing/CHANGELOG.md b/packages/testing/CHANGELOG.md index 1f2b8df452e..1b73d7bd019 100644 --- a/packages/testing/CHANGELOG.md +++ b/packages/testing/CHANGELOG.md @@ -1,5 +1,12 @@ # @keystone-next/testing +## 1.1.1 + +### Patch Changes + +- Updated dependencies [[`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/keystone@24.0.0 + ## 1.1.0 ### Minor Changes diff --git a/packages/testing/package.json b/packages/testing/package.json index fc0d46f2c7f..4e1827333ea 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -1,7 +1,7 @@ { "name": "@keystone-next/testing", "description": "Tools to assist with testing Keystone projects", - "version": "1.1.0", + "version": "1.1.1", "author": "The KeystoneJS Development Team", "license": "MIT", "main": "dist/testing.cjs.js", @@ -10,11 +10,11 @@ "node": "^12.20 || >= 14.13" }, "dependencies": { - "@keystone-next/keystone": "^23.0.0", + "@keystone-next/keystone": "^24.0.0", "@types/supertest": "^2.0.11", "express": "^4.17.1", "memoize-one": "^5.2.1", - "supertest": "^6.1.4" + "supertest": "^6.1.5" }, "repository": "https://github.com/keystonejs/keystone/tree/master/packages/testing" } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index b192772357c..152cc92c772 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,131 @@ # @keystone-next/types +## 24.0.0 + +### Major Changes + +- [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `_ListKeyMeta` and `_toManyRelationshipFieldMeta` fields. You should use `listKeyCount` and `toManyRelationshipFieldCount` instead + +* [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed all arguments from `context.lists.List.count` and `context.db.lists.List.count` except for `where`. + +- [#6266](https://github.com/keystonejs/keystone/pull/6266) [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Renamed `first` argument in find many queries to `take` to align with Prisma. + + ```graphql + type Query { + users( + where: UserWhereInput! = {} + orderBy: [UserOrderByInput!]! = [] + # previously was first: Int + take: Int + skip: Int! = 0 + ): [User!] + # ... + } + + type User { + # ... + posts( + where: PostWhereInput! = {} + orderBy: [PostOrderByInput!]! = [] + # previously was first: Int + take: Int + skip: Int! = 0 + ): [Post!] + # ... + } + ``` + +* [#6208](https://github.com/keystonejs/keystone/pull/6208) [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The create one mutation now requires a non-null `data` argument and the create many mutation accepts a list of `ItemCreateInput` directly instead of being nested inside of an object with the `ItemCreateInput` in a `data` field. + + If you have a list called `Item`, `createItem` now looks like `createItem(data: ItemCreateInput!): Item` and `createItems` now looks like `createItems(data: [ItemCreateInput!]!): [Item]`. + +- [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `search` argument from the GraphQL API for finding many items, Lists/DB API and to-many relationship fields. You should use `contains` filters instead. + +* [#6095](https://github.com/keystonejs/keystone/pull/6095) [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Updated filters to be nested instead of flattened and add top-level `NOT` operator. See the [Query Filter API docs](https://keystonejs.com/docs/apis/filters) and the upgrade guide for more information. + + ```graphql + query { + posts(where: { title: { contains: "Something" } }) { + title + content + } + } + ``` + +- [#6196](https://github.com/keystonejs/keystone/pull/6196) [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Removed `sortBy` argument from the GraphQL API for finding many items, Lists/DB API and to-many relationship fields. You should use `orderBy` instead. + +* [#6312](https://github.com/keystonejs/keystone/pull/6312) [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Updated `@graphql-ts/schema`. The second type parameter of `schema.Arg` exported from `@keystone-next/types` is now a boolean that defines whether or not the arg has a default value to make it easier to define circular input objects. + +- [#6217](https://github.com/keystonejs/keystone/pull/6217) [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - `disconnectAll` has been renamed to `disconnect` in to-one relationship inputs and the old `disconnect` field has been removed. There are also seperate input types for create and update where the input for create doesn't have `disconnect`. It's also now required that if you provide a to-one relationship input, you must provide exactly one field to the input. + + If you have a list called `Item`, the to-one relationship inputs now look like this: + + ```graphql + input ItemRelateToOneForCreateInput { + create: ItemCreateInput + connect: ItemWhereUniqueInput + } + input ItemRelateToOneForUpdateInput { + create: ItemCreateInput + connect: ItemWhereUniqueInput + disconnect: Boolean + } + ``` + +* [#6224](https://github.com/keystonejs/keystone/pull/6224) [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - `disconnectAll` has been replaced by `set` in to-many relationship inputs, the equivalent to `disconnectAll: true` is now `set: []`. There are also seperate input types for create and update where the input for create doesn't have `disconnect` or `set`. The inputs in the lists in the input field are now also non-null. + + If you have a list called `Item`, the to-many relationship inputs now look like this: + + ```graphql + input ItemRelateToManyForCreateInput { + create: [ItemCreateInput!] + connect: [ItemWhereUniqueInput!] + } + input ItemRelateToManyForUpdateInput { + disconnect: [ItemWhereUniqueInput!] + set: [ItemWhereUniqueInput!] + create: [ItemCreateInput!] + connect: [ItemWhereUniqueInput!] + } + ``` + +- [#6197](https://github.com/keystonejs/keystone/pull/6197) [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The generated CRUD queries, and some of the input types, in the GraphQL API have been renamed. + + If you have a list called `Item`, the query for multiple values, `allItems` will be renamed to `items`. The query for a single value, `Item`, will be renamed to `item`. + + Also, the input type used in the `updateItems` mutation has been renamed from `ItemsUpdateInput` to `ItemUpdateArgs`. + +* [#6211](https://github.com/keystonejs/keystone/pull/6211) [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The update mutations now accept `where` unique inputs instead of only an `id` and the `where` and `data` arguments are non-null. + + If you have a list called `Item`, the update mutations now look like this: + + ```graphql + type Mutation { + updateItem(where: ItemWhereUniqueInput!, data: ItemUpdateInput!): Item + updateItems(data: [ItemUpdateArgs!]!): [Item] + } + + input ItemUpdateArgs { + where: ItemWhereUniqueInput! + data: ItemUpdateInput! + } + ``` + +- [#6206](https://github.com/keystonejs/keystone/pull/6206) [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - The delete mutations now accept `where` unique inputs instead of only an `id`. + + If you have a list called `Item`, `deleteItem` now looks like `deleteItem(where: ItemWhereUniqueInput!): Item` and `deleteItems` now looks like `deleteItems(where: [ItemWhereUniqueInput!]!): [Item]` + +### Minor Changes + +- [#6267](https://github.com/keystonejs/keystone/pull/6267) [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590) Thanks [@timleslie](https://github.com/timleslie)! - Added `config.graphql.debug` option, which can be used to control whether debug information such as stack traces are included in the errors returned by the GraphQL API. + +### Patch Changes + +- [#6249](https://github.com/keystonejs/keystone/pull/6249) [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2) Thanks [@timleslie](https://github.com/timleslie)! - Updated types to allow the `'id'` field in `ui.labelField`, `ui.listView.initialColumns`, and `ui.listView.initialSort`. + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e)]: + - @keystone-next/fields@14.0.0 + ## 23.0.0 ### Major Changes diff --git a/packages/types/package.json b/packages/types/package.json index ec7047c5305..e3fadba83dd 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/types", - "version": "23.0.0", + "version": "24.0.0", "license": "MIT", "main": "dist/types.cjs.js", "module": "dist/types.esm.js", @@ -8,8 +8,9 @@ "node": "^12.20 || >= 14.13" }, "dependencies": { - "@graphql-ts/schema": "0.1.2", - "@keystone-next/fields": "^13.0.0", + "@babel/runtime": "^7.15.3", + "@graphql-ts/schema": "0.2.0", + "@keystone-next/fields": "^14.0.0", "apollo-server-types": "^0.9.0", "cors": "^2.8.5", "decimal.js": "10.3.1", diff --git a/packages/types/src/config/index.ts b/packages/types/src/config/index.ts index 0717d6c62aa..c7023c74d30 100644 --- a/packages/types/src/config/index.ts +++ b/packages/types/src/config/index.ts @@ -143,6 +143,40 @@ export type GraphQLConfig = { * @see https://www.apollographql.com/docs/apollo-server/api/apollo-server/#constructor */ apolloConfig?: Config; + /* + * When an error is returned from the GraphQL API, Apollo can include a stacktrace + * indicating where the error occurred. When Keystone is processing mutations, it + * will sometimes captures more than one error at a time, and then group these into + * a single error returned from the GraphQL API. Each of these errors will include + * a stacktrace. + * + * In general both categories of stacktrace are useful for debugging while developing, + * but should not be exposed in production, and this is the default behaviour of Keystone. + * + * You can use the `debug` option to change this behaviour. A use case for this + * would be if you need to send the stacktraces to a log, but do not want to return them + * from the API. In this case you could set `debug: true` and use + * `apolloConfig.formatError` to log the stacktraces and then strip them out before + * returning the error. + * + * ``` + * graphql: { + * debug: true, + * apolloConfig: { + * formatError: err => { + * console.error(err); + * delete err.extensions?.errors; + * delete err.extensions?.exception?.errors; + * delete err.extensions?.exception?.stacktrace; + * return err; + * }, + * }, + * } + * ``` + * * + * Default: process.env.NODE_ENV !== 'production' + */ + debug?: boolean; }; // config.extendGraphqlSchema diff --git a/packages/types/src/filters/enum-filter.ts b/packages/types/src/filters/enum-filter.ts new file mode 100644 index 00000000000..c6d91228411 --- /dev/null +++ b/packages/types/src/filters/enum-filter.ts @@ -0,0 +1,76 @@ +import { schema } from '../schema'; + +// yes, these two types have the fields but they're semantically different types +// (even though, yes, having EnumFilter by defined as EnumNullableFilter, would be the same type but names would show up differently in editors for example) + +export type EnumNullableFilter> = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + // can be null + not: schema.Arg>; +}>; + +export type EnumFilter> = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + not: schema.Arg>; +}>; + +type EnumNullableListFilterType> = schema.InputObjectType<{ + // can be null + equals: schema.Arg>>; + // can be null + has: schema.Arg; + hasEvery: schema.Arg>>; + hasSome: schema.Arg>>; + isEmpty: schema.Arg; +}>; + +export function enumFilters>( + enumType: Enum +): { + optional: EnumNullableFilter; + required: EnumFilter; + many: EnumNullableListFilterType; +} { + const optional: EnumNullableFilter = schema.inputObject({ + name: `${enumType.graphQLType.name}NullableFilter`, + fields: () => ({ + equals: schema.arg({ type: enumType }), + in: schema.arg({ type: schema.list(schema.nonNull(enumType)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(enumType)) }), + not: schema.arg({ type: optional }), + }), + }); + const required: EnumFilter = schema.inputObject({ + name: `${enumType.graphQLType.name}Filter`, + fields: () => ({ + equals: schema.arg({ type: enumType }), + in: schema.arg({ type: schema.list(schema.nonNull(enumType)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(enumType)) }), + not: schema.arg({ type: optional }), + }), + }); + const many: EnumNullableListFilterType = schema.inputObject({ + name: `${enumType.graphQLType.name}NullableListFilter`, + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.list(schema.nonNull(enumType)) }), + // can be null + has: schema.arg({ type: enumType }), + hasEvery: schema.arg({ type: schema.list(schema.nonNull(enumType)) }), + hasSome: schema.arg({ type: schema.list(schema.nonNull(enumType)) }), + isEmpty: schema.arg({ type: enumType }), + }), + }); + return { + optional, + required, + many, + }; +} diff --git a/packages/types/src/filters/index.ts b/packages/types/src/filters/index.ts new file mode 100644 index 00000000000..a83f16933d1 --- /dev/null +++ b/packages/types/src/filters/index.ts @@ -0,0 +1,88 @@ +export * as postgresql from './providers/postgresql'; +export * as sqlite from './providers/sqlite'; + +type EntriesAssumingNoExtraProps = { + [Key in keyof T]-?: [Key, T[Key]]; +}[keyof T][]; + +const objectEntriesButAssumeNoExtraProperties: (obj: T) => EntriesAssumingNoExtraProps = + Object.entries as any; + +type CommonFilter = { + equals?: T | null; + in?: readonly T[] | null; + notIn?: readonly T[] | null; + lt?: T | null; + lte?: T | null; + gt?: T | null; + gte?: T | null; + contains?: T | null; + startsWith?: T | null; + endsWith?: T | null; + not?: CommonFilter | null; +}; + +function internalResolveFilter( + entries: EntriesAssumingNoExtraProps>, + mode: 'default' | 'insensitive' | undefined +): object { + const entry = entries.shift(); + if (entry === undefined) return {}; + const [key, val] = entry; + if (val == null) { + return { + AND: [{ [key]: val }, internalResolveFilter(entries, mode)], + }; + } + switch (key) { + case 'equals': + case 'lt': + case 'lte': + case 'gt': + case 'gte': + case 'in': + case 'contains': + case 'startsWith': + case 'endsWith': { + return { + AND: [{ [key]: val, mode }, { not: null }, internalResolveFilter(entries, mode)], + }; + } + + case 'notIn': { + return { + AND: [ + { + NOT: [ + internalResolveFilter(objectEntriesButAssumeNoExtraProperties({ in: val }), mode), + ], + }, + internalResolveFilter(entries, mode), + ], + }; + } + case 'not': { + return { + AND: [ + { + NOT: [internalResolveFilter(objectEntriesButAssumeNoExtraProperties(val) as any, mode)], + }, + internalResolveFilter(entries, mode), + ], + }; + } + } +} + +export function resolveCommon(val: CommonFilter | null) { + if (val === null) return null; + return internalResolveFilter(objectEntriesButAssumeNoExtraProperties(val), undefined); +} + +export function resolveString( + val: (CommonFilter & { mode?: 'default' | 'insensitive' | null }) | null +) { + if (val === null) return null; + let { mode, ...rest } = val; + return internalResolveFilter(objectEntriesButAssumeNoExtraProperties(rest), mode as any); +} diff --git a/packages/types/src/filters/providers/postgresql.ts b/packages/types/src/filters/providers/postgresql.ts new file mode 100644 index 00000000000..cac24d557da --- /dev/null +++ b/packages/types/src/filters/providers/postgresql.ts @@ -0,0 +1,647 @@ +// Do not manually modify this file, it is automatically generated by the package at /prisma-utils in this repo. +// Update the script if you need this file to be different + +import { schema } from '../../schema'; + +import { QueryMode } from '../..'; + +type StringNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + contains: schema.Arg; + startsWith: schema.Arg; + endsWith: schema.Arg; + mode: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const StringNullableFilter: StringNullableFilterType = schema.inputObject({ + name: 'StringNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.String }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + contains: schema.arg({ type: schema.String }), + startsWith: schema.arg({ type: schema.String }), + endsWith: schema.arg({ type: schema.String }), + mode: schema.arg({ type: QueryMode }), + // can be null + not: schema.arg({ type: NestedStringNullableFilter }), + }), +}); + +type NestedStringNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + contains: schema.Arg; + startsWith: schema.Arg; + endsWith: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const NestedStringNullableFilter: NestedStringNullableFilterType = schema.inputObject({ + name: 'NestedStringNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.String }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + contains: schema.arg({ type: schema.String }), + startsWith: schema.arg({ type: schema.String }), + endsWith: schema.arg({ type: schema.String }), + // can be null + not: schema.arg({ type: NestedStringNullableFilter }), + }), +}); + +type StringFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + contains: schema.Arg; + startsWith: schema.Arg; + endsWith: schema.Arg; + mode: schema.Arg; + not: schema.Arg; +}>; + +const StringFilter: StringFilterType = schema.inputObject({ + name: 'StringFilter', + fields: () => ({ + equals: schema.arg({ type: schema.String }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + contains: schema.arg({ type: schema.String }), + startsWith: schema.arg({ type: schema.String }), + endsWith: schema.arg({ type: schema.String }), + mode: schema.arg({ type: QueryMode }), + not: schema.arg({ type: NestedStringFilter }), + }), +}); + +type NestedStringFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + contains: schema.Arg; + startsWith: schema.Arg; + endsWith: schema.Arg; + not: schema.Arg; +}>; + +const NestedStringFilter: NestedStringFilterType = schema.inputObject({ + name: 'NestedStringFilter', + fields: () => ({ + equals: schema.arg({ type: schema.String }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + contains: schema.arg({ type: schema.String }), + startsWith: schema.arg({ type: schema.String }), + endsWith: schema.arg({ type: schema.String }), + not: schema.arg({ type: NestedStringFilter }), + }), +}); + +type StringNullableListFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg>>; + // can be null + has: schema.Arg; + hasEvery: schema.Arg>>; + hasSome: schema.Arg>>; + isEmpty: schema.Arg; +}>; + +const StringNullableListFilter: StringNullableListFilterType = schema.inputObject({ + name: 'StringNullableListFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + has: schema.arg({ type: schema.String }), + hasEvery: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + hasSome: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + isEmpty: schema.arg({ type: schema.Boolean }), + }), +}); + +type BoolNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const BoolNullableFilter: BoolNullableFilterType = schema.inputObject({ + name: 'BooleanNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.Boolean }), + // can be null + not: schema.arg({ type: BoolNullableFilter }), + }), +}); + +type BoolFilterType = schema.InputObjectType<{ + equals: schema.Arg; + not: schema.Arg; +}>; + +const BoolFilter: BoolFilterType = schema.inputObject({ + name: 'BooleanFilter', + fields: () => ({ + equals: schema.arg({ type: schema.Boolean }), + not: schema.arg({ type: BoolFilter }), + }), +}); + +type BoolNullableListFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg>>; + // can be null + has: schema.Arg; + hasEvery: schema.Arg>>; + hasSome: schema.Arg>>; + isEmpty: schema.Arg; +}>; + +const BoolNullableListFilter: BoolNullableListFilterType = schema.inputObject({ + name: 'BooleanNullableListFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.list(schema.nonNull(schema.Boolean)) }), + // can be null + has: schema.arg({ type: schema.Boolean }), + hasEvery: schema.arg({ type: schema.list(schema.nonNull(schema.Boolean)) }), + hasSome: schema.arg({ type: schema.list(schema.nonNull(schema.Boolean)) }), + isEmpty: schema.arg({ type: schema.Boolean }), + }), +}); + +type IntNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const IntNullableFilter: IntNullableFilterType = schema.inputObject({ + name: 'IntNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.Int }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + lt: schema.arg({ type: schema.Int }), + lte: schema.arg({ type: schema.Int }), + gt: schema.arg({ type: schema.Int }), + gte: schema.arg({ type: schema.Int }), + // can be null + not: schema.arg({ type: IntNullableFilter }), + }), +}); + +type IntFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + not: schema.Arg; +}>; + +const IntFilter: IntFilterType = schema.inputObject({ + name: 'IntFilter', + fields: () => ({ + equals: schema.arg({ type: schema.Int }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + lt: schema.arg({ type: schema.Int }), + lte: schema.arg({ type: schema.Int }), + gt: schema.arg({ type: schema.Int }), + gte: schema.arg({ type: schema.Int }), + not: schema.arg({ type: IntFilter }), + }), +}); + +type IntNullableListFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg>>; + // can be null + has: schema.Arg; + hasEvery: schema.Arg>>; + hasSome: schema.Arg>>; + isEmpty: schema.Arg; +}>; + +const IntNullableListFilter: IntNullableListFilterType = schema.inputObject({ + name: 'IntNullableListFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + // can be null + has: schema.arg({ type: schema.Int }), + hasEvery: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + hasSome: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + isEmpty: schema.arg({ type: schema.Boolean }), + }), +}); + +type FloatNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const FloatNullableFilter: FloatNullableFilterType = schema.inputObject({ + name: 'FloatNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.Float }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + lt: schema.arg({ type: schema.Float }), + lte: schema.arg({ type: schema.Float }), + gt: schema.arg({ type: schema.Float }), + gte: schema.arg({ type: schema.Float }), + // can be null + not: schema.arg({ type: FloatNullableFilter }), + }), +}); + +type FloatFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + not: schema.Arg; +}>; + +const FloatFilter: FloatFilterType = schema.inputObject({ + name: 'FloatFilter', + fields: () => ({ + equals: schema.arg({ type: schema.Float }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + lt: schema.arg({ type: schema.Float }), + lte: schema.arg({ type: schema.Float }), + gt: schema.arg({ type: schema.Float }), + gte: schema.arg({ type: schema.Float }), + not: schema.arg({ type: FloatFilter }), + }), +}); + +type FloatNullableListFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg>>; + // can be null + has: schema.Arg; + hasEvery: schema.Arg>>; + hasSome: schema.Arg>>; + isEmpty: schema.Arg; +}>; + +const FloatNullableListFilter: FloatNullableListFilterType = schema.inputObject({ + name: 'FloatNullableListFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + // can be null + has: schema.arg({ type: schema.Float }), + hasEvery: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + hasSome: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + isEmpty: schema.arg({ type: schema.Boolean }), + }), +}); + +type DateTimeNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const DateTimeNullableFilter: DateTimeNullableFilterType = schema.inputObject({ + name: 'DateTimeNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.String }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + // can be null + not: schema.arg({ type: DateTimeNullableFilter }), + }), +}); + +type DateTimeFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + not: schema.Arg; +}>; + +const DateTimeFilter: DateTimeFilterType = schema.inputObject({ + name: 'DateTimeFilter', + fields: () => ({ + equals: schema.arg({ type: schema.String }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + not: schema.arg({ type: DateTimeFilter }), + }), +}); + +type DateTimeNullableListFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg>>; + // can be null + has: schema.Arg; + hasEvery: schema.Arg>>; + hasSome: schema.Arg>>; + isEmpty: schema.Arg; +}>; + +const DateTimeNullableListFilter: DateTimeNullableListFilterType = schema.inputObject({ + name: 'DateTimeNullableListFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + has: schema.arg({ type: schema.String }), + hasEvery: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + hasSome: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + isEmpty: schema.arg({ type: schema.Boolean }), + }), +}); + +type JsonNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const JsonNullableFilter: JsonNullableFilterType = schema.inputObject({ + name: 'JsonNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.JSON }), + // can be null + not: schema.arg({ type: schema.JSON }), + }), +}); + +type JsonFilterType = schema.InputObjectType<{ + equals: schema.Arg; + not: schema.Arg; +}>; + +const JsonFilter: JsonFilterType = schema.inputObject({ + name: 'JsonFilter', + fields: () => ({ + equals: schema.arg({ type: schema.JSON }), + not: schema.arg({ type: schema.JSON }), + }), +}); + +type JsonNullableListFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg>>; + // can be null + has: schema.Arg; + hasEvery: schema.Arg>>; + hasSome: schema.Arg>>; + isEmpty: schema.Arg; +}>; + +const JsonNullableListFilter: JsonNullableListFilterType = schema.inputObject({ + name: 'JsonNullableListFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.list(schema.nonNull(schema.JSON)) }), + // can be null + has: schema.arg({ type: schema.JSON }), + hasEvery: schema.arg({ type: schema.list(schema.nonNull(schema.JSON)) }), + hasSome: schema.arg({ type: schema.list(schema.nonNull(schema.JSON)) }), + isEmpty: schema.arg({ type: schema.Boolean }), + }), +}); + +type DecimalNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const DecimalNullableFilter: DecimalNullableFilterType = schema.inputObject({ + name: 'DecimalNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.String }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + // can be null + not: schema.arg({ type: DecimalNullableFilter }), + }), +}); + +type DecimalFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + not: schema.Arg; +}>; + +const DecimalFilter: DecimalFilterType = schema.inputObject({ + name: 'DecimalFilter', + fields: () => ({ + equals: schema.arg({ type: schema.String }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + not: schema.arg({ type: DecimalFilter }), + }), +}); + +type DecimalNullableListFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg>>; + // can be null + has: schema.Arg; + hasEvery: schema.Arg>>; + hasSome: schema.Arg>>; + isEmpty: schema.Arg; +}>; + +const DecimalNullableListFilter: DecimalNullableListFilterType = schema.inputObject({ + name: 'DecimalNullableListFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + has: schema.arg({ type: schema.String }), + hasEvery: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + hasSome: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + isEmpty: schema.arg({ type: schema.Boolean }), + }), +}); + +export const String = { + optional: StringNullableFilter, + required: StringFilter, + many: StringNullableListFilter, +}; + +export const Boolean = { + optional: BoolNullableFilter, + required: BoolFilter, + many: BoolNullableListFilter, +}; + +export const Int = { + optional: IntNullableFilter, + required: IntFilter, + many: IntNullableListFilter, +}; + +export const Float = { + optional: FloatNullableFilter, + required: FloatFilter, + many: FloatNullableListFilter, +}; + +export const DateTime = { + optional: DateTimeNullableFilter, + required: DateTimeFilter, + many: DateTimeNullableListFilter, +}; + +export const Json = { + optional: JsonNullableFilter, + required: JsonFilter, + many: JsonNullableListFilter, +}; + +export const Decimal = { + optional: DecimalNullableFilter, + required: DecimalFilter, + many: DecimalNullableListFilter, +}; + +export { enumFilters as enum } from '../enum-filter'; diff --git a/packages/types/src/filters/providers/sqlite.ts b/packages/types/src/filters/providers/sqlite.ts new file mode 100644 index 00000000000..20591155798 --- /dev/null +++ b/packages/types/src/filters/providers/sqlite.ts @@ -0,0 +1,438 @@ +// Do not manually modify this file, it is automatically generated by the package at /prisma-utils in this repo. +// Update the script if you need this file to be different + +import { schema } from '../../schema'; + +type StringNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + contains: schema.Arg; + startsWith: schema.Arg; + endsWith: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const StringNullableFilter: StringNullableFilterType = schema.inputObject({ + name: 'StringNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.String }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + contains: schema.arg({ type: schema.String }), + startsWith: schema.arg({ type: schema.String }), + endsWith: schema.arg({ type: schema.String }), + // can be null + not: schema.arg({ type: NestedStringNullableFilter }), + }), +}); + +type NestedStringNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + contains: schema.Arg; + startsWith: schema.Arg; + endsWith: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const NestedStringNullableFilter: NestedStringNullableFilterType = schema.inputObject({ + name: 'NestedStringNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.String }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + contains: schema.arg({ type: schema.String }), + startsWith: schema.arg({ type: schema.String }), + endsWith: schema.arg({ type: schema.String }), + // can be null + not: schema.arg({ type: NestedStringNullableFilter }), + }), +}); + +type StringFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + contains: schema.Arg; + startsWith: schema.Arg; + endsWith: schema.Arg; + not: schema.Arg; +}>; + +const StringFilter: StringFilterType = schema.inputObject({ + name: 'StringFilter', + fields: () => ({ + equals: schema.arg({ type: schema.String }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + contains: schema.arg({ type: schema.String }), + startsWith: schema.arg({ type: schema.String }), + endsWith: schema.arg({ type: schema.String }), + not: schema.arg({ type: NestedStringFilter }), + }), +}); + +type NestedStringFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + contains: schema.Arg; + startsWith: schema.Arg; + endsWith: schema.Arg; + not: schema.Arg; +}>; + +const NestedStringFilter: NestedStringFilterType = schema.inputObject({ + name: 'NestedStringFilter', + fields: () => ({ + equals: schema.arg({ type: schema.String }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + contains: schema.arg({ type: schema.String }), + startsWith: schema.arg({ type: schema.String }), + endsWith: schema.arg({ type: schema.String }), + not: schema.arg({ type: NestedStringFilter }), + }), +}); + +type BoolNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const BoolNullableFilter: BoolNullableFilterType = schema.inputObject({ + name: 'BooleanNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.Boolean }), + // can be null + not: schema.arg({ type: BoolNullableFilter }), + }), +}); + +type BoolFilterType = schema.InputObjectType<{ + equals: schema.Arg; + not: schema.Arg; +}>; + +const BoolFilter: BoolFilterType = schema.inputObject({ + name: 'BooleanFilter', + fields: () => ({ + equals: schema.arg({ type: schema.Boolean }), + not: schema.arg({ type: BoolFilter }), + }), +}); + +type IntNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const IntNullableFilter: IntNullableFilterType = schema.inputObject({ + name: 'IntNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.Int }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + lt: schema.arg({ type: schema.Int }), + lte: schema.arg({ type: schema.Int }), + gt: schema.arg({ type: schema.Int }), + gte: schema.arg({ type: schema.Int }), + // can be null + not: schema.arg({ type: IntNullableFilter }), + }), +}); + +type IntFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + not: schema.Arg; +}>; + +const IntFilter: IntFilterType = schema.inputObject({ + name: 'IntFilter', + fields: () => ({ + equals: schema.arg({ type: schema.Int }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.Int)) }), + lt: schema.arg({ type: schema.Int }), + lte: schema.arg({ type: schema.Int }), + gt: schema.arg({ type: schema.Int }), + gte: schema.arg({ type: schema.Int }), + not: schema.arg({ type: IntFilter }), + }), +}); + +type FloatNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const FloatNullableFilter: FloatNullableFilterType = schema.inputObject({ + name: 'FloatNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.Float }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + lt: schema.arg({ type: schema.Float }), + lte: schema.arg({ type: schema.Float }), + gt: schema.arg({ type: schema.Float }), + gte: schema.arg({ type: schema.Float }), + // can be null + not: schema.arg({ type: FloatNullableFilter }), + }), +}); + +type FloatFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + not: schema.Arg; +}>; + +const FloatFilter: FloatFilterType = schema.inputObject({ + name: 'FloatFilter', + fields: () => ({ + equals: schema.arg({ type: schema.Float }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.Float)) }), + lt: schema.arg({ type: schema.Float }), + lte: schema.arg({ type: schema.Float }), + gt: schema.arg({ type: schema.Float }), + gte: schema.arg({ type: schema.Float }), + not: schema.arg({ type: FloatFilter }), + }), +}); + +type DateTimeNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const DateTimeNullableFilter: DateTimeNullableFilterType = schema.inputObject({ + name: 'DateTimeNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.String }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + // can be null + not: schema.arg({ type: DateTimeNullableFilter }), + }), +}); + +type DateTimeFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + not: schema.Arg; +}>; + +const DateTimeFilter: DateTimeFilterType = schema.inputObject({ + name: 'DateTimeFilter', + fields: () => ({ + equals: schema.arg({ type: schema.String }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + not: schema.arg({ type: DateTimeFilter }), + }), +}); + +type DecimalNullableFilterType = schema.InputObjectType<{ + // can be null + equals: schema.Arg; + // can be null + in: schema.Arg>>; + // can be null + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + // can be null + not: schema.Arg; +}>; + +const DecimalNullableFilter: DecimalNullableFilterType = schema.inputObject({ + name: 'DecimalNullableFilter', + fields: () => ({ + // can be null + equals: schema.arg({ type: schema.String }), + // can be null + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + // can be null + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + // can be null + not: schema.arg({ type: DecimalNullableFilter }), + }), +}); + +type DecimalFilterType = schema.InputObjectType<{ + equals: schema.Arg; + in: schema.Arg>>; + notIn: schema.Arg>>; + lt: schema.Arg; + lte: schema.Arg; + gt: schema.Arg; + gte: schema.Arg; + not: schema.Arg; +}>; + +const DecimalFilter: DecimalFilterType = schema.inputObject({ + name: 'DecimalFilter', + fields: () => ({ + equals: schema.arg({ type: schema.String }), + in: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + notIn: schema.arg({ type: schema.list(schema.nonNull(schema.String)) }), + lt: schema.arg({ type: schema.String }), + lte: schema.arg({ type: schema.String }), + gt: schema.arg({ type: schema.String }), + gte: schema.arg({ type: schema.String }), + not: schema.arg({ type: DecimalFilter }), + }), +}); + +export const String = { + optional: StringNullableFilter, + required: StringFilter, +}; + +export const Boolean = { + optional: BoolNullableFilter, + required: BoolFilter, +}; + +export const Int = { + optional: IntNullableFilter, + required: IntFilter, +}; + +export const Float = { + optional: FloatNullableFilter, + required: FloatFilter, +}; + +export const DateTime = { + optional: DateTimeNullableFilter, + required: DateTimeFilter, +}; + +export const Decimal = { + optional: DecimalNullableFilter, + required: DecimalFilter, +}; + +export { enumFilters as enum } from '../enum-filter'; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index e3a6ce3cb54..a614595cca9 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -7,5 +7,6 @@ export * from './base'; export * from './context'; export * from './next-fields'; export * as legacyFilters from './legacy-filters'; +export * as filters from './filters'; export * from './schema'; export { jsonFieldTypePolyfilledForSQLite } from './json-field-type-polyfill-for-sqlite'; diff --git a/packages/types/src/json-field-type-polyfill-for-sqlite.ts b/packages/types/src/json-field-type-polyfill-for-sqlite.ts index 5ea475b0fed..9ad7a9a307c 100644 --- a/packages/types/src/json-field-type-polyfill-for-sqlite.ts +++ b/packages/types/src/json-field-type-polyfill-for-sqlite.ts @@ -98,8 +98,8 @@ export function jsonFieldTypePolyfilledForSQLite< ScalarDBField<'Json', 'optional'>, CreateArg, UpdateArg, - schema.Arg, - schema.Arg + schema.Arg, + schema.Arg > & { input?: { uniqueWhere?: undefined; diff --git a/packages/types/src/next-fields.ts b/packages/types/src/next-fields.ts index 349b815ed91..645f36b37a8 100644 --- a/packages/types/src/next-fields.ts +++ b/packages/types/src/next-fields.ts @@ -39,17 +39,21 @@ export type NextFieldType< | schema.Arg | undefined, UpdateArg extends schema.Arg = schema.Arg, - UniqueWhereArg extends schema.Arg = schema.Arg< + UniqueWhereArg extends schema.Arg = schema.Arg< schema.NullableInputType, - undefined + false >, - OrderByArg extends schema.Arg = schema.Arg< + OrderByArg extends schema.Arg = schema.Arg< schema.NullableInputType, - undefined + false + >, + FilterArg extends schema.Arg = schema.Arg< + schema.NullableInputType, + false > > = { dbField: TDBField; -} & FieldTypeWithoutDBField; +} & FieldTypeWithoutDBField; type ScalarPrismaTypes = { String: string; @@ -111,6 +115,11 @@ export const orderDirectionEnum = schema.enum({ values: schema.enumValues(['asc', 'desc']), }); +export const QueryMode = schema.enum({ + name: 'QueryMode', + values: schema.enumValues(['default', 'insensitive']), +}); + export type RelationDBField = { kind: 'relation'; list: string; @@ -224,6 +233,39 @@ type FieldInputResolver = ( relationshipInputResolver: RelationshipInputResolver ) => MaybePromise; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type DBFieldFiltersInner = Record; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type DBFieldFilters = + | ({ + AND?: DBFieldFiltersInner; + OR?: DBFieldFiltersInner; + NOT?: DBFieldFiltersInner; + } & DBFieldFiltersInner) + | null; + +export type WhereFieldInputArg< + TDBField extends DBField, + TArg extends schema.Arg +> = { + arg: TArg; +} & ResolveFunc< + FieldInputResolver< + Exclude, undefined>, + DBFieldFilters, + any + // i think this is broken because variance? + // TDBField extends RelationDBField + // ? ( + // input: { + // many: types.InferValueFromArg>; + // one: types.InferValueFromArg>; + // }[Mode] + // ) => Promise + // : undefined + > +>; + export type UpdateFieldInputArg< TDBField extends DBField, TArg extends schema.Arg @@ -309,17 +351,22 @@ export type FieldTypeWithoutDBField< | schema.Arg | undefined, UpdateArg extends schema.Arg = schema.Arg, - UniqueWhereArg extends schema.Arg = schema.Arg< + UniqueWhereArg extends schema.Arg = schema.Arg< schema.NullableInputType, - undefined + false >, - OrderByArg extends schema.Arg = schema.Arg< + OrderByArg extends schema.Arg = schema.Arg< schema.NullableInputType, - undefined + false + >, + FilterArg extends schema.Arg = schema.Arg< + schema.NullableInputType, + false > > = { input?: { uniqueWhere?: UniqueWhereFieldInputArg, UniqueWhereArg>; + where?: WhereFieldInputArg; create?: CreateFieldInputArg; update?: UpdateFieldInputArg; orderBy?: OrderByFieldInputArg, OrderByArg>; @@ -346,10 +393,18 @@ export function fieldType(dbField: TDBField) { return function < CreateArg extends schema.Arg | undefined, UpdateArg extends schema.Arg, - UniqueWhereArg extends schema.Arg, - OrderByArg extends schema.Arg + UniqueWhereArg extends schema.Arg, + OrderByArg extends schema.Arg, + FilterArg extends schema.Arg >( - stuff: FieldTypeWithoutDBField + stuff: FieldTypeWithoutDBField< + TDBField, + CreateArg, + UpdateArg, + UniqueWhereArg, + OrderByArg, + FilterArg + > ): NextFieldType { return { ...stuff, dbField }; }; @@ -367,6 +422,11 @@ export type TypesForList = { findManyArgs: FindManyArgs; relateTo: { many: { + where: schema.InputObjectType<{ + every: schema.Arg; + some: schema.Arg; + none: schema.Arg; + }>; create: schema.InputObjectType<{ connect: schema.Arg>>; create?: schema.Arg>>; @@ -393,13 +453,13 @@ export type TypesForList = { }; export type FindManyArgs = { - where: schema.Arg, {}>; + where: schema.Arg, true>; orderBy: schema.Arg< schema.NonNullType>>, - Record[] + true >; - first: schema.Arg; - skip: schema.Arg, number>; + take: schema.Arg; + skip: schema.Arg, true>; }; export type FindManyArgsValue = schema.InferValueFromArgs; diff --git a/packages/types/src/utils.ts b/packages/types/src/utils.ts index b6aee7d0508..7100c4c35f8 100644 --- a/packages/types/src/utils.ts +++ b/packages/types/src/utils.ts @@ -11,7 +11,7 @@ export type BaseGeneratedListTypes = { args: { listQuery: { readonly where?: GraphQLInput | null; - readonly first?: number | null; + readonly take?: number | null; readonly skip?: number; readonly orderBy?: | Record diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 494c10fdf6f..3e9da6bed6a 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @keystone-next/utils +## 1.0.4 + +### Patch Changes + +- Updated dependencies [[`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/types@24.0.0 + ## 1.0.3 ### Patch Changes diff --git a/packages/utils/package.json b/packages/utils/package.json index d7c4246fed3..43197d74571 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,7 +1,7 @@ { "name": "@keystone-next/utils", "description": "Common utility functions used throughout @keystone-next packages.", - "version": "1.0.3", + "version": "1.0.4", "author": "The KeystoneJS Development Team", "license": "MIT", "homepage": "https://github.com/keystonejs/keystone", @@ -11,8 +11,8 @@ "node": "^12.20 || >= 14.13" }, "dependencies": { - "@babel/runtime": "^7.14.8", - "@keystone-next/types": "^23.0.0" + "@babel/runtime": "^7.15.3", + "@keystone-next/types": "^24.0.0" }, "repository": "https://github.com/keystonejs/keystone/tree/master/packages/utils" } diff --git a/prisma-utils/.gitignore b/prisma-utils/.gitignore new file mode 100644 index 00000000000..80697f2272d --- /dev/null +++ b/prisma-utils/.gitignore @@ -0,0 +1 @@ +src/generated \ No newline at end of file diff --git a/prisma-utils/README.md b/prisma-utils/README.md new file mode 100644 index 00000000000..bfe77378a66 --- /dev/null +++ b/prisma-utils/README.md @@ -0,0 +1,5 @@ +# prisma-utils + +This (unpublished) package generates the GraphQL filters for most field types in Keystone from Prisma information. + +You can run it with `yarn generate-filters` from the root, you should run this whenever CI fails because of this which should only be after updating Prisma or the script. diff --git a/prisma-utils/package.json b/prisma-utils/package.json new file mode 100644 index 00000000000..925b4d8e5fa --- /dev/null +++ b/prisma-utils/package.json @@ -0,0 +1,17 @@ +{ + "name": "@keystone-next/prisma-utils", + "version": "0.0.0", + "private": true, + "main": "dist/prisma-utils.cjs.js", + "dependencies": { + "@prisma/generator-helper": "2.29.1", + "@prisma/sdk": "2.29.1", + "fs-extra": "^10.0.0", + "prettier": "^2.3.2" + }, + "scripts": { + "generate": "node .", + "verify": "VERIFY=1 node ." + }, + "repository": "https://github.com/keystonejs/keystone/tree/master/prisma-utils" +} diff --git a/prisma-utils/src/index.ts b/prisma-utils/src/index.ts new file mode 100644 index 00000000000..c4570a8c39f --- /dev/null +++ b/prisma-utils/src/index.ts @@ -0,0 +1,313 @@ +// this file generates the GraphQL filter types for most of our fields. +// generating them from the prisma information isn't really about time or convenience. +// it's about clearly showing what the rules for getting the filters are and what the exceptions are. + +import { deepStrictEqual } from 'assert'; +import { isDeepStrictEqual } from 'util'; +import fs from 'fs-extra'; +import { DMMF } from '@prisma/generator-helper'; +import { getDMMF } from '@prisma/sdk'; +import { format, resolveConfig } from 'prettier'; + +const providers = ['postgresql', 'sqlite'] as const; + +type Provider = typeof providers[number]; + +// https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#model-field-scalar-types +// only the prisma scalars that we currently use are here because adding one requires choosing a graphql scalar +// we can add them when we want field types for those scalars +// the missing ones are: +// - Bytes +// - BigInt +// - Unsupported (this one can't be interacted with in the prisma client (and therefore cannot be filtered) so it's irrelevant here) +const scalarTypes = ['String', 'Boolean', 'Int', 'Float', 'DateTime', 'Json', 'Decimal'] as const; + +const getScalarTypesForProvider = (provider: Provider): readonly typeof scalarTypes[number][] => + provider === 'sqlite' ? scalarTypes.filter(x => x !== 'Json') : scalarTypes; + +const getSchemaForProvider = (provider: Provider) => { + const scalarTypesForProvider = getScalarTypesForProvider(provider); + return `datasource ${provider} { + url = env("DATABASE_URL") + provider = "${provider}" +} + +generator client { + provider = "prisma-client-js" +} + +model Optional { + id Int @id @default(autoincrement()) + ${scalarTypesForProvider.map(scalarType => `${scalarType} ${scalarType}?`).join('\n')} +} + +model Required { + id Int @id @default(autoincrement()) + ${scalarTypesForProvider.map(scalarType => `${scalarType} ${scalarType}`).join('\n')} +} + +${ + provider === 'postgresql' + ? `model Many { + id Int @id @default(autoincrement()) + ${scalarTypesForProvider.map(scalarType => `${scalarType} ${scalarType}[]`).join('\n')} +}` + : '' +} + +`; +}; + +(async () => { + const prettierConfig = await resolveConfig(`${__dirname}/index.ts`); + assert(prettierConfig !== null); + for (const provider of providers) { + const schema = getSchemaForProvider(provider); + const dmmf = await getDMMF({ datamodel: schema }); + + await fs.outputFile( + `${__dirname}/generated/${provider}.json`, + JSON.stringify(dmmf.schema.inputObjectTypes, null, 2) + ); + const types = getInputTypesByName(dmmf.schema.inputObjectTypes.prisma); + const rootTypes = getScalarTypesForProvider(provider).flatMap((scalar: string) => { + if (scalar === 'Boolean') { + scalar = 'Bool'; + } + if (scalar === 'SomeEnum') { + // the filter types have to be generic over the enum so it's just easier to write it out manually + // but we still want this here to snapshot what the filters look like for a given prisma version & provider combination + return []; + } + let types = [`${scalar}NullableFilter`, `${scalar}Filter`]; + if (provider === 'postgresql') { + // i'm not sure this is says nullable when they're not nullable? + // i don't think there is a nullable and non-nullable list? + types.push(`${scalar}NullableListFilter`); + } + return types; + }); + for (const typeName of Object.keys(types)) { + replaceNestedNotFilterTypes(types, typeName); + } + const referencedTypes = new Set(); + for (const typeName of rootTypes) { + collectReferencedTypes(types, typeName, referencedTypes); + } + if (provider !== 'sqlite') { + deepStrictEqual( + dmmf.schema.enumTypes.prisma.find(x => x.name === 'QueryMode'), + { name: 'QueryMode', values: ['default', 'insensitive'] } + ); + } + + await fs.outputFile( + `${__dirname}/generated/only-filters/${provider}.json`, + JSON.stringify( + Object.fromEntries([...referencedTypes].map(typeName => [typeName, types[typeName]])), + null, + 2 + ) + ); + const filepath = `${__dirname}/../../packages/types/src/filters/providers/${provider}.ts`; + const newContent = format( + `// Do not manually modify this file, it is automatically generated by the package at /prisma-utils in this repo. +// Update the script if you need this file to be different + + import { schema } from '../../schema'; + + +${provider !== 'sqlite' ? `import { QueryMode } from '../..'` : ''} + +${[...referencedTypes].map(typeName => printInputTypeForGraphQLTS(typeName, types)).join('\n\n')} + +${getScalarTypesForProvider(provider) + .map(scalar => { + const scalarInFilterName = scalar === 'Boolean' ? 'Bool' : scalar; + return `export const ${scalar} = { +optional: ${scalarInFilterName}NullableFilter, +required: ${scalarInFilterName}Filter, +${provider === 'postgresql' ? `many: ${scalarInFilterName}NullableListFilter` : ''} +}`; + }) + .join('\n\n')} + +export {enumFilters as enum } from '../enum-filter' + `, + { ...prettierConfig, filepath } + ); + if (process.env.VERIFY) { + const contents = await fs.readFile(filepath, 'utf8'); + if (contents !== newContent) { + throw new Error( + `The file at ${filepath} is inconsistent with the expected generated contents, please run \`yarn generate-filters\` from the root to update it` + ); + } + } else { + await fs.outputFile(filepath, newContent); + } + } +})().catch(x => { + console.error(x); + process.exit(1); +}); + +function getInputTypesByName(types: DMMF.InputType[]) { + return Object.fromEntries(types.map(x => [x.name, x])); +} + +function assert(condition: boolean, message?: string): asserts condition { + if (!condition) { + debugger; + throw new Error(`assertion failed${message === undefined ? '' : `: ${message}`}`); + } +} + +function replaceNestedNotFilterTypes( + inputTypesByName: Record, + inputTypeName: string +) { + // we want to not replace the nested filter for strings because we don't replace it on postgresql + // and we want the naming to be the same on SQLite + if (inputTypeName.includes('String')) return; + const inputType = inputTypesByName[inputTypeName]; + for (const field of inputType.fields) { + if (field.name === 'not') { + const objectInput = field.inputTypes.find(input => input.namespace === 'prisma'); + if ( + typeof objectInput?.type === 'string' && + isDeepStrictEqual(inputType.fields, inputTypesByName[objectInput.type].fields) + ) { + objectInput.type = inputTypeName; + } + } + } +} + +const expectedScalars = new Set(['Null', 'QueryMode', ...scalarTypes]); + +function collectReferencedTypes( + inputTypesByName: Record, + inputTypeName: string, + referencedTypes: Set +) { + referencedTypes.add(inputTypeName); + const inputType = inputTypesByName[inputTypeName]; + assert(inputType !== undefined, `could not find input type ${inputTypeName}`); + + for (const field of inputType.fields) { + assert(!field.isRequired, 'unexpected required field'); + for (const inputType of field.inputTypes) { + assert(typeof inputType.type === 'string', 'unexpected non-type name in input types'); + if (inputType.location === 'scalar' || inputType.location === 'enumTypes') { + assert(expectedScalars.has(inputType.type), `unexpected scalar ${inputType.type}`); + continue; + } + assert(inputType.location === 'inputObjectTypes', `unexpected ${inputType.location} type`); + if (!referencedTypes.has(inputType.type)) { + collectReferencedTypes(inputTypesByName, inputType.type, referencedTypes); + } + } + } +} + +/** + * Note a field can be both nullable and a list. + * + * Translated into typescript, that means `Array | null`, + * not `Array` or `Array | null` + */ +type TransformedInputTypeField = { + type: string; + isNullable: boolean; + isList: boolean; +}; + +function pickInputTypeForField(field: DMMF.SchemaArg): TransformedInputTypeField { + assert(!field.isRequired, 'unexpected required field'); + // null is already represented with field.isNullable + const inputTypesWithoutNull = field.inputTypes.filter(type => { + if (type.type === 'Null') { + assert(!type.isList, 'unexpected null list'); + assert(field.isNullable, 'unexpected isNullable false when null type in input types'); + return false; + } + return true; + }); + + assert( + inputTypesWithoutNull.length + Number(field.isNullable) === field.inputTypes.length, + 'unexpected isNullable false when null type in input types' + ); + const inputType = (() => { + if (inputTypesWithoutNull.length === 1) { + return inputTypesWithoutNull[0]; + } + assert( + inputTypesWithoutNull.length === 2, + 'unexpected more than two input types excluding null' + ); + const inputType = inputTypesWithoutNull.find(x => x.location == 'inputObjectTypes'); + assert( + !!inputType, + 'could not find input object type when more than one input type excluding null' + ); + return inputType; + })(); + assert(typeof inputType.type === 'string'); + return { + isList: inputType.isList, + isNullable: field.isNullable, + type: scalarsToGqlScalars[inputType.type] ?? inputType.type, + }; +} + +function printInputTypeForGraphQLTS( + inputTypeName: string, + inputTypesByName: Record +) { + const inputType = inputTypesByName[inputTypeName]; + assert(inputType !== undefined, `could not find input type ${inputTypeName}`); + const expectedMaxMinNumFields = inputTypeName.endsWith('NullableListFilter') ? 1 : null; + assert(inputType.constraints.maxNumFields === expectedMaxMinNumFields); + assert(inputType.constraints.minNumFields === expectedMaxMinNumFields); + const nameOfInputObjectTypeType = `${inputTypeName}Type`; + const fields = inputType.fields.map(x => [x.name, pickInputTypeForField(x)] as const); + return `type ${nameOfInputObjectTypeType} = schema.InputObjectType<{ + ${fields + .map(([name, field]) => { + return `${field.isNullable ? '// can be null\n' : ''}${name}: schema.Arg<${ + field.isList + ? `schema.ListType>` + : `typeof ${field.type}` + }>`; + }) + .join(',\n')} + }> + + const ${inputTypeName}: ${nameOfInputObjectTypeType} = schema.inputObject({ + name: '${ + // we want to use Boolean instead of Bool because GraphQL calls it Boolean + inputTypeName.replace('Bool', 'Boolean') + }', + fields: () => ({ + ${fields + .map(([name, field]) => { + return `${field.isNullable ? '// can be null\n' : ''}${name}: schema.arg({ type: ${ + field.isList ? `schema.list(schema.nonNull(${field.type}))` : field.type + } })`; + }) + .join(',\n')} + }) + })`; +} + +const scalarsToGqlScalars: Record = { + String: 'schema.String', + Boolean: 'schema.Boolean', + Int: 'schema.Int', + Float: 'schema.Float', + Json: 'schema.JSON', + DateTime: 'schema.String', + Decimal: 'schema.String', +}; diff --git a/tests/admin-ui-tests/CHANGELOG.md b/tests/admin-ui-tests/CHANGELOG.md index 5cc500dd0a4..e552f895fa8 100644 --- a/tests/admin-ui-tests/CHANGELOG.md +++ b/tests/admin-ui-tests/CHANGELOG.md @@ -1,5 +1,14 @@ # @keystone-next/admin-ui-tests +## 0.0.4 + +### Patch Changes + +- Updated dependencies [[`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/types@24.0.0 + - @keystone-next/testing@1.1.1 + - @keystone-next/utils@1.0.4 + ## 0.0.3 ### Patch Changes diff --git a/tests/admin-ui-tests/package.json b/tests/admin-ui-tests/package.json index 81d9992dab8..c59a07e04fe 100644 --- a/tests/admin-ui-tests/package.json +++ b/tests/admin-ui-tests/package.json @@ -2,7 +2,7 @@ "name": "@keystone-next/admin-ui-tests", "description": "A set of tests for the KeystoneJS Admin UI.", "private": true, - "version": "0.0.3", + "version": "0.0.4", "author": "The KeystoneJS Development Team", "license": "MIT", "engines": { @@ -16,7 +16,7 @@ "@keystone-next/keystone": "*", "execa": "^5.1.1", "express": "^4.17.1", - "playwright": "^1.13.1", + "playwright": "^1.14.0", "treekill": "^1.0.0" }, "scripts": { @@ -25,9 +25,9 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/testing": "^1.1.0", - "@keystone-next/types": "^23.0.0", - "@keystone-next/utils": "^1.0.3", + "@keystone-next/testing": "^1.1.1", + "@keystone-next/types": "^24.0.0", + "@keystone-next/utils": "^1.0.4", "@manypkg/find-root": "^1.1.0", "dotenv": "^10.0.0", "tree-kill": "^1.2.2" diff --git a/tests/api-tests/CHANGELOG.md b/tests/api-tests/CHANGELOG.md index f48751d233d..59cf2f73b16 100644 --- a/tests/api-tests/CHANGELOG.md +++ b/tests/api-tests/CHANGELOG.md @@ -1,5 +1,18 @@ # @keystonejs/api-tests +## 11.1.0 + +### Minor Changes + +- [#6276](https://github.com/keystonejs/keystone/pull/6276) [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb) Thanks [@gautamsi](https://github.com/gautamsi)! - Added option for `Bearer` token auth when using session. + +### Patch Changes + +- Updated dependencies [[`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`8187ea019`](https://github.com/keystonejs/keystone/commit/8187ea019a212874f3c602573af3382c6f3bd3b2), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/types@24.0.0 + - @keystone-next/testing@1.1.1 + - @keystone-next/utils@1.0.4 + ## 11.0.6 ### Patch Changes diff --git a/tests/api-tests/access-control/authed.test.ts b/tests/api-tests/access-control/authed.test.ts index 963c6e670e9..746bab83f25 100644 --- a/tests/api-tests/access-control/authed.test.ts +++ b/tests/api-tests/access-control/authed.test.ts @@ -22,7 +22,7 @@ const expectNoAccess = ( errors: readonly GraphQLError[] | undefined, name: N ) => { - expectAccessDenied(errors, [{ path: [name] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: [name] }]); expect(data?.[name]).toBe(null); }; @@ -158,7 +158,9 @@ describe('Authed', () => { expect(data).toEqual({ authenticatedItem: { id: user.id, yesRead: user.yesRead, noRead: null }, }); - expectAccessDenied(errors, [{ path: ['authenticatedItem', 'noRead'] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['authenticatedItem', 'noRead'] }, + ]); }); (['imperative', 'declarative'] as const).forEach(mode => { @@ -218,7 +220,7 @@ describe('Authed', () => { test(`multiple not existing: ${JSON.stringify(access)}`, async () => { const _items = await context.lists[listKey].findMany({ - where: { id_in: [FAKE_ID[provider], FAKE_ID_2[provider]] }, + where: { id: { in: [FAKE_ID[provider], FAKE_ID_2[provider]] } }, }); expect(_items).toHaveLength(0); }); @@ -442,7 +444,7 @@ describe('Authed', () => { if (mode === 'imperative') { expectNamedArray(data, errors, multiDeleteMutationName, [validId1, validId2]); } else { - expectAccessDenied(errors, [ + expectAccessDenied('dev', false, undefined, errors, [ { path: [multiDeleteMutationName, 0] }, { path: [multiDeleteMutationName, 1] }, ]); @@ -459,7 +461,9 @@ describe('Authed', () => { if (mode === 'imperative') { expectNamedArray(data, errors, multiDeleteMutationName, [validId1, invalidId]); } else { - expectAccessDenied(errors, [{ path: [multiDeleteMutationName, 1] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: [multiDeleteMutationName, 1] }, + ]); expect(data).toEqual({ [multiDeleteMutationName]: [{ id: validId1 }, null] }); } }); @@ -468,7 +472,7 @@ describe('Authed', () => { const multiDeleteMutationName = `delete${nameFn[mode](access)}s`; const query = `mutation { ${multiDeleteMutationName}(where: [{ id: "${FAKE_ID[provider]}" }, { id: "${FAKE_ID_2[provider]}" }]) { id } }`; const { data, errors } = await context.graphql.raw({ query }); - expectAccessDenied(errors, [ + expectAccessDenied('dev', false, undefined, errors, [ { path: [multiDeleteMutationName, 0] }, { path: [multiDeleteMutationName, 1] }, ]); diff --git a/tests/api-tests/access-control/mutations-field-static.test.ts b/tests/api-tests/access-control/mutations-field-static.test.ts index 5ce3f47daca..7eebb4e8b4e 100644 --- a/tests/api-tests/access-control/mutations-field-static.test.ts +++ b/tests/api-tests/access-control/mutations-field-static.test.ts @@ -51,7 +51,7 @@ describe('Access control - Imperative => static', () => { // Returns null and throws an error expect(data).toEqual({ createUser: null }); - expectAccessDenied(errors, [{ path: ['createUser'] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: ['createUser'] }]); // Only the original user should exist const _users = await context.lists.User.findMany({ query: 'id name other' }); @@ -79,7 +79,7 @@ describe('Access control - Imperative => static', () => { // Returns null and throws an error expect(data).toEqual({ updateUser: null }); - expectAccessDenied(errors, [{ path: ['updateUser'] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: ['updateUser'] }]); // User should have its original name const _users = await context.lists.User.findMany({ query: 'id name other' }); @@ -118,7 +118,10 @@ describe('Access control - Imperative => static', () => { }); // The invalid updates should have errors which point to the nulls in their path - expectAccessDenied(errors, [{ path: ['createUsers', 1] }, { path: ['createUsers', 3] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['createUsers', 1] }, + { path: ['createUsers', 3] }, + ]); // Valid users should exist in the database const users = await context.lists.User.findMany({ @@ -172,7 +175,10 @@ describe('Access control - Imperative => static', () => { }); // The invalid updates should have errors which point to the nulls in their path - expectAccessDenied(errors, [{ path: ['updateUsers', 1] }, { path: ['updateUsers', 3] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['updateUsers', 1] }, + { path: ['updateUsers', 3] }, + ]); // All users should still exist in the database const _users = await context.lists.User.findMany({ diff --git a/tests/api-tests/access-control/mutations-list-declarative.test.ts b/tests/api-tests/access-control/mutations-list-declarative.test.ts index cbe015f6a77..c1618c85856 100644 --- a/tests/api-tests/access-control/mutations-list-declarative.test.ts +++ b/tests/api-tests/access-control/mutations-list-declarative.test.ts @@ -12,8 +12,8 @@ const runner = setupTestRunner({ access: { read: true, create: true, - update: () => ({ name_not: 'bad' }), - delete: async () => ({ name_not_contains: 'no delete' }), + update: () => ({ name: { not: { equals: 'bad' } } }), + delete: async () => ({ name: { not: { contains: 'no delete' } } }), }, }), }), @@ -40,7 +40,7 @@ describe('Access control - Imperative => declarative', () => { // Returns null and throws an error expect(data).toEqual({ updateUser: null }); - expectAccessDenied(errors, [{ path: ['updateUser'] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: ['updateUser'] }]); // User should have its original name const _users = await context.lists.User.findMany({ query: 'id name' }); @@ -65,7 +65,7 @@ describe('Access control - Imperative => declarative', () => { // Returns null and throws an error expect(data).toEqual({ deleteUser: null }); - expectAccessDenied(errors, [{ path: ['deleteUser'] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: ['deleteUser'] }]); // Bad users should still be in the database. const _users = await context.lists.User.findMany({ query: 'id name' }); @@ -120,7 +120,10 @@ describe('Access control - Imperative => declarative', () => { null, ], }); - expectAccessDenied(errors, [{ path: ['updateUsers', 1] }, { path: ['updateUsers', 3] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['updateUsers', 1] }, + { path: ['updateUsers', 3] }, + ]); // All users should still exist in the database const _users = await context.lists.User.findMany({ @@ -161,7 +164,10 @@ describe('Access control - Imperative => declarative', () => { }, }); - expectAccessDenied(errors, [{ path: ['deleteUsers', 1] }, { path: ['deleteUsers', 3] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['deleteUsers', 1] }, + { path: ['deleteUsers', 3] }, + ]); // Valid users are returned, invalid come back as null // The invalid deletes should have errors which point to the nulls in their path diff --git a/tests/api-tests/access-control/mutations-list-static.test.ts b/tests/api-tests/access-control/mutations-list-static.test.ts index 3984c0c9e6f..3c2ce0d1a0a 100644 --- a/tests/api-tests/access-control/mutations-list-static.test.ts +++ b/tests/api-tests/access-control/mutations-list-static.test.ts @@ -46,7 +46,7 @@ describe('Access control - Imperative => static', () => { // Returns null and throws an error expect(data).toEqual({ createUser: null }); - expectAccessDenied(errors, [{ path: ['createUser'] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: ['createUser'] }]); // Only the original user should exist const _users = await context.lists.User.findMany({ query: 'id name' }); @@ -70,7 +70,7 @@ describe('Access control - Imperative => static', () => { // Returns null and throws an error expect(data).toEqual({ updateUser: null }); - expectAccessDenied(errors, [{ path: ['updateUser'] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: ['updateUser'] }]); // User should have its original name const _users = await context.lists.User.findMany({ query: 'id name' }); @@ -95,7 +95,7 @@ describe('Access control - Imperative => static', () => { // Returns null and throws an error expect(data).toEqual({ deleteUser: null }); - expectAccessDenied(errors, [{ path: ['deleteUser'] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: ['deleteUser'] }]); // Bad users should still be in the database. const _users = await context.lists.User.findMany({ query: 'id name' }); @@ -133,7 +133,10 @@ describe('Access control - Imperative => static', () => { }); // The invalid updates should have errors which point to the nulls in their path - expectAccessDenied(errors, [{ path: ['createUsers', 1] }, { path: ['createUsers', 3] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['createUsers', 1] }, + { path: ['createUsers', 3] }, + ]); // The good users should exist in the database const users = await context.lists.User.findMany(); @@ -182,7 +185,10 @@ describe('Access control - Imperative => static', () => { ]); // The invalid updates should have errors which point to the nulls in their path - expectAccessDenied(errors, [{ path: ['updateUsers', 1] }, { path: ['updateUsers', 3] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['updateUsers', 1] }, + { path: ['updateUsers', 3] }, + ]); // All users should still exist in the database const _users = await context.lists.User.findMany({ @@ -232,7 +238,10 @@ describe('Access control - Imperative => static', () => { ]); // The invalid updates should have errors which point to the nulls in their path - expectAccessDenied(errors, [{ path: ['deleteUsers', 1] }, { path: ['deleteUsers', 3] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['deleteUsers', 1] }, + { path: ['deleteUsers', 3] }, + ]); const _users = await context.lists.User.findMany({ orderBy: { name: 'asc' }, diff --git a/tests/api-tests/access-control/not-authed.test.ts b/tests/api-tests/access-control/not-authed.test.ts index 2ac02923d9d..d95b7f3f4f8 100644 --- a/tests/api-tests/access-control/not-authed.test.ts +++ b/tests/api-tests/access-control/not-authed.test.ts @@ -20,7 +20,7 @@ const expectNoAccess = ( name: N ) => { expect(data?.[name]).toBe(null); - expectAccessDenied(errors, [{ path: [name] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: [name] }]); }; type IdType = any; @@ -136,7 +136,7 @@ describe(`Not authed`, () => { const query = `mutation { ${createMutationName}(data: { ${fieldName}: "bar" }) { id } }`; const { data, errors } = await context.graphql.raw({ query }); expect(data).toEqual({ [createMutationName]: null }); - expectAccessDenied(errors, [{ path: [createMutationName] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: [createMutationName] }]); }); }); }); @@ -163,7 +163,7 @@ describe(`Not authed`, () => { const query = `query { ${countName} }`; const { data, errors } = await context.graphql.raw({ query }); expect(data).toEqual({ [countName]: null }); - expectAccessDenied(errors, [{ path: [countName] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: [countName] }]); }); test(`single denied: ${JSON.stringify(access)}`, async () => { @@ -197,7 +197,9 @@ describe(`Not authed`, () => { }); const query = `query { ${singleQueryName}(where: { id: "${item.id}" }) { id ${fieldName} } }`; const { data, errors } = await context.graphql.raw({ query }); - expectAccessDenied(errors, [{ path: [singleQueryName, fieldName] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: [singleQueryName, fieldName] }, + ]); expect(data).toEqual({ [singleQueryName]: { id: item.id, [fieldName]: null } }); }); test(`field allowed - multi: ${JSON.stringify(access)}`, async () => { @@ -217,7 +219,7 @@ describe(`Not authed`, () => { }); const query = `query { ${allQueryName} { id ${fieldName} } }`; const { data, errors } = await context.graphql.raw({ query }); - expectAccessDenied(errors, [ + expectAccessDenied('dev', false, undefined, errors, [ { path: [allQueryName, 0, fieldName] }, { path: [allQueryName, 1, fieldName] }, ]); @@ -351,7 +353,7 @@ describe(`Not authed`, () => { const query = `mutation { ${updateMutationName}(where: { id: "${item.id}" }, data: { ${fieldName}: "bar" }) { id } }`; const { data, errors } = await context.graphql.raw({ query }); expect(data).toEqual({ [updateMutationName]: null }); - expectAccessDenied(errors, [{ path: [updateMutationName] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: [updateMutationName] }]); }); }); }); @@ -377,7 +379,9 @@ describe(`Not authed`, () => { const { data, errors } = await context.graphql.raw({ query }); expect(data).toEqual({ [multiDeleteMutationName]: [null] }); - expectAccessDenied(errors, [{ path: [multiDeleteMutationName, 0] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: [multiDeleteMutationName, 0] }, + ]); }); }); }); diff --git a/tests/api-tests/access-control/utils.ts b/tests/api-tests/access-control/utils.ts index 012a038f17c..f8ad58fb1db 100644 --- a/tests/api-tests/access-control/utils.ts +++ b/tests/api-tests/access-control/utils.ts @@ -38,7 +38,7 @@ const getDeclarativeListName = (access: BooleanAccess) => `${getPrefix(access)}D }), {})); } */ -const listAccessVariations: BooleanAccess[] = [ +const listAccessVariations: (BooleanAccess & { delete: boolean })[] = [ { create: false, read: false, update: false, delete: false }, { create: true, read: false, update: false, delete: false }, { create: false, read: true, update: false, delete: false }, @@ -129,9 +129,9 @@ listAccessVariations.forEach(access => { access: { create: access.create, // arbitrarily restrict the data to a single item (see data.js) - read: () => access.read && { name: 'Hello' }, - update: () => access.update && { name: 'Hello' }, - delete: () => access.delete && { name: 'Hello' }, + read: () => access.read && { name: { equals: 'Hello' } }, + update: () => access.update && { name: { equals: 'Hello' } }, + delete: () => access.delete && { name: { equals: 'Hello' } }, }, }); }); diff --git a/tests/api-tests/auth-header.test.ts b/tests/api-tests/auth-header.test.ts index c64eeda9a61..e9d027d55d3 100644 --- a/tests/api-tests/auth-header.test.ts +++ b/tests/api-tests/auth-header.test.ts @@ -83,7 +83,7 @@ describe('Auth testing', () => { } const { data, errors } = await context.graphql.raw({ query: '{ users { id } }' }); expect(data).toEqual({ users: null }); - expectAccessDenied(errors, [{ path: ['users'] }]); + expectAccessDenied('dev', false, undefined, errors, [{ path: ['users'] }]); }) ); @@ -118,8 +118,7 @@ describe('Auth testing', () => { }); describe('logged in', () => { - // eslint-disable-next-line jest/no-disabled-tests - test.skip( + test( 'Allows access with bearer token', runner(async ({ context, graphQLRequest }) => { for (const [listKey, data] of Object.entries(initialData)) { diff --git a/tests/api-tests/extend-graphql-schema/extend-graphql-schema.test.ts b/tests/api-tests/extend-graphql-schema/extend-graphql-schema.test.ts index c14587da68a..5d07a5b50a9 100644 --- a/tests/api-tests/extend-graphql-schema/extend-graphql-schema.test.ts +++ b/tests/api-tests/extend-graphql-schema/extend-graphql-schema.test.ts @@ -70,13 +70,15 @@ describe('extendGraphqlSchema', () => { runner(async ({ graphQLRequest }) => { const { body } = await graphQLRequest({ query: ` - query { - quads(x: 10) - } - `, + query { + quads(x: 10) + } + `, }); expect(body.data).toEqual({ quads: null }); - expectInternalServerError(body.errors, [{ path: ['quads'], message: 'Access denied' }]); + expectInternalServerError(body.errors, false, [ + { path: ['quads'], message: 'Access denied' }, + ]); }) ); it( diff --git a/tests/api-tests/fields/filter.test.ts b/tests/api-tests/fields/filter.test.ts index 96da9dd5ba6..21d803ed0c5 100644 --- a/tests/api-tests/fields/filter.test.ts +++ b/tests/api-tests/fields/filter.test.ts @@ -93,289 +93,357 @@ testModules ? `name ${fieldName} { ${subfieldName} }` : `name ${fieldName}`; - const match = async ( - context: KeystoneContext, - where: Record | undefined, - expected: any[], - orderBy: Record = { name: 'asc' } - ) => - expect(await context.lists[listKey].findMany({ where, orderBy, query })).toEqual( - expected.map(i => storedValues[i]) - ); + describe.each(['without negation', 'with negation'] as const)('%s', kind => { + const match = async ( + context: KeystoneContext, + where: Record | undefined, + expectedIndexes: number[] + ) => { + let expected = expectedIndexes.map(i => storedValues[i]); + if (kind === 'with negation') { + const expectedWithoutNegation = new Set(expected); + expected = storedValues.filter((v: any) => !expectedWithoutNegation.has(v)); + } + expect( + await context.lists[listKey].findMany({ + where: kind === 'with negation' ? { NOT: where } : where, + orderBy: { name: 'asc' }, + query, + }) + ).toEqual(expected); + }; - test( - `No Filter`, - withKeystone(({ context }) => match(context, undefined, [0, 1, 2, 3, 4, 5, 6])) - ); + if (kind === 'without negation') { + test( + `No Filter`, + withKeystone(({ context }) => match(context, undefined, [0, 1, 2, 3, 4, 5, 6])) + ); + // arguably this should return [] when negated + // but i assume prisma is like "this things empty, let's just ignore it" + // this is fine imo since "i want to query this api and have it definitely return no results" + // isn't really very useful + test( + `Empty Filter`, + withKeystone(({ context }) => match(context, {}, [0, 1, 2, 3, 4, 5, 6])) + ); + } - test( - `Empty Filter`, - withKeystone(({ context }) => match(context, {}, [0, 1, 2, 3, 4, 5, 6])) - ); - if (mod.supportedFilters(provider).includes('null_equality')) { - test( - 'Equals null', - withKeystone(({ context }) => match(context, { [`${fieldName}`]: null }, [5, 6])) - ); - test( - 'Not Equals null', - withKeystone(({ context }) => - match(context, { [`${fieldName}_not`]: null }, [0, 1, 2, 3, 4]) - ) - ); - } - if (mod.supportedFilters(provider).includes('equality')) { - test( - 'Equals', - withKeystone(({ context }) => - match(context, { [`${fieldName}`]: storedValues[3][fieldName] }, [3]) - ) - ); - test( - 'Not Equals', - withKeystone(({ context }) => - match( - context, - { [`${fieldName}_not`]: storedValues[3][fieldName] }, - [0, 1, 2, 4, 5, 6] + if (mod.supportedFilters(provider).includes('null_equality')) { + test( + 'Equals null', + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { equals: null } }, [5, 6]) ) - ) - ); - } - if (mod.supportedFilters(provider).includes('equality_case_insensitive')) { - test( - `Equals - Case Insensitive`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_i`]: storedValues[3][fieldName] }, [2, 3, 4]) - ) - ); - - test( - `Not Equals - Case Insensitive`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_i`]: storedValues[3][fieldName] }, [0, 1, 5, 6]) - ) - ); - } - if (mod.supportedFilters(provider).includes('string')) { - test( - `Contains`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_contains`]: 'oo' }, [3, 4]) - ) - ); - test( - `Not Contains`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_contains`]: 'oo' }, [0, 1, 2, 5, 6]) - ) - ); - test( - `Starts With`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_starts_with`]: 'foo' }, [3, 4]) - ) - ); - test( - `Not Starts With`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_starts_with`]: 'foo' }, [0, 1, 2, 5, 6]) - ) - ); - test( - `Ends With`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_ends_with`]: 'BAR' }, [2, 3]) - ) - ); - test( - `Not Ends With`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_ends_with`]: 'BAR' }, [0, 1, 4, 5, 6]) - ) - ); - } - if (mod.supportedFilters(provider).includes('string_case_insensitive')) { - test( - `Contains - Case Insensitive`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_contains_i`]: 'oo' }, [2, 3, 4]) - ) - ); - - test( - `Not Contains - Case Insensitive`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_contains_i`]: 'oo' }, [0, 1, 5, 6]) - ) - ); + ); + test( + 'Not Equals null', + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { not: { equals: null } } }, [0, 1, 2, 3, 4]) + ) + ); + } + if (mod.supportedFilters(provider).includes('equality')) { + test( + 'Equals', + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { equals: storedValues[3][fieldName] } }, [3]) + ) + ); + test( + 'Not Equals', + withKeystone(({ context }) => + match( + context, + { NOT: { [`${fieldName}`]: { equals: storedValues[3][fieldName] } } }, + [0, 1, 2, 4, 5, 6] + ) + ) + ); + } + if (mod.supportedFilters(provider).includes('equality_case_insensitive')) { + test( + `Equals - Case Insensitive`, + withKeystone(({ context }) => + match( + context, + { + [`${fieldName}`]: { equals: storedValues[3][fieldName], mode: 'insensitive' }, + }, + [2, 3, 4] + ) + ) + ); - test( - `Starts With - Case Insensitive`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_starts_with_i`]: 'foo' }, [2, 3, 4]) - ) - ); + test( + `Not Equals - Case Insensitive`, + withKeystone(({ context }) => + match( + context, + { + [`${fieldName}`]: { + mode: 'insensitive', + not: { equals: storedValues[3][fieldName] }, + }, + }, + [0, 1, 5, 6] + ) + ) + ); + } + if (mod.supportedFilters(provider).includes('string')) { + test( + `Contains`, + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { contains: 'oo' } }, [3, 4]) + ) + ); + test( + `Not Contains`, + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { not: { contains: 'oo' } } }, [0, 1, 2, 5, 6]) + ) + ); + test( + `Starts With`, + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { startsWith: 'foo' } }, [3, 4]) + ) + ); + test( + `Not Starts With`, + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { not: { startsWith: 'foo' } } }, + [0, 1, 2, 5, 6] + ) + ) + ); + test( + `Ends With`, + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { endsWith: 'BAR' } }, [2, 3]) + ) + ); + test( + `Not Ends With`, + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { not: { endsWith: 'BAR' } } }, + [0, 1, 4, 5, 6] + ) + ) + ); + } + if (mod.supportedFilters(provider).includes('string_case_insensitive')) { + test( + `Contains - Case Insensitive`, + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { mode: 'insensitive', contains: 'oo' } }, + [2, 3, 4] + ) + ) + ); - test( - `Not Starts With - Case Insensitive`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_starts_with_i`]: 'foo' }, [0, 1, 5, 6]) - ) - ); + test( + `Not Contains - Case Insensitive`, + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { mode: 'insensitive', not: { contains: 'oo' } } }, + [0, 1, 5, 6] + ) + ) + ); - test( - `Ends With - Case Insensitive`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_ends_with_i`]: 'BAR' }, [2, 3, 4]) - ) - ); + test( + `Starts With - Case Insensitive`, + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { mode: 'insensitive', startsWith: 'foo' } }, + [2, 3, 4] + ) + ) + ); - test( - `Not Ends With - Case Insensitive`, - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_ends_with_i`]: 'BAR' }, [0, 1, 5, 6]) - ) - ); - } - if (mod.supportedFilters(provider).includes('ordering')) { - test( - 'Less than', - withKeystone(({ context }) => - match(context, { [`${fieldName}_lt`]: storedValues[2][fieldName] }, [0, 1]) - ) - ); - test( - 'Less than or equal', - withKeystone(({ context }) => - match(context, { [`${fieldName}_lte`]: storedValues[2][fieldName] }, [0, 1, 2]) - ) - ); - test( - 'Greater than', - withKeystone(({ context }) => - match(context, { [`${fieldName}_gt`]: storedValues[2][fieldName] }, [3, 4]) - ) - ); - test( - 'Greater than or equal', - withKeystone(({ context }) => - match(context, { [`${fieldName}_gte`]: storedValues[2][fieldName] }, [2, 3, 4]) - ) - ); - } - if (mod.supportedFilters(provider).includes('in_empty_null')) { - test( - 'In - Empty List', - withKeystone(({ context }) => match(context, { [`${fieldName}_in`]: [] }, [])) - ); + test( + `Not Starts With - Case Insensitive`, + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { mode: 'insensitive', not: { startsWith: 'foo' } } }, + [0, 1, 5, 6] + ) + ) + ); - test( - 'Not In - Empty List', - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_in`]: [] }, [0, 1, 2, 3, 4, 5, 6]) - ) - ); + test( + `Ends With - Case Insensitive`, + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { mode: 'insensitive', endsWith: 'BAR' } }, + [2, 3, 4] + ) + ) + ); - test( - 'In - null', - withKeystone(({ context }) => match(context, { [`${fieldName}_in`]: [null] }, [5, 6])) - ); + test( + `Not Ends With - Case Insensitive`, + withKeystone(({ context }) => + match( + context, + { + [`${fieldName}`]: { mode: 'insensitive', not: { endsWith: 'BAR' } }, + }, + [0, 1, 5, 6] + ) + ) + ); + } + if (mod.supportedFilters(provider).includes('ordering')) { + test( + 'Less than', + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { lt: storedValues[2][fieldName] } }, [0, 1]) + ) + ); + test( + 'Less than or equal', + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { lte: storedValues[2][fieldName] } }, + [0, 1, 2] + ) + ) + ); + test( + 'Greater than', + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { gt: storedValues[2][fieldName] } }, [3, 4]) + ) + ); + test( + 'Greater than or equal', + withKeystone(({ context }) => + match( + context, + { [`${fieldName}`]: { gte: storedValues[2][fieldName] } }, + [2, 3, 4] + ) + ) + ); + } + if (mod.supportedFilters(provider).includes('in_empty_null')) { + test( + 'In - Empty List', + withKeystone(({ context }) => match(context, { [`${fieldName}`]: { in: [] } }, [])) + ); - test( - 'Not In - null', - withKeystone(({ context }) => - match(context, { [`${fieldName}_not_in`]: [null] }, [0, 1, 2, 3, 4]) - ) - ); - } - if (mod.supportedFilters(provider).includes('in_equal')) { - test( - 'In - values', - withKeystone(({ context }) => - match( - context, - { - [`${fieldName}_in`]: [ - storedValues[0][fieldName], - storedValues[2][fieldName], - storedValues[4][fieldName], - ], - }, - [0, 2, 4] + test( + 'Not In - Empty List', + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { notIn: [] } }, [0, 1, 2, 3, 4, 5, 6]) ) - ) - ); - test( - 'Not In - values', - withKeystone(({ context }) => - match( - context, - { - [`${fieldName}_not_in`]: [ - storedValues[0][fieldName], - storedValues[2][fieldName], - storedValues[4][fieldName], - ], - }, - [1, 3, 5, 6] + ); + } + if (mod.supportedFilters(provider).includes('in_equal')) { + test( + 'In - values', + withKeystone(({ context }) => + match( + context, + { + [`${fieldName}`]: { + in: [ + storedValues[0][fieldName], + storedValues[2][fieldName], + storedValues[4][fieldName], + ], + }, + }, + [0, 2, 4] + ) ) - ) - ); - } - if (mod.supportedFilters(provider).includes('is_set')) { - test( - 'Is Set - true', - withKeystone(({ context }) => - match(context, { [`${fieldName}_is_set`]: true }, [0, 2, 3, 4]) - ) - ); - test( - 'Is Set - false', - withKeystone(({ context }) => - match(context, { [`${fieldName}_is_set`]: false }, [1, 5, 6]) - ) - ); - } - if (mod.supportedFilters(provider).includes('unique_equality')) { - test( - 'Unique equality', - setupTestRunner({ - config: apiTestConfig({ - lists: createSchema({ - [listKey]: list({ - fields: { - field: mod.typeFunction({ isUnique: true }), + ); + test( + 'Not In - values', + withKeystone(({ context }) => + match( + context, + { + [`${fieldName}`]: { + notIn: [ + storedValues[0][fieldName], + storedValues[2][fieldName], + storedValues[4][fieldName], + ], }, + }, + [1, 3, 5, 6] + ) + ) + ); + } + if (mod.supportedFilters(provider).includes('is_set')) { + test( + 'Is Set - true', + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { isSet: true } }, [0, 2, 3, 4]) + ) + ); + test( + 'Is Set - false', + withKeystone(({ context }) => + match(context, { [`${fieldName}`]: { isSet: false } }, [1, 5, 6]) + ) + ); + } + if (mod.supportedFilters(provider).includes('unique_equality')) { + test( + 'Unique equality', + setupTestRunner({ + config: apiTestConfig({ + lists: createSchema({ + [listKey]: list({ + fields: { + field: mod.typeFunction({ isUnique: true }), + }, + }), }), }), - }), - })(async ({ context }) => { - // Populate the database before running the tests - // Note: this seeding has to be in an order defined by the array returned by `mod.initItems()` - for (const data of mod.initItems(matrixValue)) { - await context.lists[listKey].createOne({ - data: { field: data[fieldName] }, - }); - } - await Promise.all( - storedValues.map(async (val: any) => { - const promise = context.lists[listKey].findOne({ - where: { field: val[fieldName] }, - query: 'field', + })(async ({ context }) => { + // Populate the database before running the tests + // Note: this seeding has to be in an order defined by the array returned by `mod.initItems()` + for (const data of mod.initItems(matrixValue)) { + await context.lists[listKey].createOne({ + data: { field: data[fieldName] }, }); - if (val[fieldName] === null) { - expect(await promise.catch(x => x.toString())).toMatch( - 'The unique value provided in a unique where input must not be null' - ); - } else { - expect(await promise).toEqual({ field: val[fieldName] }); - } - }) - ); - }) - ); - } + } + await Promise.all( + storedValues.map(async (val: any) => { + const promise = context.lists[listKey].findOne({ + where: { field: val[fieldName] }, + query: 'field', + }); + if (val[fieldName] === null) { + expect(await promise.catch(x => x.toString())).toMatch( + 'The unique value provided in a unique where input must not be null' + ); + } else { + expect(await promise).toEqual({ field: val[fieldName] }); + } + }) + ); + }) + ); + } + }); }); } }); diff --git a/tests/api-tests/fields/types/Virtual.test.ts b/tests/api-tests/fields/types/Virtual.test.ts index 93e0093274d..1c06f1b9de6 100644 --- a/tests/api-tests/fields/types/Virtual.test.ts +++ b/tests/api-tests/fields/types/Virtual.test.ts @@ -118,10 +118,14 @@ describe('Virtual field type', () => { async resolve(rootVal, args, context) { const [personAuthors, organisationAuthors] = await Promise.all([ context.db.lists.Person.findMany({ - where: { authoredPosts_some: { id: rootVal.id.toString() } }, + where: { + authoredPosts: { some: { id: { equals: rootVal.id.toString() } } }, + }, }), context.db.lists.Organisation.findMany({ - where: { authoredPosts_some: { id: rootVal.id.toString() } }, + where: { + authoredPosts: { some: { id: { equals: rootVal.id.toString() } } }, + }, }), ]); if (personAuthors.length) { diff --git a/tests/api-tests/fields/types/document.test.ts b/tests/api-tests/fields/types/document.test.ts index c3df3699ba4..8609c0ca211 100644 --- a/tests/api-tests/fields/types/document.test.ts +++ b/tests/api-tests/fields/types/document.test.ts @@ -46,7 +46,7 @@ const runner = setupTestRunner({ }, }), }, - access: { read: { name_not: 'Charlie' } }, + access: { read: { name: { not: { equals: 'Charlie' } } } }, }), }), }), @@ -284,7 +284,7 @@ describe('Document field type', () => { }); // FIXME: The path doesn't match the null field here! expect(body.data).toEqual({ author: { badBio: null } }); - expectInternalServerError(body.errors, [ + expectInternalServerError(body.errors, true, [ { path: ['author', 'badBio', 'document'], message: 'Cannot query field "bad" on type "Author". Did you mean "bio" or "id"?', diff --git a/tests/api-tests/fields/unique.test.ts b/tests/api-tests/fields/unique.test.ts index 8b656ecf57a..b2bf558bb4c 100644 --- a/tests/api-tests/fields/unique.test.ts +++ b/tests/api-tests/fields/unique.test.ts @@ -76,6 +76,8 @@ testModules message: expect.stringMatching( /\nInvalid `prisma\.test\.create\(\)` invocation:\n(.*\n){2} Unique constraint failed on the fields: \(`testField`\)/ ), + code: 'P2002', + target: ['testField'], }, ]); }) @@ -104,6 +106,8 @@ testModules message: expect.stringMatching( /\nInvalid `prisma\.test\.create\(\)` invocation:\n(.*\n){2} Unique constraint failed on the fields: \(`testField`\)/ ), + code: 'P2002', + target: ['testField'], }, ]); }) diff --git a/tests/api-tests/hooks/hook-errors.test.ts b/tests/api-tests/hooks/hook-errors.test.ts index fd9938f5f64..bd2dad79fab 100644 --- a/tests/api-tests/hooks/hook-errors.test.ts +++ b/tests/api-tests/hooks/hook-errors.test.ts @@ -1,407 +1,574 @@ import { text } from '@keystone-next/fields'; import { createSchema, list } from '@keystone-next/keystone/schema'; -import { setupTestRunner } from '@keystone-next/testing'; +import { GraphQLRequest, setupTestRunner } from '@keystone-next/testing'; +import { KeystoneContext } from '../../../packages/types/src'; import { apiTestConfig, expectAccessDenied, expectExtensionError } from '../utils'; -const runner = setupTestRunner({ - config: apiTestConfig({ - lists: createSchema({ - User: list({ - fields: { name: text() }, - hooks: { - beforeChange: ({ resolvedData }) => { - if (resolvedData.name === 'trigger before') { - throw new Error('Simulated error: beforeChange'); - } - }, - afterChange: ({ resolvedData }) => { - if (resolvedData.name === 'trigger after') { - throw new Error('Simulated error: afterChange'); - } - }, - beforeDelete: ({ existingItem }) => { - if (existingItem.name === 'trigger before delete') { - throw new Error('Simulated error: beforeDelete'); - } - }, - afterDelete: ({ existingItem }) => { - if (existingItem.name === 'trigger after delete') { - throw new Error('Simulated error: afterDelete'); - } - }, - }, - }), - Post: list({ - fields: { - title: text({ - hooks: { - beforeChange: ({ resolvedData }) => { - if (resolvedData.title === 'trigger before') { - throw new Error('Simulated error: title: beforeChange'); - } - }, - afterChange: ({ resolvedData }) => { - if (resolvedData.title === 'trigger after') { - throw new Error('Simulated error: title: afterChange'); - } - }, - beforeDelete: ({ existingItem }) => { - if (existingItem.title === 'trigger before delete') { - throw new Error('Simulated error: title: beforeDelete'); - } - }, - afterDelete: ({ existingItem }) => { - if (existingItem.title === 'trigger after delete') { - throw new Error('Simulated error: title: afterDelete'); - } - }, +const runner = (debug: boolean | undefined) => + setupTestRunner({ + config: apiTestConfig({ + lists: createSchema({ + User: list({ + fields: { name: text() }, + hooks: { + beforeChange: ({ resolvedData }) => { + if (resolvedData.name === 'trigger before') { + throw new Error('Simulated error: beforeChange'); + } }, - }), - content: text({ - hooks: { - beforeChange: ({ resolvedData }) => { - if (resolvedData.content === 'trigger before') { - throw new Error('Simulated error: content: beforeChange'); - } - }, - afterChange: ({ resolvedData }) => { - if (resolvedData.content === 'trigger after') { - throw new Error('Simulated error: content: afterChange'); - } - }, - beforeDelete: ({ existingItem }) => { - if (existingItem.content === 'trigger before delete') { - throw new Error('Simulated error: content: beforeDelete'); - } + afterChange: ({ resolvedData }) => { + if (resolvedData.name === 'trigger after') { + throw new Error('Simulated error: afterChange'); + } + }, + beforeDelete: ({ existingItem }) => { + if (existingItem.name === 'trigger before delete') { + throw new Error('Simulated error: beforeDelete'); + } + }, + afterDelete: ({ existingItem }) => { + if (existingItem.name === 'trigger after delete') { + throw new Error('Simulated error: afterDelete'); + } + }, + }, + }), + Post: list({ + fields: { + title: text({ + hooks: { + beforeChange: ({ resolvedData }) => { + if (resolvedData.title === 'trigger before') { + throw new Error('Simulated error: title: beforeChange'); + } + }, + afterChange: ({ resolvedData }) => { + if (resolvedData.title === 'trigger after') { + throw new Error('Simulated error: title: afterChange'); + } + }, + beforeDelete: ({ existingItem }) => { + if (existingItem.title === 'trigger before delete') { + throw new Error('Simulated error: title: beforeDelete'); + } + }, + afterDelete: ({ existingItem }) => { + if (existingItem.title === 'trigger after delete') { + throw new Error('Simulated error: title: afterDelete'); + } + }, }, - afterDelete: ({ existingItem }) => { - if (existingItem.content === 'trigger after delete') { - throw new Error('Simulated error: content: afterDelete'); - } + }), + content: text({ + hooks: { + beforeChange: ({ resolvedData }) => { + if (resolvedData.content === 'trigger before') { + throw new Error('Simulated error: content: beforeChange'); + } + }, + afterChange: ({ resolvedData }) => { + if (resolvedData.content === 'trigger after') { + throw new Error('Simulated error: content: afterChange'); + } + }, + beforeDelete: ({ existingItem }) => { + if (existingItem.content === 'trigger before delete') { + throw new Error('Simulated error: content: beforeDelete'); + } + }, + afterDelete: ({ existingItem }) => { + if (existingItem.content === 'trigger after delete') { + throw new Error('Simulated error: content: afterDelete'); + } + }, }, - }, - }), - }, + }), + }, + }), }), + graphql: { debug }, }), - }), -}); + }); -['before', 'after'].map(phase => { - describe(`List Hooks: ${phase}Change/${phase}Delete()`, () => { - test( - 'createOne', - runner(async ({ context }) => { - // Valid name should pass - await context.lists.User.createOne({ data: { name: 'good' } }); +[true, false].map(useHttp => { + const runQuery = async ( + context: KeystoneContext, + graphQLRequest: GraphQLRequest, + query: { query: string; variables?: Record } + ) => { + if (useHttp) { + const { body } = await graphQLRequest(query); + return body; + } else { + return await context.graphql.raw(query); + } + }; - // Trigger an error - const { data, errors } = await context.graphql.raw({ - query: `mutation ($data: UserCreateInput!) { createUser(data: $data) { id } }`, - variables: { data: { name: `trigger ${phase}` } }, + [true, false, undefined].map(debug => { + (['dev', 'production'] as const).map(mode => + describe(`NODE_ENV=${mode}, debug=${debug} useHttp=${useHttp}`, () => { + beforeAll(() => { + // @ts-ignore + process.env.NODE_ENV = mode; + }); + afterAll(() => { + // @ts-ignore + process.env.NODE_ENV = 'test'; }); - // Returns null and throws an error - expect(data).toEqual({ createUser: null }); - expectExtensionError(errors, `${phase}Change`, [ - { path: ['createUser'], messages: [`User: Simulated error: ${phase}Change`] }, - ]); + ['before', 'after'].map(phase => { + describe(`List Hooks: ${phase}Change/${phase}Delete()`, () => { + test( + 'createOne', + runner(debug)(async ({ context, graphQLRequest }) => { + // Valid name should pass + await context.lists.User.createOne({ data: { name: 'good' } }); - // Only the original user should exist for 'before', both exist for 'after' - const _users = await context.lists.User.findMany({ query: 'id name' }); - expect(_users.map(({ name }) => name)).toEqual( - phase === 'before' ? ['good'] : ['good', 'trigger after'] - ); - }) - ); + // Trigger an error + const { data, errors } = await runQuery(context, graphQLRequest, { + query: `mutation ($data: UserCreateInput!) { createUser(data: $data) { id } }`, + variables: { data: { name: `trigger ${phase}` } }, + }); - test( - 'updateOne', - runner(async ({ context }) => { - // Valid name should pass - const user = await context.lists.User.createOne({ data: { name: 'good' } }); - await context.lists.User.updateOne({ where: { id: user.id }, data: { name: 'better' } }); + // Returns null and throws an error + expect(data).toEqual({ createUser: null }); + const message = `Simulated error: ${phase}Change`; + expectExtensionError(mode, useHttp, debug, errors, `${phase}Change`, [ + { + path: ['createUser'], + messages: [`User: Simulated error: ${phase}Change`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Change .${__filename}`) + ), + }, + ], + }, + ]); - // Invalid name - const { data, errors } = await context.graphql.raw({ - query: `mutation ($id: ID! $data: UserUpdateInput!) { updateUser(where: { id: $id }, data: $data) { id } }`, - variables: { id: user.id, data: { name: `trigger ${phase}` } }, - }); + // Only the original user should exist for 'before', both exist for 'after' + const _users = await context.lists.User.findMany({ query: 'id name' }); + expect(_users.map(({ name }) => name)).toEqual( + phase === 'before' ? ['good'] : ['good', 'trigger after'] + ); + }) + ); - // Returns null and throws an error - expect(data).toEqual({ updateUser: null }); - expectExtensionError(errors, `${phase}Change`, [ - { path: ['updateUser'], messages: [`User: Simulated error: ${phase}Change`] }, - ]); + test( + 'updateOne', + runner(debug)(async ({ context, graphQLRequest }) => { + // Valid name should pass + const user = await context.lists.User.createOne({ data: { name: 'good' } }); + await context.lists.User.updateOne({ + where: { id: user.id }, + data: { name: 'better' }, + }); - // User should have its original name for 'before', and the new name for 'after'. - const _users = await context.lists.User.findMany({ query: 'id name' }); - expect(_users.map(({ name }) => name)).toEqual( - phase === 'before' ? ['better'] : ['trigger after'] - ); - }) - ); + // Invalid name + const { data, errors } = await runQuery(context, graphQLRequest, { + query: `mutation ($id: ID! $data: UserUpdateInput!) { updateUser(where: { id: $id }, data: $data) { id } }`, + variables: { id: user.id, data: { name: `trigger ${phase}` } }, + }); - test( - 'deleteOne', - runner(async ({ context }) => { - // Valid names should pass - const user1 = await context.lists.User.createOne({ data: { name: 'good' } }); - const user2 = await context.lists.User.createOne({ - data: { name: `trigger ${phase} delete` }, - }); - await context.lists.User.deleteOne({ where: { id: user1.id } }); + // Returns null and throws an error + expect(data).toEqual({ updateUser: null }); + const message = `Simulated error: ${phase}Change`; + expectExtensionError(mode, useHttp, debug, errors, `${phase}Change`, [ + { + path: ['updateUser'], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Change .${__filename}`) + ), + }, + ], + }, + ]); - // Invalid name - const { data, errors } = await context.graphql.raw({ - query: `mutation ($id: ID!) { deleteUser(where: { id: $id }) { id } }`, - variables: { id: user2.id }, - }); + // User should have its original name for 'before', and the new name for 'after'. + const _users = await context.lists.User.findMany({ query: 'id name' }); + expect(_users.map(({ name }) => name)).toEqual( + phase === 'before' ? ['better'] : ['trigger after'] + ); + }) + ); - // Returns null and throws an error - expect(data).toEqual({ deleteUser: null }); - expectExtensionError(errors, `${phase}Delete`, [ - { path: ['deleteUser'], messages: [`User: Simulated error: ${phase}Delete`] }, - ]); + test( + 'deleteOne', + runner(debug)(async ({ context, graphQLRequest }) => { + // Valid names should pass + const user1 = await context.lists.User.createOne({ data: { name: 'good' } }); + const user2 = await context.lists.User.createOne({ + data: { name: `trigger ${phase} delete` }, + }); + await context.lists.User.deleteOne({ where: { id: user1.id } }); - // Bad users should still be in the database for 'before', deleted for 'after'. - const _users = await context.lists.User.findMany({ query: 'id name' }); - expect(_users.map(({ name }) => name)).toEqual( - phase === 'before' ? ['trigger before delete'] : [] - ); - }) - ); + // Invalid name + const { data, errors } = await runQuery(context, graphQLRequest, { + query: `mutation ($id: ID!) { deleteUser(where: { id: $id }) { id } }`, + variables: { id: user2.id }, + }); - test( - 'createMany', - runner(async ({ context }) => { - // Mix of good and bad names - const { data, errors } = await context.graphql.raw({ - query: `mutation ($data: [UserCreateInput!]!) { createUsers(data: $data) { id name } }`, - variables: { - data: [ - { name: 'good 1' }, - { name: `trigger ${phase}` }, - { name: 'good 2' }, - { name: `trigger ${phase}` }, - { name: 'good 3' }, - ], - }, - }); + // Returns null and throws an error + expect(data).toEqual({ deleteUser: null }); + const message = `Simulated error: ${phase}Delete`; + expectExtensionError(mode, useHttp, debug, errors, `${phase}Delete`, [ + { + path: ['deleteUser'], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Delete .${__filename}`) + ), + }, + ], + }, + ]); - // Valid users are returned, invalid come back as null - expect(data).toEqual({ - createUsers: [ - { id: expect.any(String), name: 'good 1' }, - null, - { id: expect.any(String), name: 'good 2' }, - null, - { id: expect.any(String), name: 'good 3' }, - ], - }); - // The invalid creates should have errors which point to the nulls in their path - expectExtensionError(errors, `${phase}Change`, [ - { path: ['createUsers', 1], messages: [`User: Simulated error: ${phase}Change`] }, - { path: ['createUsers', 3], messages: [`User: Simulated error: ${phase}Change`] }, - ]); + // Bad users should still be in the database for 'before', deleted for 'after'. + const _users = await context.lists.User.findMany({ query: 'id name' }); + expect(_users.map(({ name }) => name)).toEqual( + phase === 'before' ? ['trigger before delete'] : [] + ); + }) + ); - // Three users should exist in the database for 'before,' five for 'after'. - const users = await context.lists.User.findMany({ - orderBy: { name: 'asc' }, - query: 'id name', - }); - expect(users.map(({ name }) => name)).toEqual( - phase === 'before' - ? ['good 1', 'good 2', 'good 3'] - : ['good 1', 'good 2', 'good 3', 'trigger after', 'trigger after'] - ); - }) - ); + test( + 'createMany', + runner(debug)(async ({ context, graphQLRequest }) => { + // Mix of good and bad names + const { data, errors } = await runQuery(context, graphQLRequest, { + query: `mutation ($data: [UserCreateInput!]!) { createUsers(data: $data) { id name } }`, + variables: { + data: [ + { name: 'good 1' }, + { name: `trigger ${phase}` }, + { name: 'good 2' }, + { name: `trigger ${phase}` }, + { name: 'good 3' }, + ], + }, + }); - test( - 'updateMany', - runner(async ({ context }) => { - // Start with some users - const users = await context.lists.User.createMany({ - data: [ - { name: 'good 1' }, - { name: 'good 2' }, - { name: 'good 3' }, - { name: 'good 4' }, - { name: 'good 5' }, - ], - query: 'id name', - }); + // Valid users are returned, invalid come back as null + expect(data).toEqual({ + createUsers: [ + { id: expect.any(String), name: 'good 1' }, + null, + { id: expect.any(String), name: 'good 2' }, + null, + { id: expect.any(String), name: 'good 3' }, + ], + }); + // The invalid creates should have errors which point to the nulls in their path + const message = `Simulated error: ${phase}Change`; + expectExtensionError(mode, useHttp, debug, errors, `${phase}Change`, [ + { + path: ['createUsers', 1], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Change .${__filename}`) + ), + }, + ], + }, + { + path: ['createUsers', 3], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Change .${__filename}`) + ), + }, + ], + }, + ]); - // Mix of good and bad names - const { data, errors } = await context.graphql.raw({ - query: `mutation ($data: [UserUpdateArgs!]!) { updateUsers(data: $data) { id name } }`, - variables: { - data: [ - { where: { id: users[0].id }, data: { name: 'still good 1' } }, - { where: { id: users[1].id }, data: { name: `trigger ${phase}` } }, - { where: { id: users[2].id }, data: { name: 'still good 3' } }, - { where: { id: users[3].id }, data: { name: `trigger ${phase}` } }, - ], - }, - }); + // Three users should exist in the database for 'before,' five for 'after'. + const users = await context.lists.User.findMany({ + orderBy: { name: 'asc' }, + query: 'id name', + }); + expect(users.map(({ name }) => name)).toEqual( + phase === 'before' + ? ['good 1', 'good 2', 'good 3'] + : ['good 1', 'good 2', 'good 3', 'trigger after', 'trigger after'] + ); + }) + ); - // Valid users are returned, invalid come back as null - expect(data).toEqual({ - updateUsers: [ - { id: users[0].id, name: 'still good 1' }, - null, - { id: users[2].id, name: 'still good 3' }, - null, - ], - }); - // The invalid updates should have errors which point to the nulls in their path - expectExtensionError(errors, `${phase}Change`, [ - { path: ['updateUsers', 1], messages: [`User: Simulated error: ${phase}Change`] }, - { path: ['updateUsers', 3], messages: [`User: Simulated error: ${phase}Change`] }, - ]); + test( + 'updateMany', + runner(debug)(async ({ context, graphQLRequest }) => { + // Start with some users + const users = await context.lists.User.createMany({ + data: [ + { name: 'good 1' }, + { name: 'good 2' }, + { name: 'good 3' }, + { name: 'good 4' }, + { name: 'good 5' }, + ], + query: 'id name', + }); - // All users should still exist in the database, un-changed for `before`, changed for `after`. - const _users = await context.lists.User.findMany({ - orderBy: { name: 'asc' }, - query: 'id name', - }); - expect(_users.map(({ name }) => name)).toEqual( - phase === 'before' - ? ['good 2', 'good 4', 'good 5', 'still good 1', 'still good 3'] - : ['good 5', 'still good 1', 'still good 3', 'trigger after', 'trigger after'] - ); - }) - ); + // Mix of good and bad names + const { data, errors } = await runQuery(context, graphQLRequest, { + query: `mutation ($data: [UserUpdateArgs!]!) { updateUsers(data: $data) { id name } }`, + variables: { + data: [ + { where: { id: users[0].id }, data: { name: 'still good 1' } }, + { where: { id: users[1].id }, data: { name: `trigger ${phase}` } }, + { where: { id: users[2].id }, data: { name: 'still good 3' } }, + { where: { id: users[3].id }, data: { name: `trigger ${phase}` } }, + ], + }, + }); - test( - 'deleteMany', - runner(async ({ context }) => { - // Start with some users - const users = await context.lists.User.createMany({ - data: [ - { name: 'good 1' }, - { name: `trigger ${phase} delete` }, - { name: 'good 3' }, - { name: `trigger ${phase} delete` }, - { name: 'good 5' }, - ], - query: 'id name', - }); + // Valid users are returned, invalid come back as null + expect(data).toEqual({ + updateUsers: [ + { id: users[0].id, name: 'still good 1' }, + null, + { id: users[2].id, name: 'still good 3' }, + null, + ], + }); + // The invalid updates should have errors which point to the nulls in their path + const message = `Simulated error: ${phase}Change`; + expectExtensionError(mode, useHttp, debug, errors, `${phase}Change`, [ + { + path: ['updateUsers', 1], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Change .${__filename}`) + ), + }, + ], + }, + { + path: ['updateUsers', 3], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Change .${__filename}`) + ), + }, + ], + }, + ]); - // Mix of good and bad names - const { data, errors } = await context.graphql.raw({ - query: `mutation ($where: [UserWhereUniqueInput!]!) { deleteUsers(where: $where) { id name } }`, - variables: { - where: [users[0].id, users[1].id, users[2].id, users[3].id].map(id => ({ id })), - }, - }); + // All users should still exist in the database, un-changed for `before`, changed for `after`. + const _users = await context.lists.User.findMany({ + orderBy: { name: 'asc' }, + query: 'id name', + }); + expect(_users.map(({ name }) => name)).toEqual( + phase === 'before' + ? ['good 2', 'good 4', 'good 5', 'still good 1', 'still good 3'] + : ['good 5', 'still good 1', 'still good 3', 'trigger after', 'trigger after'] + ); + }) + ); - // Valid users are returned, invalid come back as null - expect(data).toEqual({ - deleteUsers: [ - { id: users[0].id, name: 'good 1' }, - null, - { id: users[2].id, name: 'good 3' }, - null, - ], - }); - // The invalid deletes should have errors which point to the nulls in their path - expectExtensionError(errors, `${phase}Delete`, [ - { path: ['deleteUsers', 1], messages: [`User: Simulated error: ${phase}Delete`] }, - { path: ['deleteUsers', 3], messages: [`User: Simulated error: ${phase}Delete`] }, - ]); + test( + 'deleteMany', + runner(debug)(async ({ context, graphQLRequest }) => { + // Start with some users + const users = await context.lists.User.createMany({ + data: [ + { name: 'good 1' }, + { name: `trigger ${phase} delete` }, + { name: 'good 3' }, + { name: `trigger ${phase} delete` }, + { name: 'good 5' }, + ], + query: 'id name', + }); - // Three users should still exist in the database for `before`, only 1 for `after`. - const _users = await context.lists.User.findMany({ - orderBy: { name: 'asc' }, - query: 'id name', - }); - expect(_users.map(({ name }) => name)).toEqual( - phase === 'before' - ? ['good 5', 'trigger before delete', 'trigger before delete'] - : ['good 5'] - ); - }) - ); - }); -}); + // Mix of good and bad names + const { data, errors } = await runQuery(context, graphQLRequest, { + query: `mutation ($where: [UserWhereUniqueInput!]!) { deleteUsers(where: $where) { id name } }`, + variables: { + where: [users[0].id, users[1].id, users[2].id, users[3].id].map(id => ({ id })), + }, + }); -['before', 'after'].map(phase => { - describe(`Field Hooks: ${phase}Change/${phase}Delete()`, () => { - test( - 'update', - runner(async ({ context }) => { - const post = await context.lists.Post.createOne({ - data: { title: 'original title', content: 'original content' }, - }); + // Valid users are returned, invalid come back as null + expect(data).toEqual({ + deleteUsers: [ + { id: users[0].id, name: 'good 1' }, + null, + { id: users[2].id, name: 'good 3' }, + null, + ], + }); + // The invalid deletes should have errors which point to the nulls in their path + const message = `Simulated error: ${phase}Delete`; + expectExtensionError(mode, useHttp, debug, errors, `${phase}Delete`, [ + { + path: ['deleteUsers', 1], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Delete .${__filename}`) + ), + }, + ], + }, + { + path: ['deleteUsers', 3], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*${phase}Delete .${__filename}`) + ), + }, + ], + }, + ]); - const { data, errors } = await context.graphql.raw({ - query: `mutation ($id: ID! $data: PostUpdateInput!) { updatePost(where: { id: $id }, data: $data) { id } }`, - variables: { - id: post.id, - data: { title: `trigger ${phase}`, content: `trigger ${phase}` }, - }, + // Three users should still exist in the database for `before`, only 1 for `after`. + const _users = await context.lists.User.findMany({ + orderBy: { name: 'asc' }, + query: 'id name', + }); + expect(_users.map(({ name }) => name)).toEqual( + phase === 'before' + ? ['good 5', 'trigger before delete', 'trigger before delete'] + : ['good 5'] + ); + }) + ); + }); }); - expectExtensionError(errors, `${phase}Change`, [ - { - path: ['updatePost'], - messages: [ - `Post.title: Simulated error: title: ${phase}Change`, - `Post.content: Simulated error: content: ${phase}Change`, - ], - }, - ]); - expect(data).toEqual({ updatePost: null }); - // Post should have its original data for 'before', and the new data for 'after'. - const _post = await context.lists.Post.findOne({ - where: { id: post.id }, - query: 'title content', - }); - expect(_post).toEqual( - phase === 'before' - ? { title: 'original title', content: 'original content' } - : { title: 'trigger after', content: 'trigger after' } - ); - }) - ); + ['before', 'after'].map(phase => { + describe(`Field Hooks: ${phase}Change/${phase}Delete()`, () => { + test( + 'update', + runner(debug)(async ({ context, graphQLRequest }) => { + const post = await context.lists.Post.createOne({ + data: { title: 'original title', content: 'original content' }, + }); - test( - 'delete', - runner(async ({ context }) => { - const post = await context.lists.Post.createOne({ - data: { title: `trigger ${phase} delete`, content: `trigger ${phase} delete` }, - }); - const { data, errors } = await context.graphql.raw({ - query: `mutation ($id: ID!) { deletePost(where: { id: $id }) { id } }`, - variables: { id: post.id }, - }); - expectExtensionError(errors, `${phase}Delete`, [ - { - path: ['deletePost'], - messages: [ - `Post.title: Simulated error: title: ${phase}Delete`, - `Post.content: Simulated error: content: ${phase}Delete`, - ], - }, - ]); - expect(data).toEqual({ deletePost: null }); + const { data, errors } = await runQuery(context, graphQLRequest, { + query: `mutation ($id: ID! $data: PostUpdateInput!) { updatePost(where: { id: $id }, data: $data) { id } }`, + variables: { + id: post.id, + data: { title: `trigger ${phase}`, content: `trigger ${phase}` }, + }, + }); + const message1 = `Simulated error: title: ${phase}Change`; + const message2 = `Simulated error: content: ${phase}Change`; + expectExtensionError(mode, useHttp, debug, errors, `${phase}Change`, [ + { + path: ['updatePost'], + messages: [`Post.title: ${message1}`, `Post.content: ${message2}`], + debug: [ + { + message: message1, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message1}\n[^\n]*${phase}Change .${__filename}`) + ), + }, + { + message: message2, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message2}\n[^\n]*${phase}Change .${__filename}`) + ), + }, + ], + }, + ]); + expect(data).toEqual({ updatePost: null }); - // Post should have its original data for 'before', and not exist for 'after'. - const result = await context.graphql.raw({ - query: `query ($id: ID!) { post(where: { id: $id }) { title content} }`, - variables: { id: post.id }, - }); - if (phase === 'before') { - expect(result.errors).toBe(undefined); - expect(result.data).toEqual({ - post: { title: 'trigger before delete', content: 'trigger before delete' }, + // Post should have its original data for 'before', and the new data for 'after'. + const _post = await context.lists.Post.findOne({ + where: { id: post.id }, + query: 'title content', + }); + expect(_post).toEqual( + phase === 'before' + ? { title: 'original title', content: 'original content' } + : { title: 'trigger after', content: 'trigger after' } + ); + }) + ); + + test( + `delete`, + runner(debug)(async ({ context, graphQLRequest }) => { + const post = await context.lists.Post.createOne({ + data: { title: `trigger ${phase} delete`, content: `trigger ${phase} delete` }, + }); + const { data, errors } = await runQuery(context, graphQLRequest, { + query: `mutation ($id: ID!) { deletePost(where: { id: $id }) { id } }`, + variables: { id: post.id }, + }); + const message1 = `Simulated error: title: ${phase}Delete`; + const message2 = `Simulated error: content: ${phase}Delete`; + expectExtensionError(mode, useHttp, debug, errors, `${phase}Delete`, [ + { + path: ['deletePost'], + messages: [`Post.title: ${message1}`, `Post.content: ${message2}`], + debug: [ + { + message: message1, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message1}\n[^\n]*${phase}Delete .${__filename}`) + ), + }, + { + message: message2, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message2}\n[^\n]*${phase}Delete .${__filename}`) + ), + }, + ], + }, + ]); + expect(data).toEqual({ deletePost: null }); + + // Post should have its original data for 'before', and not exist for 'after'. + const result = await runQuery(context, graphQLRequest, { + query: `query ($id: ID!) { post(where: { id: $id }) { title content} }`, + variables: { id: post.id }, + }); + if (phase === 'before') { + expect(result.errors).toBe(undefined); + expect(result.data).toEqual({ + post: { title: 'trigger before delete', content: 'trigger before delete' }, + }); + } else { + expectAccessDenied(mode, useHttp, debug, result.errors, [{ path: ['post'] }]); + expect(result.data).toEqual({ post: null }); + } + }) + ); }); - } else { - expectAccessDenied(result.errors, [{ path: ['post'] }]); - expect(result.data).toEqual({ post: null }); - } + }); }) ); }); diff --git a/tests/api-tests/hooks/list-hooks.test.ts b/tests/api-tests/hooks/list-hooks.test.ts index e862804b491..72ab482736c 100644 --- a/tests/api-tests/hooks/list-hooks.test.ts +++ b/tests/api-tests/hooks/list-hooks.test.ts @@ -1,7 +1,7 @@ import { text } from '@keystone-next/fields'; import { createSchema, list } from '@keystone-next/keystone/schema'; import { setupTestRunner } from '@keystone-next/testing'; -import { apiTestConfig } from '../utils'; +import { apiTestConfig, expectExtensionError } from '../utils'; const runner = setupTestRunner({ config: apiTestConfig({ @@ -11,6 +11,10 @@ const runner = setupTestRunner({ name: text({ hooks: { resolveInput: ({ resolvedData }) => { + if (resolvedData.name === 'trigger field error') { + throw new Error('Field error triggered'); + } + return `${resolvedData.name}-field`; }, }, @@ -18,6 +22,9 @@ const runner = setupTestRunner({ }, hooks: { resolveInput: ({ resolvedData }) => { + if (resolvedData.name === 'trigger list error-field') { + throw new Error('List error triggered'); + } return { name: `${resolvedData.name}-list`, }; @@ -29,18 +36,70 @@ const runner = setupTestRunner({ }); describe('List Hooks: #resolveInput()', () => { - it( + test( 'resolves fields first, then passes them to the list', runner(async ({ context }) => { - const user = await context.lists.User.createOne({ - data: { name: 'jess' }, - query: 'name', - }); - + const user = await context.lists.User.createOne({ data: { name: 'jess' }, query: 'name' }); // Field should be executed first, appending `-field`, then the list // should be executed which appends `-list`, and finally that total // result should be stored. expect(user.name).toBe('jess-field-list'); }) ); + + test( + 'List error', + runner(async ({ context }) => { + // Trigger an error + const { data, errors } = await context.graphql.raw({ + query: `mutation ($data: UserCreateInput!) { createUser(data: $data) { id } }`, + variables: { data: { name: `trigger list error` } }, + }); + // Returns null and throws an error + expect(data).toEqual({ createUser: null }); + const message = `List error triggered`; + expectExtensionError('dev', false, undefined, errors, `resolveInput`, [ + { + path: ['createUser'], + messages: [`User: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*resolveInput .${__filename}`) + ), + }, + ], + }, + ]); + }) + ); + + test( + 'Field error', + runner(async ({ context }) => { + // Trigger an error + const { data, errors } = await context.graphql.raw({ + query: `mutation ($data: UserCreateInput!) { createUser(data: $data) { id } }`, + variables: { data: { name: `trigger field error` } }, + }); + // Returns null and throws an error + expect(data).toEqual({ createUser: null }); + const message = `Field error triggered`; + expectExtensionError('dev', false, undefined, errors, `resolveInput`, [ + { + path: ['createUser'], + messages: [`User.name: ${message}`], + debug: [ + { + message, + stacktrace: expect.stringMatching( + new RegExp(`Error: ${message}\n[^\n]*resolveInput .${__filename}`) + ), + }, + ], + }, + ]); + }) + ); }); diff --git a/tests/api-tests/id-field.test.ts b/tests/api-tests/id-field.test.ts index bc5dc63b5ca..095be4f6000 100644 --- a/tests/api-tests/id-field.test.ts +++ b/tests/api-tests/id-field.test.ts @@ -35,7 +35,7 @@ describe.each(['autoincrement', 'cuid', 'uuid'] as const)('%s', kind => { 'Filtering an item with an invalid id throws an error', runner(async ({ graphQLRequest }) => { const { body } = await graphQLRequest({ - query: `{ users(where: { id: "adskjnfasdfkjekfj"}) { id } }`, + query: `{ users(where: { id: { equals: "adskjnfasdfkjekfj" } }) { id } }`, }); expect(body.data).toEqual({ users: null }); const s = kind === 'autoincrement' ? 'an integer' : `a ${kind}`; diff --git a/tests/api-tests/package.json b/tests/api-tests/package.json index f8d1aa978d6..d14c3f9ac32 100644 --- a/tests/api-tests/package.json +++ b/tests/api-tests/package.json @@ -2,7 +2,7 @@ "name": "@keystone-next/api-tests-legacy", "description": "A set of tests for running against the KeystoneJS API.", "private": true, - "version": "11.0.6", + "version": "11.1.0", "author": "The KeystoneJS Development Team", "license": "MIT", "engines": { @@ -22,14 +22,14 @@ "graphql": "^15.5.1", "memoize-one": "^5.2.1", "superagent": "^6.1.0", - "supertest": "^6.1.4", + "supertest": "^6.1.5", "testcheck": "^1.0.0-rc.2", "uuid": "^8.3.2" }, "dependencies": { - "@keystone-next/testing": "^1.1.0", - "@keystone-next/types": "^23.0.0", - "@keystone-next/utils": "^1.0.3", + "@keystone-next/testing": "^1.1.1", + "@keystone-next/types": "^24.0.0", + "@keystone-next/utils": "^1.0.4", "apollo-cache-control": "^0.14.0", "express": "^4.17.1" } diff --git a/tests/api-tests/queries/cache-hints.test.ts b/tests/api-tests/queries/cache-hints.test.ts index acb37a5030d..f2146dcfcfc 100644 --- a/tests/api-tests/queries/cache-hints.test.ts +++ b/tests/api-tests/queries/cache-hints.test.ts @@ -177,7 +177,7 @@ describe('cache hints', () => { const { body, headers } = await graphQLRequest({ query: ` query { - users(where: { name: "nope" }) { + users(where: { name: { equals: "nope" } }) { name } } @@ -270,7 +270,7 @@ describe('cache hints', () => { query: ` query { posts { - author(where: { name: "nope" }) { + author(where: { name: { equals: "nope" } }) { name } } diff --git a/tests/api-tests/queries/filters.test.ts b/tests/api-tests/queries/filters.test.ts index 3b9f05c9a20..c3a731f665b 100644 --- a/tests/api-tests/queries/filters.test.ts +++ b/tests/api-tests/queries/filters.test.ts @@ -23,14 +23,16 @@ describe('filtering on field name', () => { test( 'filter works when there is no dash in field name', runner(async ({ context }) => { - const users = await context.lists.User.findMany({ where: { noDash: 'aValue' } }); + const users = await context.lists.User.findMany({ where: { noDash: { equals: 'aValue' } } }); expect(users).toEqual([]); }) ); test( 'filter works when there is one dash in field name', runner(async ({ context }) => { - const users = await context.lists.User.findMany({ where: { single_dash: 'aValue' } }); + const users = await context.lists.User.findMany({ + where: { single_dash: { equals: 'aValue' } }, + }); expect(users).toEqual([]); }) ); @@ -38,7 +40,7 @@ describe('filtering on field name', () => { 'filter works when there are multiple dashes in field name', runner(async ({ context }) => { const users = await context.lists.User.findMany({ - where: { many_many_many_dashes: 'aValue' }, + where: { many_many_many_dashes: { equals: 'aValue' } }, }); expect(users).toEqual([]); }) @@ -46,7 +48,9 @@ describe('filtering on field name', () => { test( 'filter works when there are multiple dashes in a row in a field name', runner(async ({ context }) => { - const users = await context.lists.User.findMany({ where: { multi____dash: 'aValue' } }); + const users = await context.lists.User.findMany({ + where: { multi____dash: { equals: 'aValue' } }, + }); expect(users).toEqual([]); }) ); @@ -54,7 +58,7 @@ describe('filtering on field name', () => { 'filter works when there is one dash in field name as part of a relationship', runner(async ({ context }) => { const secondaries = await context.lists.SecondaryList.findMany({ - where: { someUser_is_null: false }, + where: { NOT: { someUser: null } }, }); expect(secondaries).toEqual([]); }) diff --git a/tests/api-tests/queries/limits.test.ts b/tests/api-tests/queries/limits.test.ts index c4a9e343d46..dd0ee7e96b8 100644 --- a/tests/api-tests/queries/limits.test.ts +++ b/tests/api-tests/queries/limits.test.ts @@ -54,7 +54,7 @@ describe('maxResults Limit', () => { query: ` query { users( - where: { name_contains: "J" }, + where: { name: { contains: "J" } }, orderBy: { name: asc }, ) { name @@ -71,7 +71,7 @@ describe('maxResults Limit', () => { query: ` query { users( - where: { name: "Nope" } + where: { name: { equals: "Nope" } } ) { name } @@ -90,11 +90,11 @@ describe('maxResults Limit', () => { expect(data).toHaveProperty('usersCount'); expect(data.usersCount).toBe(users.length); - // This query is only okay because of the "first" parameter + // This query is only okay because of the "take" parameter data = await context.graphql.run({ query: ` query { - users(first: 1) { + users(take: 1) { name } } @@ -118,13 +118,13 @@ describe('maxResults Limit', () => { expectLimitsExceededError(errors, [{ path: ['users'] }]); - // The query results don't break the limits, but the "first" parameter does + // The query results don't break the limits, but the "take" parameter does ({ errors } = await context.graphql.raw({ query: ` query { users( - where: { name: "Nope" }, - first: 100000 + where: { name: { equals: "Nope" } }, + take: 100000 ) { name } @@ -165,7 +165,7 @@ describe('maxResults Limit', () => { context.totalResults = 0; // A basic query that should work let posts = await context.lists.Post.findMany({ - where: { title: 'One author' }, + where: { title: { equals: 'One author' } }, query: 'title author { name }', }); @@ -176,7 +176,7 @@ describe('maxResults Limit', () => { // Each subquery is within the limit (even though the total isn't) posts = await context.lists.Post.findMany({ where: { - OR: [{ title: 'One author' }, { title: 'Two authors' }], + OR: [{ title: { equals: 'One author' } }, { title: { equals: 'Two authors' } }], }, orderBy: { title: 'asc' }, query: 'title author(orderBy: { name: asc }) { name }', @@ -194,7 +194,7 @@ describe('maxResults Limit', () => { query: ` query { posts( - where: { title: "Three authors" }, + where: { title: { equals: "Three authors" } }, ) { title author { @@ -211,7 +211,7 @@ describe('maxResults Limit', () => { // Reset the count for each query context.totalResults = 0; posts = await context.lists.Post.findMany({ - where: { title: 'Three authors' }, + where: { title: { equals: 'Three authors' } }, query: 'title', }); @@ -241,7 +241,7 @@ describe('maxResults Limit', () => { ({ errors } = await context.graphql.raw({ query: ` query { - posts(where: { title: "Two authors" }) { + posts(where: { title: { equals: "Two authors" } }) { title author { posts { diff --git a/tests/api-tests/queries/relationships.test.ts b/tests/api-tests/queries/relationships.test.ts index c26f4370c85..a87d4960d3c 100644 --- a/tests/api-tests/queries/relationships.test.ts +++ b/tests/api-tests/queries/relationships.test.ts @@ -47,7 +47,7 @@ describe('Querying with relationship filters', () => { // Create an item that does the linking const allPosts = await context.lists.Post.findMany({ - where: { author: { name_contains: 'J' } }, + where: { author: { name: { contains: 'J' } } }, query: 'id title', }); expect(allPosts).toHaveLength(3); @@ -74,7 +74,7 @@ describe('Querying with relationship filters', () => { // Create an item that does the linking const _posts = await context.lists.Post.findMany({ - where: { author: { name_contains: 'J' } }, + where: { author: { name: { contains: 'J' } } }, query: 'id title author { id name }', }); expect(_posts).toMatchObject([{ id: posts[0].id, title: posts[0].title }]); @@ -110,7 +110,7 @@ describe('Querying with relationship filters', () => { }; test( - '_every condition', + 'every condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -118,7 +118,7 @@ describe('Querying with relationship filters', () => { // EVERY const _users = await context.lists.User.findMany({ - where: { feed_every: { title_contains: 'J' } }, + where: { feed: { every: { title: { contains: 'J' } } } }, query: 'id name feed { id title }', }); @@ -127,7 +127,7 @@ describe('Querying with relationship filters', () => { ); test( - '_some condition', + 'some condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -135,7 +135,7 @@ describe('Querying with relationship filters', () => { // SOME const _users = await context.lists.User.findMany({ - where: { feed_some: { title_contains: 'J' } }, + where: { feed: { some: { title: { contains: 'J' } } } }, query: 'id feed(orderBy: { title: asc }) { title }', }); @@ -150,7 +150,7 @@ describe('Querying with relationship filters', () => { ); test( - '_none condition', + 'none condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -158,7 +158,7 @@ describe('Querying with relationship filters', () => { // NONE const _users = await context.lists.User.findMany({ - where: { feed_none: { title_contains: 'J' } }, + where: { feed: { none: { title: { contains: 'J' } } } }, query: 'id name feed { id title }', }); @@ -191,7 +191,7 @@ describe('Querying with relationship filters', () => { }; test( - '_every condition', + 'every condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -199,7 +199,7 @@ describe('Querying with relationship filters', () => { // EVERY const _users = await context.lists.User.findMany({ - where: { feed_every: { title_contains: 'J' } }, + where: { feed: { every: { title: { contains: 'J' } } } }, query: 'id feed { id title }', }); @@ -210,7 +210,7 @@ describe('Querying with relationship filters', () => { ); test( - '_some condition', + 'some condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -218,7 +218,7 @@ describe('Querying with relationship filters', () => { // SOME const _users = await context.lists.User.findMany({ - where: { feed_some: { title_contains: 'J' } }, + where: { feed: { some: { title: { contains: 'J' } } } }, query: 'id name feed(orderBy: { title: asc }) { id title }', }); @@ -230,7 +230,7 @@ describe('Querying with relationship filters', () => { ); test( - '_none condition', + 'none condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -238,7 +238,7 @@ describe('Querying with relationship filters', () => { // NONE const _users = await context.lists.User.findMany({ - where: { feed_none: { title_contains: 'J' } }, + where: { feed: { none: { title: { contains: 'J' } } } }, query: 'id feed { title }', }); @@ -264,7 +264,7 @@ describe('Querying with relationship filters', () => { }; test( - '_every condition', + 'every condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -272,7 +272,7 @@ describe('Querying with relationship filters', () => { // EVERY const _users = await context.lists.User.findMany({ - where: { feed_every: { title_contains: 'J' } }, + where: { feed: { every: { title: { contains: 'J' } } } }, query: 'id feed { id title }', }); @@ -286,7 +286,7 @@ describe('Querying with relationship filters', () => { ); test( - '_some condition', + 'some condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -294,7 +294,7 @@ describe('Querying with relationship filters', () => { // SOME const _users = await context.lists.User.findMany({ - where: { feed_some: { author: { id: users[0].id } } }, + where: { feed: { some: { author: { id: { equals: users[0].id } } } } }, query: 'id name feed { id title }', }); expect(_users).toEqual([]); @@ -302,7 +302,7 @@ describe('Querying with relationship filters', () => { ); test( - '_none condition', + 'none condition', runner(async ({ context }) => { const create = async (listKey: string, data: any) => context.lists[listKey].createOne({ data }); @@ -310,7 +310,7 @@ describe('Querying with relationship filters', () => { // NONE const _users = await context.lists.User.findMany({ - where: { feed_none: { title_contains: 'J' } }, + where: { feed: { none: { title: { contains: 'J' } } } }, query: 'id feed { title }', }); diff --git a/tests/api-tests/relationships/crud-self-ref/many-to-many-one-sided.test.ts b/tests/api-tests/relationships/crud-self-ref/many-to-many-one-sided.test.ts index ca88814dfc9..62c85fb47ef 100644 --- a/tests/api-tests/relationships/crud-self-ref/many-to-many-one-sided.test.ts +++ b/tests/api-tests/relationships/crud-self-ref/many-to-many-one-sided.test.ts @@ -93,7 +93,7 @@ const runner = setupTestRunner({ describe(`Many-to-many relationships`, () => { describe('Read', () => { test( - '_some', + 'some', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -104,7 +104,7 @@ describe(`Many-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friends_some: { name } }, + where: { friends: { some: { name: { equals: name } } } }, }); expect(users.length).toEqual(count); }) @@ -112,7 +112,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_none', + 'none', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -123,7 +123,7 @@ describe(`Many-to-many relationships`, () => { ['D', 9], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friends_none: { name } }, + where: { friends: { none: { name: { equals: name } } } }, }); expect(users.length).toEqual(count); }) @@ -131,7 +131,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_every', + 'every', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -142,7 +142,7 @@ describe(`Many-to-many relationships`, () => { ['D', 1], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friends_every: { name } }, + where: { friends: { every: { name: { equals: name } } } }, }); expect(users.length).toEqual(count); }) diff --git a/tests/api-tests/relationships/crud-self-ref/many-to-many.test.ts b/tests/api-tests/relationships/crud-self-ref/many-to-many.test.ts index 1d7fa945071..6be81e702de 100644 --- a/tests/api-tests/relationships/crud-self-ref/many-to-many.test.ts +++ b/tests/api-tests/relationships/crud-self-ref/many-to-many.test.ts @@ -99,7 +99,7 @@ const runner = setupTestRunner({ describe(`Many-to-many relationships`, () => { describe('Read', () => { test( - '_some', + 'some', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -110,7 +110,7 @@ describe(`Many-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const _users = await context.lists.User.findMany({ - where: { friends_some: { name } }, + where: { friends: { some: { name: { equals: name } } } }, }); expect(_users.length).toEqual(count); }) @@ -118,7 +118,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_none', + 'none', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -129,7 +129,7 @@ describe(`Many-to-many relationships`, () => { ['D', 9], ].map(async ([name, count]) => { const _users = await context.lists.User.findMany({ - where: { friends_none: { name } }, + where: { friends: { none: { name: { equals: name } } } }, }); expect(_users.length).toEqual(count); }) @@ -137,7 +137,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_every', + 'every', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -148,7 +148,7 @@ describe(`Many-to-many relationships`, () => { ['D', 1], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friends_every: { name } }, + where: { friends: { every: { name: { equals: name } } } }, }); expect(users.length).toEqual(count); }) diff --git a/tests/api-tests/relationships/crud-self-ref/one-to-many-one-sided.test.ts b/tests/api-tests/relationships/crud-self-ref/one-to-many-one-sided.test.ts index f664a582ac9..9d77cb502f1 100644 --- a/tests/api-tests/relationships/crud-self-ref/one-to-many-one-sided.test.ts +++ b/tests/api-tests/relationships/crud-self-ref/one-to-many-one-sided.test.ts @@ -104,7 +104,7 @@ describe(`One-to-many relationships`, () => { ['E', 0], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friend: { name_contains: name } }, + where: { friend: { name: { contains: name } } }, }); expect(users.length).toEqual(count); }) @@ -112,18 +112,18 @@ describe(`One-to-many relationships`, () => { }) ); test( - 'is_null: true', + 'is null', runner(async ({ context }) => { await createComplexData(context); - const users = await context.lists.User.findMany({ where: { friend_is_null: true } }); + const users = await context.lists.User.findMany({ where: { friend: null } }); expect(users.length).toEqual(5); }) ); test( - 'is_null: false', + 'is not null', runner(async ({ context }) => { await createComplexData(context); - const users = await context.lists.User.findMany({ where: { friend_is_null: false } }); + const users = await context.lists.User.findMany({ where: { NOT: { friend: null } } }); expect(users.length).toEqual(4); }) ); diff --git a/tests/api-tests/relationships/crud-self-ref/one-to-many.test.ts b/tests/api-tests/relationships/crud-self-ref/one-to-many.test.ts index e48664dd30d..09d4ec8dae5 100644 --- a/tests/api-tests/relationships/crud-self-ref/one-to-many.test.ts +++ b/tests/api-tests/relationships/crud-self-ref/one-to-many.test.ts @@ -105,7 +105,7 @@ describe(`One-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friendOf: { name_contains: name } }, + where: { friendOf: { name: { contains: name } } }, }); expect(users.length).toEqual(count); }) @@ -113,23 +113,23 @@ describe(`One-to-many relationships`, () => { }) ); test( - 'is_null: true', + 'is null', runner(async ({ context }) => { await createReadData(context); - const users = await context.lists.User.findMany({ where: { friendOf_is_null: true } }); + const users = await context.lists.User.findMany({ where: { friendOf: null } }); expect(users.length).toEqual(5); }) ); test( - 'is_null: false', + 'is not null', runner(async ({ context }) => { await createReadData(context); - const users = await context.lists.User.findMany({ where: { friendOf_is_null: false } }); + const users = await context.lists.User.findMany({ where: { NOT: { friendOf: null } } }); expect(users.length).toEqual(6); }) ); test( - '_some', + 'some', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -140,7 +140,7 @@ describe(`One-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friends_some: { name } }, + where: { friends: { some: { name: { equals: name } } } }, }); expect(users.length).toEqual(count); }) @@ -148,7 +148,7 @@ describe(`One-to-many relationships`, () => { }) ); test( - '_none', + 'none', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -159,7 +159,7 @@ describe(`One-to-many relationships`, () => { ['D', 4 + 7], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friends_none: { name } }, + where: { friends: { none: { name: { equals: name } } } }, }); expect(users.length).toEqual(count); }) @@ -167,7 +167,7 @@ describe(`One-to-many relationships`, () => { }) ); test( - '_every', + 'every', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -178,7 +178,7 @@ describe(`One-to-many relationships`, () => { ['D', 1 + 7], ].map(async ([name, count]) => { const users = await context.lists.User.findMany({ - where: { friends_every: { name } }, + where: { friends: { every: { name: { equals: name } } } }, }); expect(users.length).toEqual(count); }) diff --git a/tests/api-tests/relationships/crud-self-ref/one-to-one.test.ts b/tests/api-tests/relationships/crud-self-ref/one-to-one.test.ts index bb04e20fe8d..3d0dc526ca7 100644 --- a/tests/api-tests/relationships/crud-self-ref/one-to-one.test.ts +++ b/tests/api-tests/relationships/crud-self-ref/one-to-one.test.ts @@ -79,7 +79,7 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); const { user, friend } = await createUserAndFriend(context); const users = await context.lists.User.findMany({ - where: { friend: { name: friend.name } }, + where: { friend: { name: { equals: friend.name } } }, }); expect(users.length).toEqual(1); expect(users[0].id).toEqual(user.id); @@ -92,45 +92,45 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); const { user, friend } = await createUserAndFriend(context); const users = await context.lists.User.findMany({ - where: { friendOf: { name: user.name } }, + where: { friendOf: { name: { equals: user.name } } }, }); expect(users.length).toEqual(1); expect(users[0].id).toEqual(friend.id); }) ); test( - 'Where friend: is_null: true', + 'Where friend: is null', runner(async ({ context }) => { await createInitialData(context); await createUserAndFriend(context); - const users = await context.lists.User.findMany({ where: { friend_is_null: true } }); + const users = await context.lists.User.findMany({ where: { friend: null } }); expect(users.length).toEqual(4); }) ); test( - 'Where friendOf: is_null: true', + 'Where friendOf: is null', runner(async ({ context }) => { await createInitialData(context); await createUserAndFriend(context); - const users = await context.lists.User.findMany({ where: { friendOf_is_null: true } }); + const users = await context.lists.User.findMany({ where: { friendOf: null } }); expect(users.length).toEqual(4); }) ); test( - 'Where friend: is_null: false', + 'Where friend: is not null', runner(async ({ context }) => { await createInitialData(context); await createUserAndFriend(context); - const users = await context.lists.User.findMany({ where: { friend_is_null: false } }); + const users = await context.lists.User.findMany({ where: { NOT: { friend: null } } }); expect(users.length).toEqual(1); }) ); test( - 'Where friendOf: is_null: false', + 'Where friendOf: is not null', runner(async ({ context }) => { await createInitialData(context); await createUserAndFriend(context); - const users = await context.lists.User.findMany({ where: { friendOf_is_null: false } }); + const users = await context.lists.User.findMany({ where: { NOT: { friendOf: null } } }); expect(users.length).toEqual(1); }) ); @@ -150,7 +150,7 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); const { friend } = await createUserAndFriend(context); const count = await context.lists.User.count({ - where: { friend: { name: friend.name } }, + where: { friend: { name: { equals: friend.name } } }, }); expect(count).toEqual(1); }) @@ -162,7 +162,7 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); const { user } = await createUserAndFriend(context); const count = await context.lists.User.count({ - where: { friendOf: { name: user.name } }, + where: { friendOf: { name: { equals: user.name } } }, }); expect(count).toEqual(1); }) @@ -172,7 +172,7 @@ describe(`One-to-one relationships`, () => { runner(async ({ context }) => { await createInitialData(context); await createUserAndFriend(context); - const count = await context.lists.User.count({ where: { friend_is_null: true } }); + const count = await context.lists.User.count({ where: { friend: null } }); expect(count).toEqual(4); }) ); @@ -182,7 +182,7 @@ describe(`One-to-one relationships`, () => { runner(async ({ context }) => { await createInitialData(context); await createUserAndFriend(context); - const count = await context.lists.User.count({ where: { friendOf_is_null: true } }); + const count = await context.lists.User.count({ where: { friendOf: null } }); expect(count).toEqual(4); }) ); diff --git a/tests/api-tests/relationships/crud/many-to-many-one-sided.test.ts b/tests/api-tests/relationships/crud/many-to-many-one-sided.test.ts index 446779b4dc4..fb9ca10d78f 100644 --- a/tests/api-tests/relationships/crud/many-to-many-one-sided.test.ts +++ b/tests/api-tests/relationships/crud/many-to-many-one-sided.test.ts @@ -100,7 +100,7 @@ const runner = setupTestRunner({ describe(`Many-to-many relationships`, () => { describe('Read', () => { test( - '_some', + 'some', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -111,7 +111,7 @@ describe(`Many-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_some: { name } }, + where: { locations: { some: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) @@ -119,7 +119,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_none', + 'none', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -130,7 +130,7 @@ describe(`Many-to-many relationships`, () => { ['D', 9], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_none: { name } }, + where: { locations: { none: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) @@ -138,7 +138,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_every', + 'every', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -149,7 +149,7 @@ describe(`Many-to-many relationships`, () => { ['D', 1], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_every: { name } }, + where: { locations: { every: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) @@ -171,7 +171,7 @@ describe(`Many-to-many relationships`, () => { ); test( - '_some', + 'some', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -182,7 +182,7 @@ describe(`Many-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const _count = await context.lists.Company.count({ - where: { locations_some: { name } }, + where: { locations: { some: { name: { equals: name } } } }, }); expect(_count).toEqual(count); }) @@ -190,7 +190,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_none', + 'none', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -201,7 +201,7 @@ describe(`Many-to-many relationships`, () => { ['D', 9], ].map(async ([name, count]) => { const _count = await context.lists.Company.count({ - where: { locations_none: { name } }, + where: { locations: { none: { name: { equals: name } } } }, }); expect(_count).toEqual(count); }) @@ -209,7 +209,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_every', + 'every', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -220,7 +220,7 @@ describe(`Many-to-many relationships`, () => { ['D', 1], ].map(async ([name, count]) => { const _count = await context.lists.Company.count({ - where: { locations_every: { name } }, + where: { locations: { every: { name: { equals: name } } } }, }); expect(_count).toEqual(count); }) diff --git a/tests/api-tests/relationships/crud/many-to-many.test.ts b/tests/api-tests/relationships/crud/many-to-many.test.ts index d7e00b5d4e9..5f9309524e3 100644 --- a/tests/api-tests/relationships/crud/many-to-many.test.ts +++ b/tests/api-tests/relationships/crud/many-to-many.test.ts @@ -114,7 +114,7 @@ const runner = setupTestRunner({ describe(`Many-to-many relationships`, () => { describe('Read', () => { test( - '_some', + 'some', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -125,7 +125,7 @@ describe(`Many-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_some: { name } }, + where: { locations: { some: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) @@ -133,7 +133,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_none', + 'none', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -144,7 +144,7 @@ describe(`Many-to-many relationships`, () => { ['D', 9], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_none: { name } }, + where: { locations: { none: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) @@ -152,7 +152,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_every', + 'every', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -163,7 +163,7 @@ describe(`Many-to-many relationships`, () => { ['D', 1], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_every: { name } }, + where: { locations: { every: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) @@ -184,7 +184,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_some', + 'some', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -195,7 +195,7 @@ describe(`Many-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const _count = await context.lists.Company.count({ - where: { locations_some: { name } }, + where: { locations: { some: { name: { equals: name } } } }, }); expect(_count).toEqual(count); }) @@ -203,7 +203,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_none', + 'none', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -214,7 +214,7 @@ describe(`Many-to-many relationships`, () => { ['D', 9], ].map(async ([name, count]) => { const _count = await context.lists.Company.count({ - where: { locations_none: { name } }, + where: { locations: { none: { name: { equals: name } } } }, }); expect(_count).toEqual(count); }) @@ -222,7 +222,7 @@ describe(`Many-to-many relationships`, () => { }) ); test( - '_every', + 'every', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -233,7 +233,7 @@ describe(`Many-to-many relationships`, () => { ['D', 1], ].map(async ([name, count]) => { const _count = await context.lists.Company.count({ - where: { locations_every: { name } }, + where: { locations: { every: { name: { equals: name } } } }, }); expect(_count).toEqual(count); }) diff --git a/tests/api-tests/relationships/crud/one-to-many-one-sided.test.ts b/tests/api-tests/relationships/crud/one-to-many-one-sided.test.ts index 5421134c607..d7b96de0065 100644 --- a/tests/api-tests/relationships/crud/one-to-many-one-sided.test.ts +++ b/tests/api-tests/relationships/crud/one-to-many-one-sided.test.ts @@ -131,7 +131,7 @@ describe(`One-to-many relationships`, () => { ['E', 0], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { location: { name_contains: name } }, + where: { location: { name: { contains: name } } }, }); expect(companies.length).toEqual(count); }) @@ -139,21 +139,21 @@ describe(`One-to-many relationships`, () => { }) ); test( - 'is_null: true', + 'is null', runner(async ({ context }) => { await createComplexData(context); const companies = await context.lists.Company.findMany({ - where: { location_is_null: true }, + where: { location: null }, }); expect(companies.length).toEqual(1); }) ); test( - 'is_null: false', + 'is not null', runner(async ({ context }) => { await createComplexData(context); const companies = await context.lists.Company.findMany({ - where: { location_is_null: false }, + where: { NOT: { location: null } }, }); expect(companies.length).toEqual(4); }) diff --git a/tests/api-tests/relationships/crud/one-to-many.test.ts b/tests/api-tests/relationships/crud/one-to-many.test.ts index bc780b80485..6585dbe2f75 100644 --- a/tests/api-tests/relationships/crud/one-to-many.test.ts +++ b/tests/api-tests/relationships/crud/one-to-many.test.ts @@ -118,7 +118,7 @@ describe(`One-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const locations = await context.lists.Location.findMany({ - where: { company: { name_contains: name } }, + where: { company: { name: { contains: name } } }, }); expect(locations.length).toEqual(count); }) @@ -126,27 +126,27 @@ describe(`One-to-many relationships`, () => { }) ); test( - 'is_null: true', + 'is null', runner(async ({ context }) => { await createReadData(context); const locations = await context.lists.Location.findMany({ - where: { company_is_null: true }, + where: { company: null }, }); expect(locations.length).toEqual(1); }) ); test( - 'is_null: false', + 'is not null', runner(async ({ context }) => { await createReadData(context); const locations = await context.lists.Location.findMany({ - where: { company_is_null: false }, + where: { NOT: { company: null } }, }); expect(locations.length).toEqual(6); }) ); test( - '_some', + 'some', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -157,7 +157,7 @@ describe(`One-to-many relationships`, () => { ['D', 0], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_some: { name } }, + where: { locations: { some: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) @@ -165,7 +165,7 @@ describe(`One-to-many relationships`, () => { }) ); test( - '_none', + 'none', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -176,7 +176,7 @@ describe(`One-to-many relationships`, () => { ['D', 4], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_none: { name } }, + where: { locations: { none: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) @@ -184,7 +184,7 @@ describe(`One-to-many relationships`, () => { }) ); test( - '_every', + 'every', runner(async ({ context }) => { await createReadData(context); await Promise.all( @@ -195,7 +195,7 @@ describe(`One-to-many relationships`, () => { ['D', 1], ].map(async ([name, count]) => { const companies = await context.lists.Company.findMany({ - where: { locations_every: { name } }, + where: { locations: { every: { name: { equals: name } } } }, }); expect(companies.length).toEqual(count); }) diff --git a/tests/api-tests/relationships/crud/one-to-one.test.ts b/tests/api-tests/relationships/crud/one-to-one.test.ts index e914749cbf4..b4117a0c4b1 100644 --- a/tests/api-tests/relationships/crud/one-to-one.test.ts +++ b/tests/api-tests/relationships/crud/one-to-one.test.ts @@ -118,10 +118,10 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); const { location, company } = await createCompanyAndLocation(context); const locations = await context.lists.Location.findMany({ - where: { company: { name: company.name } }, + where: { company: { name: { equals: company.name } } }, }); const companies = await context.lists.Company.findMany({ - where: { location: { name: location.name } }, + where: { location: { name: { equals: location.name } } }, }); expect(locations.length).toEqual(1); expect(locations[0].id).toEqual(location.id); @@ -135,10 +135,10 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); const { location, company } = await createLocationAndCompany(context); const locations = await context.lists.Location.findMany({ - where: { company: { name: company.name } }, + where: { company: { name: { equals: company.name } } }, }); const companies = await context.lists.Company.findMany({ - where: { location: { name: location.name } }, + where: { location: { name: { equals: location.name } } }, }); expect(locations.length).toEqual(1); expect(locations[0].id).toEqual(location.id); @@ -147,60 +147,60 @@ describe(`One-to-one relationships`, () => { }) ); test( - 'Where A: is_null: true', + 'Where A: is null', runner(async ({ context }) => { await createInitialData(context); await createCompanyAndLocation(context); const locations = await context.lists.Location.findMany({ - where: { company_is_null: true }, + where: { company: null }, }); const companies = await context.lists.Company.findMany({ - where: { location_is_null: true }, + where: { location: null }, }); expect(locations.length).toEqual(4); expect(companies.length).toEqual(3); }) ); test( - 'Where B: is_null: true', + 'Where B: is null', runner(async ({ context }) => { await createInitialData(context); await createLocationAndCompany(context); const locations = await context.lists.Location.findMany({ - where: { company_is_null: true }, + where: { company: null }, }); const companies = await context.lists.Company.findMany({ - where: { location_is_null: true }, + where: { location: null }, }); expect(locations.length).toEqual(4); expect(companies.length).toEqual(3); }) ); test( - 'Where A: is_null: false', + 'Where A: is not null', runner(async ({ context }) => { await createInitialData(context); await createCompanyAndLocation(context); const locations = await context.lists.Location.findMany({ - where: { company_is_null: false }, + where: { NOT: { company: null } }, }); const companies = await context.lists.Company.findMany({ - where: { location_is_null: false }, + where: { NOT: { location: null } }, }); expect(locations.length).toEqual(1); expect(companies.length).toEqual(1); }) ); test( - 'Where B: is_null: false', + 'Where B: is not null', runner(async ({ context }) => { await createInitialData(context); await createLocationAndCompany(context); const locations = await context.lists.Location.findMany({ - where: { company_is_null: false }, + where: { NOT: { company: null } }, }); const companies = await context.lists.Company.findMany({ - where: { location_is_null: false }, + where: { NOT: { location: null } }, }); expect(locations.length).toEqual(1); expect(companies.length).toEqual(1); @@ -224,10 +224,10 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); const { location, company } = await createCompanyAndLocation(context); const locationsCount = await context.lists.Location.count({ - where: { company: { name: company.name } }, + where: { company: { name: { equals: company.name } } }, }); const companiesCount = await context.lists.Company.count({ - where: { location: { name: location.name } }, + where: { location: { name: { equals: location.name } } }, }); expect(companiesCount).toEqual(1); expect(locationsCount).toEqual(1); @@ -239,10 +239,10 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); const { location, company } = await createLocationAndCompany(context); const locationsCount = await context.lists.Location.count({ - where: { company: { name: company.name } }, + where: { company: { name: { equals: company.name } } }, }); const companiesCount = await context.lists.Company.count({ - where: { location: { name: location.name } }, + where: { location: { name: { equals: location.name } } }, }); expect(companiesCount).toEqual(1); expect(locationsCount).toEqual(1); @@ -254,10 +254,10 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); await createCompanyAndLocation(context); const locationsCount = await context.lists.Location.count({ - where: { company_is_null: true }, + where: { company: null }, }); const companiesCount = await context.lists.Company.count({ - where: { location_is_null: true }, + where: { location: null }, }); expect(companiesCount).toEqual(3); expect(locationsCount).toEqual(4); @@ -269,10 +269,10 @@ describe(`One-to-one relationships`, () => { await createInitialData(context); await createLocationAndCompany(context); const locationsCount = await context.lists.Location.count({ - where: { company_is_null: true }, + where: { company: null }, }); const companiesCount = await context.lists.Company.count({ - where: { location_is_null: true }, + where: { location: null }, }); expect(companiesCount).toEqual(3); expect(locationsCount).toEqual(4); diff --git a/tests/api-tests/relationships/filtering/access-control.test.ts b/tests/api-tests/relationships/filtering/access-control.test.ts index e49208685d5..df0b3715ba7 100644 --- a/tests/api-tests/relationships/filtering/access-control.test.ts +++ b/tests/api-tests/relationships/filtering/access-control.test.ts @@ -24,7 +24,7 @@ const runner = setupTestRunner({ }, access: { // Limit read access to the first post only - read: { name_in: [postNames[1]] }, + read: { name: { in: [postNames[1]] } }, }, }), }), @@ -97,7 +97,7 @@ describe('relationship filtering with access control', () => { where: { id: user.id }, // Knowingly filter to an ID I don't have read access to // to see if the filter is correctly "AND"d with the access control - query: `id username posts(where: { id_in: ["${postIds[2]}"] }) { id }`, + query: `id username posts(where: { id: { in: ["${postIds[2]}"] } }) { id }`, }); expect(item).toMatchObject({ id: expect.any(String), username, posts: [] }); diff --git a/tests/api-tests/relationships/filtering/filtering.test.ts b/tests/api-tests/relationships/filtering/filtering.test.ts index 3caff8d17b8..32224aea55a 100644 --- a/tests/api-tests/relationships/filtering/filtering.test.ts +++ b/tests/api-tests/relationships/filtering/filtering.test.ts @@ -36,7 +36,10 @@ describe('relationship filtering', () => { const users = await context.lists.User.findMany({ where: { - AND: [{ company: { name_contains: 'in' } }, { company: { name_contains: 'll' } }], + AND: [ + { company: { name: { contains: 'in' } } }, + { company: { name: { contains: 'll' } } }, + ], }, query: 'id company { id name }', }); @@ -63,7 +66,10 @@ describe('relationship filtering', () => { const users = await context.lists.User.findMany({ where: { - OR: [{ company: { name_contains: 'in' } }, { company: { name_contains: 'xx' } }], + OR: [ + { company: { name: { contains: 'in' } } }, + { company: { name: { contains: 'xx' } } }, + ], }, query: 'id company { id name }', }); @@ -93,8 +99,8 @@ describe('relationship filtering', () => { const users = (await context.lists.User.findMany({ where: { AND: [ - { posts_some: { content_contains: 'hi' } }, - { posts_some: { content_contains: 'lo' } }, + { posts: { some: { content: { contains: 'hi' } } } }, + { posts: { some: { content: { contains: 'lo' } } } }, ], }, query: 'id posts { id content }', @@ -125,8 +131,8 @@ describe('relationship filtering', () => { const users = (await context.lists.User.findMany({ where: { OR: [ - { posts_some: { content_contains: 'o w' } }, - { posts_some: { content_contains: '? O' } }, + { posts: { some: { content: { contains: 'o w' } } } }, + { posts: { some: { content: { contains: '? O' } } } }, ], }, query: 'id posts { id content }', @@ -199,7 +205,10 @@ describe('relationship filtering', () => { posts: { content: string }[]; }[]; const users = (await context.lists.User.findMany({ - where: { company: { name: adsCompany.name }, posts_every: { content: 'spam' } }, + where: { + company: { name: { equals: adsCompany.name } }, + posts: { every: { content: { equals: 'spam' } } }, + }, query: 'id company { id name } posts { content }', })) as T; expect(users).toHaveLength(2); @@ -209,7 +218,10 @@ describe('relationship filtering', () => { // adsCompany users with no spam const users2 = (await context.lists.User.findMany({ - where: { company: { name: adsCompany.name }, posts_none: { content: 'spam' } }, + where: { + company: { name: { equals: adsCompany.name } }, + posts: { none: { content: { equals: 'spam' } } }, + }, query: 'id company { id name } posts { content }', })) as T; @@ -220,7 +232,10 @@ describe('relationship filtering', () => { // adsCompany users with some spam const users3 = (await context.lists.User.findMany({ - where: { company: { name: adsCompany.name }, posts_some: { content: 'spam' } }, + where: { + company: { name: { equals: adsCompany.name } }, + posts: { some: { content: { equals: 'spam' } } }, + }, query: 'id company { id name } posts { content }', })) as T; diff --git a/tests/api-tests/relationships/filtering/nested.test.ts b/tests/api-tests/relationships/filtering/nested.test.ts index 9679c5122b8..daf5829477b 100644 --- a/tests/api-tests/relationships/filtering/nested.test.ts +++ b/tests/api-tests/relationships/filtering/nested.test.ts @@ -36,7 +36,7 @@ describe('relationship filtering', () => { }); const users = (await context.lists.User.findMany({ - query: `id posts (where: { content_contains: "hi" }){ id content }`, + query: `id posts (where: { content: { contains: "hi" } }){ id content }`, })) as { id: IdType; posts: { id: IdType; content: string }[] }[]; expect(users).toHaveLength(2); users[0].posts = users[0].posts.map(({ id }) => id).sort(); @@ -63,7 +63,7 @@ describe('relationship filtering', () => { }); const users = await context.lists.User.findMany({ - query: 'id posts(first: 1, orderBy: { content: asc }) { id }', + query: 'id posts(take: 1, orderBy: { content: asc }) { id }', }); expect(users).toContainEqual({ id: user.id, posts: [ids[0]] }); expect(users).toContainEqual({ id: user2.id, posts: [ids[0]] }); @@ -86,7 +86,7 @@ describe('relationship filtering', () => { const users = await context.lists.User.findMany({ query: - 'id posts(where: { AND: [{ content_contains: "hi" }, { content_contains: "lo" }] }){ id }', + 'id posts(where: { AND: [{ content: { contains: "hi" } }, { content: { contains: "lo" } }] }){ id }', }); expect(users).toContainEqual({ id: user.id, posts: [ids[2]] }); @@ -110,7 +110,7 @@ describe('relationship filtering', () => { const users = await context.lists.User.findMany({ query: - 'id posts(where: { OR: [{ content_contains: "i w" }, { content_contains: "? O" }] }){ id content }', + 'id posts(where: { OR: [{ content: { contains: "i w" } }, { content: { contains: "? O" } }] }){ id content }', }); expect(users).toContainEqual({ id: user.id, @@ -129,7 +129,7 @@ describe('relationship filtering', () => { await context.lists.User.createOne({ data: {} }); const users = await context.lists.User.findMany({ - where: { posts_some: { content_contains: 'foo' } }, + where: { posts: { some: { content: { contains: 'foo' } } } }, query: 'posts { id }', }); expect(users).toHaveLength(0); @@ -174,7 +174,7 @@ describe('relationship meta filtering', () => { }); const users = await context.lists.User.findMany({ - query: 'id postsCount(where: { content_contains: "hi" })', + query: 'id postsCount(where: { content: { contains: "hi" } })', }); expect(users).toHaveLength(2); expect(users).toContainEqual({ id: user.id, postsCount: 2 }); @@ -197,7 +197,7 @@ describe('relationship meta filtering', () => { }); const users = await context.lists.User.findMany({ - query: `id postsCount(where: { AND: [{ content_contains: "hi" }, { content_contains: "lo" }] })`, + query: `id postsCount(where: { AND: [{ content: { contains: "hi" } }, { content: { contains: "lo" } }] })`, }); expect(users).toHaveLength(2); @@ -222,7 +222,7 @@ describe('relationship meta filtering', () => { const users = await context.lists.User.findMany({ query: - 'id postsCount(where: { OR: [{ content_contains: "i w" }, { content_contains: "? O" }] })', + 'id postsCount(where: { OR: [{ content: { contains: "i w" } }, { content: { contains: "? O" } }] })', }); expect(users).toHaveLength(2); expect(users).toContainEqual({ id: user.id, postsCount: 2 }); diff --git a/tests/api-tests/relationships/many-to-one-to-one.test.ts b/tests/api-tests/relationships/many-to-one-to-one.test.ts index 0bbf84d21ab..b56821167b4 100644 --- a/tests/api-tests/relationships/many-to-one-to-one.test.ts +++ b/tests/api-tests/relationships/many-to-one-to-one.test.ts @@ -123,7 +123,11 @@ describe(`One-to-one relationships`, () => { const owner = await createCompanyAndLocation(context); const name1 = owner.companies[0].location.custodians[0].name; const owners = await context.lists.Owner.findMany({ - where: { companies_some: { location: { custodians_some: { name: name1 } } } }, + where: { + companies: { + some: { location: { custodians: { some: { name: { equals: name1 } } } } }, + }, + }, query: 'id companies { location { custodians { name } } }', }); expect(owners.length).toEqual(1); @@ -137,7 +141,9 @@ describe(`One-to-one relationships`, () => { const owner = await createCompanyAndLocation(context); const name1 = owner.name; const custodians = await context.lists.Custodian.findMany({ - where: { locations_some: { company: { owners_some: { name: name1 } } } }, + where: { + locations: { some: { company: { owners: { some: { name: { equals: name1 } } } } } }, + }, query: 'id locations { company { owners { name } } }', }); expect(custodians.length).toEqual(2); @@ -151,10 +157,16 @@ describe(`One-to-one relationships`, () => { const name1 = owner.name; const owners = await context.lists.Owner.findMany({ where: { - companies_some: { - location: { - custodians_some: { - locations_some: { company: { owners_some: { name: name1 } } }, + companies: { + some: { + location: { + custodians: { + some: { + locations: { + some: { company: { owners: { some: { name: { equals: name1 } } } } }, + }, + }, + }, }, }, }, @@ -174,10 +186,16 @@ describe(`One-to-one relationships`, () => { const custodians = await context.lists.Custodian.findMany({ where: { - locations_some: { - company: { - owners_some: { - companies_some: { location: { custodians_some: { name: name1 } } }, + locations: { + some: { + company: { + owners: { + some: { + companies: { + some: { location: { custodians: { some: { name: { equals: name1 } } } } }, + }, + }, + }, }, }, }, diff --git a/tests/api-tests/relationships/nested-mutations/create-and-connect-many.test.ts b/tests/api-tests/relationships/nested-mutations/create-and-connect-many.test.ts index a1929770098..f0e5bcdfb62 100644 --- a/tests/api-tests/relationships/nested-mutations/create-and-connect-many.test.ts +++ b/tests/api-tests/relationships/nested-mutations/create-and-connect-many.test.ts @@ -84,7 +84,7 @@ describe('no access control', () => { // Sanity check that the items are actually created const allNotes = await context.lists.Note.findMany({ - where: { id_in: user.notes.map(({ id }) => id) }, + where: { id: { in: user.notes.map(({ id }) => id) } }, query: 'id content', }); @@ -125,7 +125,7 @@ describe('no access control', () => { // Sanity check that the items are actually created const allNotes = await context.lists.Note.findMany({ - where: { id_in: user.notes.map(({ id }) => id) }, + where: { id: { in: user.notes.map(({ id }) => id) } }, query: 'id content', }); diff --git a/tests/api-tests/relationships/nested-mutations/create-many.test.ts b/tests/api-tests/relationships/nested-mutations/create-many.test.ts index 92b0dcb2226..0440365f3aa 100644 --- a/tests/api-tests/relationships/nested-mutations/create-many.test.ts +++ b/tests/api-tests/relationships/nested-mutations/create-many.test.ts @@ -131,7 +131,7 @@ describe('no access control', () => { // Sanity check that the items are actually created const notes = await context.lists.Note.findMany({ - where: { id_in: user1.notes.map(({ id }) => id) }, + where: { id: { in: user1.notes.map(({ id }) => id) } }, }); expect(notes).toHaveLength(user1.notes.length); @@ -187,7 +187,7 @@ describe('no access control', () => { // Sanity check that the items are actually created const notes = await context.lists.Note.findMany({ - where: { id_in: _user.notes.map(({ id }) => id) }, + where: { id: { in: _user.notes.map(({ id }) => id) } }, }); expect(notes).toHaveLength(_user.notes.length); }) @@ -218,7 +218,9 @@ describe('with access control', () => { }); expect(data).toEqual({ createUserToNotesNoRead: { id: expect.any(String), notes: null } }); - expectAccessDenied(errors, [{ path: ['createUserToNotesNoRead', 'notes'] }]); + expectAccessDenied('dev', false, undefined, errors, [ + { path: ['createUserToNotesNoRead', 'notes'] }, + ]); }) ); @@ -307,10 +309,10 @@ describe('with access control', () => { // Confirm it didn't insert either of the records anyway const allNoteNoCreates = await context.lists.NoteNoCreate.findMany({ - where: { content: noteContent }, + where: { content: { equals: noteContent } }, }); const allUserToNotesNoCreates = await context.lists.UserToNotesNoCreate.findMany({ - where: { username: userName }, + where: { username: { equals: userName } }, }); expect(allNoteNoCreates).toMatchObject([]); expect(allUserToNotesNoCreates).toMatchObject([]); @@ -355,7 +357,7 @@ describe('with access control', () => { // Confirm it didn't insert the record anyway const items = await context.lists.NoteNoCreate.findMany({ - where: { content: noteContent }, + where: { content: { equals: noteContent } }, }); expect(items).toMatchObject([]); }) diff --git a/tests/api-tests/relationships/nested-mutations/create-singular.test.ts b/tests/api-tests/relationships/nested-mutations/create-singular.test.ts index 5126d0e0c9a..feedce7adc8 100644 --- a/tests/api-tests/relationships/nested-mutations/create-singular.test.ts +++ b/tests/api-tests/relationships/nested-mutations/create-singular.test.ts @@ -265,14 +265,14 @@ describe('with access control', () => { } // Confirm it didn't insert either of the records anyway const data1 = await context.lists[group.name].findMany({ - where: { name: groupName }, + where: { name: { equals: groupName } }, query: 'id name', }); expect(data1).toMatchObject([]); // Confirm it didn't insert either of the records anyway const data2 = await context.lists[`EventTo${group.name}`].findMany({ - where: { title: eventName }, + where: { title: { equals: eventName } }, query: 'id title', }); expect(data2).toMatchObject([]); @@ -326,7 +326,7 @@ describe('with access control', () => { // Confirm it didn't insert the record anyway const groups = await context.lists[group.name].findMany({ - where: { name: groupName }, + where: { name: { equals: groupName } }, query: 'id name', }); expect(groups).toMatchObject([]); diff --git a/tests/api-tests/relationships/shared-names.test.ts b/tests/api-tests/relationships/shared-names.test.ts index 11e4724c4f0..2d058c296c8 100644 --- a/tests/api-tests/relationships/shared-names.test.ts +++ b/tests/api-tests/relationships/shared-names.test.ts @@ -129,7 +129,7 @@ test( runner(async ({ context }) => { await createInitialData(context); const employees = await context.lists.Employee.findMany({ - where: { company: { employees_some: { role: { name: 'RoleA' } } } }, + where: { company: { employees: { some: { role: { name: { equals: 'RoleA' } } } } } }, query: 'id name', }); expect(employees).toHaveLength(1); diff --git a/tests/api-tests/utils.ts b/tests/api-tests/utils.ts index 9b824b50bf2..5b85916b4cc 100644 --- a/tests/api-tests/utils.ts +++ b/tests/api-tests/utils.ts @@ -17,21 +17,24 @@ export const apiTestConfig = ( }); const unpackErrors = (errors: readonly any[] | undefined) => - (errors || []).map(({ locations, extensions: { exception, ...extensions }, ...unpacked }) => ({ - extensions, - ...unpacked, - })); + (errors || []).map(({ locations, ...unpacked }) => unpacked); const j = (messages: string[]) => messages.map(m => ` - ${m}`).join('\n'); +// FIXME: It's not clear to me right now why sometimes +// we get an expcetion, and other times we don't - TL export const expectInternalServerError = ( errors: readonly any[] | undefined, + expectException: boolean, args: { path: any[]; message: string }[] ) => { const unpackedErrors = unpackErrors(errors); expect(unpackedErrors).toEqual( args.map(({ path, message }) => ({ - extensions: { code: 'INTERNAL_SERVER_ERROR' }, + extensions: { + code: 'INTERNAL_SERVER_ERROR', + ...(expectException ? { exception: { locations: [expect.any(Object)], message } } : {}), + }, path, message, })) @@ -52,17 +55,36 @@ export const expectGraphQLValidationError = ( }; export const expectAccessDenied = ( + mode: 'dev' | 'production', + httpQuery: boolean, + _debug: boolean | undefined, errors: readonly any[] | undefined, args: { path: (string | number)[] }[] ) => { const unpackedErrors = (errors || []).map(({ locations, ...unpacked }) => ({ ...unpacked, })); + const message = 'You do not have access to this resource'; + // We expect to see debug details if: + // - httpQuery is false + // - graphql.debug is true or + // - graphql.debug is undefined and mode !== production or + const expectDebug = + _debug === true || (_debug === undefined && mode !== 'production') || !httpQuery; + // We expect to see the Apollo exception under the same conditions, but only if + // httpQuery is also true. + const expectException = httpQuery && expectDebug; + expect(unpackedErrors).toEqual( args.map(({ path }) => ({ - extensions: { code: undefined }, + extensions: { + code: httpQuery ? 'INTERNAL_SERVER_ERROR' : undefined, + ...(expectException + ? { exception: { stacktrace: expect.arrayContaining([`Error: ${message}`]) } } + : {}), + }, path, - message: 'You do not have access to this resource', + message, })) ); }; @@ -84,28 +106,56 @@ export const expectValidationError = ( }; export const expectExtensionError = ( + mode: 'dev' | 'production', + httpQuery: boolean, + _debug: boolean | undefined, errors: readonly any[] | undefined, extensionName: string, - args: { path: (string | number)[]; messages: string[] }[] + args: { path: (string | number)[]; messages: string[]; debug: any[] }[] ) => { const unpackedErrors = unpackErrors(errors); expect(unpackedErrors).toEqual( - args.map(({ path, messages }) => ({ - extensions: { code: undefined }, - path, - message: `An error occured while running "${extensionName}".\n${j(messages)}`, - })) + args.map(({ path, messages, debug }) => { + const message = `An error occured while running "${extensionName}".\n${j(messages)}`; + const stacktrace = message.split('\n'); + stacktrace[0] = `Error: ${stacktrace[0]}`; + + // We expect to see debug details if: + // - httpQuery is false + // - graphql.debug is true or + // - graphql.debug is undefined and mode !== production or + const expectDebug = + _debug === true || (_debug === undefined && mode !== 'production') || !httpQuery; + // We expect to see the Apollo exception under the same conditions, but only if + // httpQuery is also true. + const expectException = httpQuery && expectDebug; + + return { + extensions: { + code: 'INTERNAL_SERVER_ERROR', + ...(expectException + ? { exception: { debug, stacktrace: expect.arrayContaining(stacktrace) } } + : {}), + ...(expectDebug ? { debug } : {}), + }, + path, + message, + }; + }) ); }; export const expectPrismaError = ( errors: readonly any[] | undefined, - args: { path: any[]; message: string }[] + args: { path: any[]; message: string; code: string; target: string[] }[] ) => { const unpackedErrors = unpackErrors(errors); expect(unpackedErrors).toEqual( - args.map(({ path, message }) => ({ - extensions: { code: 'INTERNAL_SERVER_ERROR' }, + args.map(({ path, message, code, target }) => ({ + extensions: { + code: 'INTERNAL_SERVER_ERROR', + exception: { clientVersion: '2.29.1', code, meta: { target } }, + }, path, message, })) diff --git a/tests/benchmarks/CHANGELOG.md b/tests/benchmarks/CHANGELOG.md index 562a52338c1..5af1efc132f 100644 --- a/tests/benchmarks/CHANGELOG.md +++ b/tests/benchmarks/CHANGELOG.md @@ -1,5 +1,14 @@ # @keystonejs/benchmarks +## 7.0.7 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + - @keystone-next/testing@1.1.1 + ## 7.0.6 ### Patch Changes diff --git a/tests/benchmarks/package.json b/tests/benchmarks/package.json index 2899ace5a3a..0d27253703b 100644 --- a/tests/benchmarks/package.json +++ b/tests/benchmarks/package.json @@ -2,7 +2,7 @@ "name": "@keystone-next/benchmarks-legacy", "description": "A set of benchmarks for running against the KeystoneJS API.", "private": true, - "version": "7.0.6", + "version": "7.0.7", "author": "The KeystoneJS Development Team", "license": "MIT", "engines": { @@ -14,9 +14,9 @@ "repository": "https://github.com/keystonejs/keystone/tree/master/tests/benchmarks", "homepage": "https://github.com/keystonejs/keystone", "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0", - "@keystone-next/testing": "^1.1.0", + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0", + "@keystone-next/testing": "^1.1.1", "cookie-signature": "^1.1.0", "testcheck": "^1.0.0-rc.2" } diff --git a/tests/examples-smoke-tests/custom-admin-ui-pages.test.ts b/tests/examples-smoke-tests/custom-admin-ui-pages.test.ts index 408baf40399..8b3dd98613a 100644 --- a/tests/examples-smoke-tests/custom-admin-ui-pages.test.ts +++ b/tests/examples-smoke-tests/custom-admin-ui-pages.test.ts @@ -11,8 +11,7 @@ exampleProjectTests('custom-admin-ui-pages', browserType => { }); test('Load list', async () => { await page.goto('http://localhost:3000/custom-page'); - const content = await page.textContent('body h1'); - expect(content).toBe('Hello this is a custom page'); + await page.waitForSelector('main h1:has-text("This is a custom Admin UI page")'); }); afterAll(async () => { await browser.close(); diff --git a/tests/examples-smoke-tests/package.json b/tests/examples-smoke-tests/package.json index e043d17e77d..55098b68602 100644 --- a/tests/examples-smoke-tests/package.json +++ b/tests/examples-smoke-tests/package.json @@ -14,7 +14,7 @@ "@types/tough-cookie": "^4.0.1", "execa": "^5.1.1", "node-fetch": "^2.6.1", - "playwright": "^1.13.1", + "playwright": "^1.14.0", "tree-kill": "^1.2.2" } } diff --git a/tests/test-projects/basic/CHANGELOG.md b/tests/test-projects/basic/CHANGELOG.md index bb5b655a22f..49f6ad1855c 100644 --- a/tests/test-projects/basic/CHANGELOG.md +++ b/tests/test-projects/basic/CHANGELOG.md @@ -1,5 +1,13 @@ # @keystone-next/test-projects-basic +## 0.0.3 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + ## 0.0.2 ### Patch Changes diff --git a/tests/test-projects/basic/package.json b/tests/test-projects/basic/package.json index ef134c60847..034d10a328c 100644 --- a/tests/test-projects/basic/package.json +++ b/tests/test-projects/basic/package.json @@ -1,6 +1,6 @@ { "name": "@keystone-next/test-projects-basic", - "version": "0.0.2", + "version": "0.0.3", "private": true, "license": "MIT", "scripts": { @@ -9,8 +9,8 @@ "build": "keystone-next build" }, "dependencies": { - "@keystone-next/fields": "^13.0.0", - "@keystone-next/keystone": "^23.0.0" + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" }, "devDependencies": { "typescript": "^4.3.5" diff --git a/tests/test-projects/basic/schema.graphql b/tests/test-projects/basic/schema.graphql index 49a0f225167..e194b7cb406 100644 --- a/tests/test-projects/basic/schema.graphql +++ b/tests/test-projects/basic/schema.graphql @@ -16,36 +16,75 @@ enum TaskPriorityType { input TaskWhereInput { AND: [TaskWhereInput!] OR: [TaskWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - label: String - label_not: String - label_contains: String - label_not_contains: String - label_in: [String] - label_not_in: [String] - priority: TaskPriorityType - priority_not: TaskPriorityType - priority_in: [TaskPriorityType] - priority_not_in: [TaskPriorityType] - isComplete: Boolean - isComplete_not: Boolean + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter assignedTo: PersonWhereInput - assignedTo_is_null: Boolean - finishBy: String - finishBy_not: String - finishBy_lt: String - finishBy_lte: String - finishBy_gt: String - finishBy_gte: String - finishBy_in: [String] - finishBy_not_in: [String] + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter } input TaskWhereUniqueInput { @@ -103,7 +142,7 @@ type Person { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] tasksCount(where: TaskWhereInput! = {}): Int @@ -112,23 +151,16 @@ type Person { input PersonWhereInput { AND: [PersonWhereInput!] OR: [PersonWhereInput!] - id: ID - id_not: ID - id_lt: ID - id_lte: ID - id_gt: ID - id_gte: ID - id_in: [ID!] - id_not_in: [ID!] - name: String - name_not: String - name_contains: String - name_not_contains: String - name_in: [String] - name_not_in: [String] - tasks_every: TaskWhereInput - tasks_some: TaskWhereInput - tasks_none: TaskWhereInput + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + tasks: TaskManyRelationFilter +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput } input PersonWhereUniqueInput { @@ -194,7 +226,7 @@ type Query { tasks( where: TaskWhereInput! = {} orderBy: [TaskOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Task!] task(where: TaskWhereUniqueInput!): Task @@ -202,7 +234,7 @@ type Query { people( where: PersonWhereInput! = {} orderBy: [PersonOrderByInput!]! = [] - first: Int + take: Int skip: Int! = 0 ): [Person!] person(where: PersonWhereUniqueInput!): Person diff --git a/tests/test-projects/crud-notifications/CHANGELOG.md b/tests/test-projects/crud-notifications/CHANGELOG.md new file mode 100644 index 00000000000..4315359a821 --- /dev/null +++ b/tests/test-projects/crud-notifications/CHANGELOG.md @@ -0,0 +1,8 @@ +# @keystone-next/test-projects-crud-notifications + +## 0.0.3 +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 diff --git a/tests/test-projects/crud-notifications/README.md b/tests/test-projects/crud-notifications/README.md new file mode 100644 index 00000000000..60e641288cb --- /dev/null +++ b/tests/test-projects/crud-notifications/README.md @@ -0,0 +1,4 @@ +## THIS IS A TEST PROJECT + +The sole purpose of this project is to act as a fixture through which we run our admin-ui integration tests. +For useful and applicable examples of how to use keystone, please visit the [examples directory](https://github.com/keystonejs/keystone/tree/master/examples/) or visit our [docs](https://next.keystonejs.com). diff --git a/tests/test-projects/crud-notifications/keystone.ts b/tests/test-projects/crud-notifications/keystone.ts new file mode 100644 index 00000000000..87ce93e9453 --- /dev/null +++ b/tests/test-projects/crud-notifications/keystone.ts @@ -0,0 +1,23 @@ +import { config } from '@keystone-next/keystone/schema'; +import { lists } from './schema'; + +export default config({ + db: { + provider: 'sqlite', + url: process.env.DATABASE_URL || 'file:./test.db', + async onConnect(context) { + await context.lists.Task.createMany({ + data: [...Array.from(Array(50).keys())].map(key => { + return { label: `do not delete ${key}` }; + }), + }); + + await context.lists.Task.createMany({ + data: [...Array.from(Array(25).keys())].map(key => { + return { label: `deletable ${key}` }; + }), + }); + }, + }, + lists, +}); diff --git a/tests/test-projects/crud-notifications/package.json b/tests/test-projects/crud-notifications/package.json new file mode 100644 index 00000000000..585846b3c43 --- /dev/null +++ b/tests/test-projects/crud-notifications/package.json @@ -0,0 +1,22 @@ +{ + "name": "@keystone-next/test-projects-crud-notifications", + "version": "0.0.3", + "private": true, + "license": "MIT", + "scripts": { + "dev": "keystone-next dev", + "start": "keystone-next start", + "build": "keystone-next build" + }, + "dependencies": { + "@keystone-next/fields": "^14.0.0", + "@keystone-next/keystone": "^24.0.0" + }, + "devDependencies": { + "typescript": "^4.3.5" + }, + "engines": { + "node": "^12.20 || >= 14.13" + }, + "repository": "https://github.com/keystonejs/keystone/tree/master/tests/test-projects/crud-notifications" +} diff --git a/tests/test-projects/crud-notifications/schema.graphql b/tests/test-projects/crud-notifications/schema.graphql new file mode 100644 index 00000000000..e194b7cb406 --- /dev/null +++ b/tests/test-projects/crud-notifications/schema.graphql @@ -0,0 +1,329 @@ +type Task { + id: ID! + label: String + priority: TaskPriorityType + isComplete: Boolean + assignedTo: Person + finishBy: String +} + +enum TaskPriorityType { + low + medium + high +} + +input TaskWhereInput { + AND: [TaskWhereInput!] + OR: [TaskWhereInput!] + NOT: [TaskWhereInput!] + id: IDFilter + label: StringNullableFilter + priority: TaskPriorityTypeNullableFilter + isComplete: BooleanNullableFilter + assignedTo: PersonWhereInput + finishBy: DateTimeNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input NestedStringNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringNullableFilter +} + +input TaskPriorityTypeNullableFilter { + equals: TaskPriorityType + in: [TaskPriorityType!] + notIn: [TaskPriorityType!] + not: TaskPriorityTypeNullableFilter +} + +input BooleanNullableFilter { + equals: Boolean + not: BooleanNullableFilter +} + +input DateTimeNullableFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + not: DateTimeNullableFilter +} + +input TaskWhereUniqueInput { + id: ID +} + +input TaskOrderByInput { + id: OrderDirection + label: OrderDirection + priority: OrderDirection + isComplete: OrderDirection + finishBy: OrderDirection +} + +enum OrderDirection { + asc + desc +} + +input TaskUpdateInput { + label: String + priority: TaskPriorityType + isComplete: Boolean + assignedTo: PersonRelateToOneForUpdateInput + finishBy: String +} + +input PersonRelateToOneForUpdateInput { + create: PersonCreateInput + connect: PersonWhereUniqueInput + disconnect: Boolean +} + +input TaskUpdateArgs { + where: TaskWhereUniqueInput! + data: TaskUpdateInput! +} + +input TaskCreateInput { + label: String + priority: TaskPriorityType + isComplete: Boolean + assignedTo: PersonRelateToOneForCreateInput + finishBy: String +} + +input PersonRelateToOneForCreateInput { + create: PersonCreateInput + connect: PersonWhereUniqueInput +} + +type Person { + id: ID! + name: String + tasks( + where: TaskWhereInput! = {} + orderBy: [TaskOrderByInput!]! = [] + take: Int + skip: Int! = 0 + ): [Task!] + tasksCount(where: TaskWhereInput! = {}): Int +} + +input PersonWhereInput { + AND: [PersonWhereInput!] + OR: [PersonWhereInput!] + NOT: [PersonWhereInput!] + id: IDFilter + name: StringNullableFilter + tasks: TaskManyRelationFilter +} + +input TaskManyRelationFilter { + every: TaskWhereInput + some: TaskWhereInput + none: TaskWhereInput +} + +input PersonWhereUniqueInput { + id: ID +} + +input PersonOrderByInput { + id: OrderDirection + name: OrderDirection +} + +input PersonUpdateInput { + name: String + tasks: TaskRelateToManyForUpdateInput +} + +input TaskRelateToManyForUpdateInput { + disconnect: [TaskWhereUniqueInput!] + set: [TaskWhereUniqueInput!] + create: [TaskCreateInput!] + connect: [TaskWhereUniqueInput!] +} + +input PersonUpdateArgs { + where: PersonWhereUniqueInput! + data: PersonUpdateInput! +} + +input PersonCreateInput { + name: String + tasks: TaskRelateToManyForCreateInput +} + +input TaskRelateToManyForCreateInput { + create: [TaskCreateInput!] + connect: [TaskWhereUniqueInput!] +} + +""" +The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). +""" +scalar JSON + @specifiedBy( + url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" + ) + +type Mutation { + createTask(data: TaskCreateInput!): Task + createTasks(data: [TaskCreateInput!]!): [Task] + updateTask(where: TaskWhereUniqueInput!, data: TaskUpdateInput!): Task + updateTasks(data: [TaskUpdateArgs!]!): [Task] + deleteTask(where: TaskWhereUniqueInput!): Task + deleteTasks(where: [TaskWhereUniqueInput!]!): [Task] + createPerson(data: PersonCreateInput!): Person + createPeople(data: [PersonCreateInput!]!): [Person] + updatePerson(where: PersonWhereUniqueInput!, data: PersonUpdateInput!): Person + updatePeople(data: [PersonUpdateArgs!]!): [Person] + deletePerson(where: PersonWhereUniqueInput!): Person + deletePeople(where: [PersonWhereUniqueInput!]!): [Person] +} + +type Query { + tasks( + where: TaskWhereInput! = {} + orderBy: [TaskOrderByInput!]! = [] + take: Int + skip: Int! = 0 + ): [Task!] + task(where: TaskWhereUniqueInput!): Task + tasksCount(where: TaskWhereInput! = {}): Int + people( + where: PersonWhereInput! = {} + orderBy: [PersonOrderByInput!]! = [] + take: Int + skip: Int! = 0 + ): [Person!] + person(where: PersonWhereUniqueInput!): Person + peopleCount(where: PersonWhereInput! = {}): Int + keystone: KeystoneMeta! +} + +type KeystoneMeta { + adminMeta: KeystoneAdminMeta! +} + +type KeystoneAdminMeta { + enableSignout: Boolean! + enableSessionItem: Boolean! + lists: [KeystoneAdminUIListMeta!]! + list(key: String!): KeystoneAdminUIListMeta +} + +type KeystoneAdminUIListMeta { + key: String! + itemQueryName: String! + listQueryName: String! + hideCreate: Boolean! + hideDelete: Boolean! + path: String! + label: String! + singular: String! + plural: String! + description: String + initialColumns: [String!]! + pageSize: Int! + labelField: String! + fields: [KeystoneAdminUIFieldMeta!]! + initialSort: KeystoneAdminUISort + isHidden: Boolean! +} + +type KeystoneAdminUIFieldMeta { + path: String! + label: String! + isOrderable: Boolean! + fieldMeta: JSON + viewsIndex: Int! + customViewsIndex: Int + createView: KeystoneAdminUIFieldMetaCreateView! + listView: KeystoneAdminUIFieldMetaListView! + itemView(id: ID!): KeystoneAdminUIFieldMetaItemView + search: QueryMode +} + +type KeystoneAdminUIFieldMetaCreateView { + fieldMode: KeystoneAdminUIFieldMetaCreateViewFieldMode! +} + +enum KeystoneAdminUIFieldMetaCreateViewFieldMode { + edit + hidden +} + +type KeystoneAdminUIFieldMetaListView { + fieldMode: KeystoneAdminUIFieldMetaListViewFieldMode! +} + +enum KeystoneAdminUIFieldMetaListViewFieldMode { + read + hidden +} + +type KeystoneAdminUIFieldMetaItemView { + fieldMode: KeystoneAdminUIFieldMetaItemViewFieldMode! +} + +enum KeystoneAdminUIFieldMetaItemViewFieldMode { + edit + read + hidden +} + +enum QueryMode { + default + insensitive +} + +type KeystoneAdminUISort { + field: String! + direction: KeystoneAdminUISortDirection! +} + +enum KeystoneAdminUISortDirection { + ASC + DESC +} diff --git a/tests/test-projects/crud-notifications/schema.prisma b/tests/test-projects/crud-notifications/schema.prisma new file mode 100644 index 00000000000..a1efaced2f7 --- /dev/null +++ b/tests/test-projects/crud-notifications/schema.prisma @@ -0,0 +1,27 @@ +datasource sqlite { + url = env("DATABASE_URL") + provider = "sqlite" +} + +generator client { + provider = "prisma-client-js" + output = "node_modules/.prisma/client" +} + +model Task { + id String @id @default(cuid()) + label String? + priority String? + isComplete Boolean? + assignedTo Person? @relation("Task_assignedTo", fields: [assignedToId], references: [id]) + assignedToId String? @map("assignedTo") + finishBy DateTime? + + @@index([assignedToId]) +} + +model Person { + id String @id @default(cuid()) + name String? + tasks Task[] @relation("Task_assignedTo") +} \ No newline at end of file diff --git a/tests/test-projects/crud-notifications/schema.ts b/tests/test-projects/crud-notifications/schema.ts new file mode 100644 index 00000000000..8cc4f22288c --- /dev/null +++ b/tests/test-projects/crud-notifications/schema.ts @@ -0,0 +1,38 @@ +import { createSchema, list } from '@keystone-next/keystone/schema'; +import { checkbox, relationship, text, timestamp } from '@keystone-next/fields'; +import { select } from '@keystone-next/fields'; + +export const lists = createSchema({ + Task: list({ + access: { + delete: async ({ itemId, context }) => { + const item: any = await context.lists.Task.findOne({ + where: { id: itemId }, + query: 'label', + }); + const matchString = item.label.replace(/([\d])+/g, '').trim(); + return !['do not delete', 'do not destroy', 'do not kill'].includes(matchString); + }, + }, + fields: { + label: text({ isRequired: true }), + priority: select({ + dataType: 'enum', + options: [ + { label: 'Low', value: 'low' }, + { label: 'Medium', value: 'medium' }, + { label: 'High', value: 'high' }, + ], + }), + isComplete: checkbox(), + assignedTo: relationship({ ref: 'Person.tasks', many: false }), + finishBy: timestamp(), + }, + }), + Person: list({ + fields: { + name: text({ isRequired: true }), + tasks: relationship({ ref: 'Task.assignedTo', many: true }), + }, + }), +}); diff --git a/yarn.lock b/yarn.lock index 034e47534e3..0bebeff4aac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,24 +2,23 @@ # yarn lockfile v1 -"@apollo/client@3.3.21", "@apollo/client@^3.1.3": - version "3.3.21" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.3.21.tgz#2862baa4e1ced8c5e89ebe6fc52877fc64a726aa" - integrity sha512-RAmZReFuKCKx0Rs5C0nVJwKomAHUHn+gGP/YvbEsXQWu0sXoncEUZa71UqlfCPVXa/0MkYOIbCXSQdOcuRrHgw== +"@apollo/client@^3.1.3", "@apollo/client@^3.4.8": + version "3.4.8" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.4.8.tgz#66d06dc1784d07d46731b3bda546046f8c280b74" + integrity sha512-/cNqTSwc2Dw8q6FDDjdd30+yvhP7rI0Fvl3Hbro0lTtFuhzkevfNyQaI2jAiOrjU6Jc0RbanxULaNrX7UmvjSQ== dependencies: "@graphql-typed-document-node/core" "^3.0.0" - "@types/zen-observable" "^0.8.0" "@wry/context" "^0.6.0" "@wry/equality" "^0.5.0" - fast-json-stable-stringify "^2.0.0" - graphql-tag "^2.12.0" + "@wry/trie" "^0.3.0" + graphql-tag "^2.12.3" hoist-non-react-statics "^3.3.2" - optimism "^0.16.0" + optimism "^0.16.1" prop-types "^15.7.2" symbol-observable "^4.0.0" - ts-invariant "^0.8.0" - tslib "^1.10.0" - zen-observable "^0.8.14" + ts-invariant "^0.9.0" + tslib "^2.3.0" + zen-observable-ts "^1.1.0" "@apollo/protobufjs@1.2.2": version "1.2.2" @@ -79,25 +78,25 @@ dependencies: "@babel/highlight" "^7.14.5" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7", "@babel/compat-data@^7.14.9": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.9.tgz#ac7996ceaafcf8f410119c8af0d1db4cf914a210" - integrity sha512-p3QjZmMGHDGdpcwEYYWu7i7oJShJvtgMjJeb0W95PPhSm++3lm8YXYOh45Y6iCN9PkZLTZ7CIX5nFrp7pw7TXw== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.7", "@babel/compat-data@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" + integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.14.8", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.7.7": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010" - integrity sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q== +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.15.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.7.7": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.0.tgz#749e57c68778b73ad8082775561f67f5196aafa8" + integrity sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw== dependencies: "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.8" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.8" + "@babel/generator" "^7.15.0" + "@babel/helper-compilation-targets" "^7.15.0" + "@babel/helper-module-transforms" "^7.15.0" "@babel/helpers" "^7.14.8" - "@babel/parser" "^7.14.8" + "@babel/parser" "^7.15.0" "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.8" - "@babel/types" "^7.14.8" + "@babel/traverse" "^7.15.0" + "@babel/types" "^7.15.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -105,12 +104,12 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.14.8", "@babel/generator@^7.14.9", "@babel/generator@^7.7.2": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.9.tgz#23b19c597d38b4f7dc2e3fe42a69c88d9ecfaa16" - integrity sha512-4yoHbhDYzFa0GLfCzLp5GxH7vPPMAHdZjyE7M/OajM9037zhx0rf+iNsJwp4PT0MSFpwjG7BsHEbPkBQpZ6cYA== +"@babel/generator@^7.15.0", "@babel/generator@^7.7.2": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.0.tgz#a7d0c172e0d814974bad5aa77ace543b97917f15" + integrity sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ== dependencies: - "@babel/types" "^7.14.9" + "@babel/types" "^7.15.0" jsesc "^2.5.1" source-map "^0.5.0" @@ -129,26 +128,26 @@ "@babel/helper-explode-assignable-expression" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" - integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5", "@babel/helper-compilation-targets@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz#973df8cbd025515f3ff25db0c05efc704fa79818" + integrity sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A== dependencies: - "@babel/compat-data" "^7.14.5" + "@babel/compat-data" "^7.15.0" "@babel/helper-validator-option" "^7.14.5" browserslist "^4.16.6" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.14.6": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.8.tgz#a6f8c3de208b1e5629424a9a63567f56501955fc" - integrity sha512-bpYvH8zJBWzeqi1o+co8qOrw+EXzQ/0c74gVmY205AWXy9nifHrOg77y+1zwxX5lXE7Icq4sPlSQ4O2kWBrteQ== +"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.0.tgz#c9a137a4d137b2d0e2c649acf536d7ba1a76c0f7" + integrity sha512-MdmDXgvTIi4heDVX/e9EFfeGpugqm9fobBVg/iioE8kueXrOHdRDe36FAY7SnE9xXLVeYCoJR/gdrBEIHRC83Q== dependencies: "@babel/helper-annotate-as-pure" "^7.14.5" "@babel/helper-function-name" "^7.14.5" - "@babel/helper-member-expression-to-functions" "^7.14.7" + "@babel/helper-member-expression-to-functions" "^7.15.0" "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-replace-supers" "^7.15.0" "@babel/helper-split-export-declaration" "^7.14.5" "@babel/helper-create-regexp-features-plugin@^7.14.5": @@ -203,12 +202,12 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-member-expression-to-functions@^7.14.5", "@babel/helper-member-expression-to-functions@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" - integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA== +"@babel/helper-member-expression-to-functions@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz#0ddaf5299c8179f27f37327936553e9bba60990b" + integrity sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.15.0" "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": version "7.14.5" @@ -217,19 +216,19 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.14.8": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz#d4279f7e3fd5f4d5d342d833af36d4dd87d7dc49" - integrity sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA== +"@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz#679275581ea056373eddbe360e1419ef23783b08" + integrity sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg== dependencies: "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-replace-supers" "^7.15.0" "@babel/helper-simple-access" "^7.14.8" "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.8" + "@babel/helper-validator-identifier" "^7.14.9" "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.8" - "@babel/types" "^7.14.8" + "@babel/traverse" "^7.15.0" + "@babel/types" "^7.15.0" "@babel/helper-optimise-call-expression@^7.14.5": version "7.14.5" @@ -252,17 +251,17 @@ "@babel/helper-wrap-function" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helper-replace-supers@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" - integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== +"@babel/helper-replace-supers@^7.14.5", "@babel/helper-replace-supers@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz#ace07708f5bf746bf2e6ba99572cce79b5d4e7f4" + integrity sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA== dependencies: - "@babel/helper-member-expression-to-functions" "^7.14.5" + "@babel/helper-member-expression-to-functions" "^7.15.0" "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/traverse" "^7.15.0" + "@babel/types" "^7.15.0" -"@babel/helper-simple-access@^7.14.5", "@babel/helper-simple-access@^7.14.8": +"@babel/helper-simple-access@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924" integrity sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg== @@ -283,7 +282,7 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8", "@babel/helper-validator-identifier@^7.14.9": +"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9": version "7.14.9" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g== @@ -304,13 +303,13 @@ "@babel/types" "^7.14.5" "@babel/helpers@^7.14.8": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77" - integrity sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw== + version "7.15.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.15.3.tgz#c96838b752b95dcd525b4e741ed40bb1dc2a1357" + integrity sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g== dependencies: "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.8" - "@babel/types" "^7.14.8" + "@babel/traverse" "^7.15.0" + "@babel/types" "^7.15.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": version "7.14.5" @@ -321,10 +320,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.5", "@babel/parser@^7.14.8", "@babel/parser@^7.14.9", "@babel/parser@^7.7.2": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.9.tgz#596c1ad67608070058ebf8df50c1eaf65db895a4" - integrity sha512-RdUTOseXJ8POjjOeEBEvNMIZU/nm4yu2rufRkcibzkkg7DmQvXU8v3M4Xk9G7uuI86CDGkKcuDWgioqZm+mScQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.5", "@babel/parser@^7.15.0", "@babel/parser@^7.7.2": + version "7.15.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.3.tgz#3416d9bea748052cfcb63dbcc27368105b1ed862" + integrity sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA== "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5": version "7.14.5" @@ -519,7 +518,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.14.5": +"@babel/plugin-syntax-jsx@7.14.5", "@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201" integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw== @@ -613,9 +612,9 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-block-scoping@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz#8cc63e61e50f42e078e6f09be775a75f23ef9939" - integrity sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw== + version "7.15.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.15.3.tgz#94c81a6e2fc230bcce6ef537ac96a1e4d2b3afaf" + integrity sha512-nBAzfZwZb4DkaGtOes1Up1nOAp9TDRRFw4XBzBBSG9QK7KVFmYzgj9o9sbPv7TX5ofL4Auq4wZnxCoPnI/lz2Q== dependencies: "@babel/helper-plugin-utils" "^7.14.5" @@ -707,14 +706,14 @@ "@babel/helper-plugin-utils" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.14.5", "@babel/plugin-transform-modules-commonjs@^7.7.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97" - integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A== +"@babel/plugin-transform-modules-commonjs@^7.15.0", "@babel/plugin-transform-modules-commonjs@^7.7.5": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.0.tgz#3305896e5835f953b5cdb363acd9e8c2219a5281" + integrity sha512-3H/R9s8cXcOGE8kgMlmjYYC9nqr5ELiPkJn4q0mypBrjhYQoc+5/Maq69vV4xRPWnkzZuwJPf5rArxpB/35Cig== dependencies: - "@babel/helper-module-transforms" "^7.14.5" + "@babel/helper-module-transforms" "^7.15.0" "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-simple-access" "^7.14.5" + "@babel/helper-simple-access" "^7.14.8" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.14.5": @@ -773,9 +772,9 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-react-display-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.5.tgz#baa92d15c4570411301a85a74c13534873885b65" - integrity sha512-07aqY1ChoPgIxsuDviptRpVkWCSbXWmzQqcgy65C6YSFOfPFvb/DX3bBRHh7pCd/PMEEYHYWUTSVkCbkVainYQ== + version "7.15.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.15.1.tgz#6aaac6099f1fcf6589d35ae6be1b6e10c8c602b9" + integrity sha512-yQZ/i/pUCJAHI/LbtZr413S3VT26qNrEm0M5RRxQJA947/YNYwbZbBaXGDrq6CG5QsZycI1VIP6d7pQaBfP+8Q== dependencies: "@babel/helper-plugin-utils" "^7.14.5" @@ -819,10 +818,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-runtime@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz#30491dad49c6059f8f8fa5ee8896a0089e987523" - integrity sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg== +"@babel/plugin-transform-runtime@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.0.tgz#d3aa650d11678ca76ce294071fda53d7804183b3" + integrity sha512-sfHYkLGjhzWTq6xsuQ01oEsUYjkHRux9fW1iUA68dC7Qd8BS1Unq4aZ8itmQp95zUzIcyR2EbNMTzAicFj+guw== dependencies: "@babel/helper-module-imports" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" @@ -867,12 +866,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-typescript@^7.14.5": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.6.tgz#6e9c2d98da2507ebe0a883b100cde3c7279df36c" - integrity sha512-XlTdBq7Awr4FYIzqhmYY80WN0V0azF74DMPyFqVHBvf81ZUgc4X7ZOpx6O8eLDK6iM5cCQzeyJw0ynTaefixRA== +"@babel/plugin-transform-typescript@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.15.0.tgz#553f230b9d5385018716586fc48db10dd228eb7e" + integrity sha512-WIIEazmngMEEHDaPTx0IZY48SaAmjVWe3TRSX7cmJXn0bEv9midFzAjxiruOWYIVf5iQ10vFx7ASDpgEO08L5w== dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.6" + "@babel/helper-create-class-features-plugin" "^7.15.0" "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript" "^7.14.5" @@ -891,13 +890,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/preset-env@^7.14.9": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.9.tgz#4a3bbbd745f20e9121d5925170bef040a21b7819" - integrity sha512-BV5JvCwBDebkyh67bPKBYVCC6gGw0MCzU6HfKe5Pm3upFpPVqiC/hB33zkOe0tVdAzaMywah0LSXQeD9v/BYdQ== +"@babel/preset-env@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.15.0.tgz#e2165bf16594c9c05e52517a194bf6187d6fe464" + integrity sha512-FhEpCNFCcWW3iZLg0L2NPE9UerdtsCR6ZcsGHUX6Om6kbCQeL5QZDqFDmeNHC6/fy6UH3jEge7K4qG5uC9In0Q== dependencies: - "@babel/compat-data" "^7.14.9" - "@babel/helper-compilation-targets" "^7.14.5" + "@babel/compat-data" "^7.15.0" + "@babel/helper-compilation-targets" "^7.15.0" "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-validator-option" "^7.14.5" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.14.5" @@ -945,7 +944,7 @@ "@babel/plugin-transform-literals" "^7.14.5" "@babel/plugin-transform-member-expression-literals" "^7.14.5" "@babel/plugin-transform-modules-amd" "^7.14.5" - "@babel/plugin-transform-modules-commonjs" "^7.14.5" + "@babel/plugin-transform-modules-commonjs" "^7.15.0" "@babel/plugin-transform-modules-systemjs" "^7.14.5" "@babel/plugin-transform-modules-umd" "^7.14.5" "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.9" @@ -963,7 +962,7 @@ "@babel/plugin-transform-unicode-escapes" "^7.14.5" "@babel/plugin-transform-unicode-regex" "^7.14.5" "@babel/preset-modules" "^0.1.4" - "@babel/types" "^7.14.9" + "@babel/types" "^7.15.0" babel-plugin-polyfill-corejs2 "^0.2.2" babel-plugin-polyfill-corejs3 "^0.2.2" babel-plugin-polyfill-regenerator "^0.2.2" @@ -993,19 +992,19 @@ "@babel/plugin-transform-react-jsx-development" "^7.14.5" "@babel/plugin-transform-react-pure-annotations" "^7.14.5" -"@babel/preset-typescript@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.14.5.tgz#aa98de119cf9852b79511f19e7f44a2d379bcce0" - integrity sha512-u4zO6CdbRKbS9TypMqrlGH7sd2TAJppZwn3c/ZRLeO/wGsbddxgbPDUZVNrie3JWYLQ9vpineKlsrWFvO6Pwkw== +"@babel/preset-typescript@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.15.0.tgz#e8fca638a1a0f64f14e1119f7fe4500277840945" + integrity sha512-lt0Y/8V3y06Wq/8H/u0WakrqciZ7Fz7mwPDHWUJAXlABL5hiUG42BNlRXiELNjeWjO5rWmnNKlx+yzJvxezHow== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-transform-typescript" "^7.14.5" + "@babel/plugin-transform-typescript" "^7.15.0" "@babel/runtime-corejs3@^7.10.2": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.14.9.tgz#fb21b1cf11650dcb8fcf4de2e6b3b8cf411da3f3" - integrity sha512-64RiH2ON4/y8qYtoa8rUiyam/tUVyGqRyNYhe+vCRGmjnV4bUlZvY+mwd0RrmLoCpJpdq3RsrNqKb7SJdw/4kw== + version "7.15.3" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.15.3.tgz#28754263988198f2a928c09733ade2fb4d28089d" + integrity sha512-30A3lP+sRL6ml8uhoJSs+8jwpKzbw8CqBvDc1laeptxPm5FahumJxirigcbD2qTs71Sonvj1cyZB0OKGAmxQ+A== dependencies: core-js-pure "^3.16.0" regenerator-runtime "^0.13.4" @@ -1017,10 +1016,10 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" - integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.15.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b" + integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA== dependencies: regenerator-runtime "^0.13.4" @@ -1033,21 +1032,29 @@ "@babel/parser" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.14.8", "@babel/traverse@^7.7.2": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.9.tgz#016126b331210bf06fff29d52971eef8383e556f" - integrity sha512-bldh6dtB49L8q9bUyB7bC20UKgU+EFDwKJylwl234Kv+ySZeMD31Xeht6URyueQ6LrRRpF2tmkfcZooZR9/e8g== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.15.0", "@babel/traverse@^7.7.2": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.0.tgz#4cca838fd1b2a03283c1f38e141f639d60b3fc98" + integrity sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw== dependencies: "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.9" + "@babel/generator" "^7.15.0" "@babel/helper-function-name" "^7.14.5" "@babel/helper-hoist-variables" "^7.14.5" "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/parser" "^7.14.9" - "@babel/types" "^7.14.9" + "@babel/parser" "^7.15.0" + "@babel/types" "^7.15.0" debug "^4.1.0" globals "^11.1.0" +"@babel/types@7.15.0", "@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.14.9", "@babel/types@^7.15.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.0.tgz#61af11f2286c4e9c69ca8deb5f4375a73c72dcbd" + integrity sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ== + dependencies: + "@babel/helper-validator-identifier" "^7.14.9" + to-fast-properties "^2.0.0" + "@babel/types@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" @@ -1057,14 +1064,6 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.14.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.9.tgz#f2b19c3f2f77c5708d67fe8f6046e9cea2b5036d" - integrity sha512-u0bLTnv3DFHeaQLYzb7oRJ1JHr1sv/SYDM7JSqHFFLwXG1wTZRughxFI5NCP8qBEo1rVVsn7Yg2Lvw49nne/Ow== - dependencies: - "@babel/helper-validator-identifier" "^7.14.9" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1282,9 +1281,9 @@ prettier "^1.19.1" "@corex/deepmerge@^2.6.20": - version "2.6.20" - resolved "https://registry.yarnpkg.com/@corex/deepmerge/-/deepmerge-2.6.20.tgz#a72d1cb5a101fd11160815b96f499a588362ba5b" - integrity sha512-oZZxwDtV0bf8VPcSIhZPvdBFUkVIC8zRblUjrrVAsbGRiqUuZJfoXw2M6NDiIXWcUCfOqbkFND6Yf3b9ej9AjA== + version "2.6.34" + resolved "https://registry.yarnpkg.com/@corex/deepmerge/-/deepmerge-2.6.34.tgz#8dd084f2bcc9cf54f6b1210a1aebd25206de2a84" + integrity sha512-5l3bQRGOoCJ1nYTxEaOW/MRuwNDq32KYatWO5rwOlOzxY4beVCrxDBaDBX5wpDn0PYm0QAul/vAC9GDHShEbTw== "@cypress/listr-verbose-renderer@^0.4.1": version "0.4.1" @@ -1416,15 +1415,15 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50" integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== -"@emotion/react@^11.1.1", "@emotion/react@^11.4.0": - version "11.4.0" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.4.0.tgz#2465ad7b073a691409b88dfd96dc17097ddad9b7" - integrity sha512-4XklWsl9BdtatLoJpSjusXhpKv9YVteYKh9hPKP1Sxl+mswEFoUe0WtmtWjxEjkA51DQ2QRMCNOvKcSlCQ7ivg== +"@emotion/react@^11.1.1", "@emotion/react@^11.4.1": + version "11.4.1" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.4.1.tgz#a1b0b767b5bad57515ffb0cad9349614d27f4d57" + integrity sha512-pRegcsuGYj4FCdZN6j5vqCALkNytdrKw3TZMekTzNXixRg4wkLsU5QEaBG5LC6l01Vppxlp7FE3aTHpIG5phLg== dependencies: "@babel/runtime" "^7.13.10" "@emotion/cache" "^11.4.0" "@emotion/serialize" "^1.0.2" - "@emotion/sheet" "^1.0.1" + "@emotion/sheet" "^1.0.2" "@emotion/utils" "^1.0.0" "@emotion/weak-memoize" "^0.2.5" hoist-non-react-statics "^3.3.1" @@ -1466,10 +1465,10 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== -"@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.1.tgz#245f54abb02dfd82326e28689f34c27aa9b2a698" - integrity sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g== +"@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.2.tgz#1d9ffde531714ba28e62dac6a996a8b1089719d0" + integrity sha512-QQPB1B70JEVUHuNtzjHftMGv6eC3Y9wqavyarj4x4lg47RACkeSfNo5pxIOKizwS9AEFLohsqoaxGQj4p0vSIw== "@emotion/stylis@0.8.5": version "0.8.5" @@ -1511,36 +1510,35 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@graphql-tools/merge@6.2.16", "@graphql-tools/merge@^6.2.16": - version "6.2.16" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-6.2.16.tgz#5dd5f582075c21ef4d74ef84dfcc4e8d0c2db186" - integrity sha512-KjZ1pppzKcr2Uspgb53p8uw5yhWVuGIL+sEroar7vLsClSsuiGib8OKVICAGWjC9wrCxGaL9SjJGavfXpJMQIg== +"@graphql-tools/merge@8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.0.1.tgz#4cb94998e7c61abc6ebec54edca5c19f06c3330d" + integrity sha512-YAozogbjC2Oun+UcwG0LZFumhlCiHBmqe68OIf7bqtBdp4pbPAiVuK/J9oJqRVJmzvUqugo6RD9zz1qDTKZaiQ== dependencies: - "@graphql-tools/schema" "^8.0.1" - "@graphql-tools/utils" "8.0.1" + "@graphql-tools/utils" "8.1.1" tslib "~2.3.0" -"@graphql-tools/schema@^8.0.1": - version "8.0.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.0.1.tgz#178b734f3784e632a76bdd144ffa8a32169a6f4a" - integrity sha512-QG2HGLJjmsNc1wcj+rwZTEArgfMp7rsrb8iVq4P8ce1mDYAt6kRV6bLyPVb9q/j8Ik2zBc/B/Y1jPsnAVUHwdA== +"@graphql-tools/schema@^8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.1.1.tgz#5de95a8f6cedaf91fa2f9e9e7fa9fae1d27a7c4f" + integrity sha512-u+0kxPtuP+GcKnGNt459Ob7iIpzesIJeJTmPPailaG7ZhB5hkXIizl4uHrzEIAh2Ja1P/VA8sEBYpu1N0n6Mmg== dependencies: - "@graphql-tools/merge" "6.2.16" - "@graphql-tools/utils" "8.0.1" + "@graphql-tools/merge" "8.0.1" + "@graphql-tools/utils" "8.1.1" tslib "~2.3.0" value-or-promise "1.0.10" -"@graphql-tools/utils@8.0.1": - version "8.0.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.0.1.tgz#bf09e8d6d17c4a0830f0fbf2c69dd725de19058c" - integrity sha512-gjQk6sht4b0/hcG+QEVxfMyO8bn5tuU1nIOVhQ4whgFaUmrnb3hx2mwzz1EJzfIOAuHKE8tY0lu6jt3bGTD4yg== +"@graphql-tools/utils@8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.1.1.tgz#2ef056a1d6e1e909085e1115d3bb48f890c2a2b6" + integrity sha512-QbFNoBmBiZ+ej4y6mOv8Ba4lNhcrTEKXAhZ0f74AhdEXi7b9xbGUH/slO5JaSyp85sGQYIPmxjRPpXBjLklbmw== dependencies: tslib "~2.3.0" -"@graphql-ts/schema@0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@graphql-ts/schema/-/schema-0.1.2.tgz#71b04cc88ff61f5c19a81ac70f878b1e36f617da" - integrity sha512-ZP4Nj70B18q1Y7B4WHeJR5gMyoH8oX9umnpmT9gu6kUD9rmTZfqHE9cGHRisw+ZOlWBnx6rPrx0BmUROFQFORA== +"@graphql-ts/schema@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@graphql-ts/schema/-/schema-0.2.0.tgz#1fc11a985cd5b0534de6616abcf05b0b493b8252" + integrity sha512-4hLQW5RfJkSkqia3Tl/LRVjsIGziTN/fHsUEuSIg1quD0y3pPpQxisDAIt4nkevY53q+JuGSVG1isJz+v8Ym/g== dependencies: "@babel/runtime" "^7.9.2" @@ -1794,17 +1792,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^27.0.6": version "27.0.6" resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.6.tgz#9a992bc517e0c49f035938b8549719c2de40706b" @@ -1914,21 +1901,36 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@napi-rs/triples@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@napi-rs/triples/-/triples-1.0.3.tgz#76d6d0c3f4d16013c61e45dfca5ff1e6c31ae53c" + integrity sha512-jDJTpta+P4p1NZTFVLHJ/TLFVYVcOqv6l8xwOeBKNPMgY/zDYH/YH7SJbvrr/h1RcS9GzbPcLKGzpuK9cV56UA== + "@next/env@10.2.3": version "10.2.3" resolved "https://registry.yarnpkg.com/@next/env/-/env-10.2.3.tgz#ede3bbe68cec9939c37168ea2077f9adbc68334e" integrity sha512-uBOjRBjsWC4C8X3DfmWWP6ekwLnf2JCCwQX9KVnJtJkqfDsv1yQPakdOEwvJzXQc3JC/v5KKffYPVmV2wHXCgQ== -"@next/mdx@^10.2.3": - version "10.2.3" - resolved "https://registry.yarnpkg.com/@next/mdx/-/mdx-10.2.3.tgz#226d25530e4b98af3a200be3e2616beebf81b56c" - integrity sha512-hseekptFqOCxLbdaNDS/yelaG2Q2uaNDilnRjq8Uv/LWHuZ9F2cp7ndwTolW9acJsbDedamKRMgdw4V2Fz0pUA== +"@next/env@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@next/env/-/env-11.1.0.tgz#cae83d8e0a65aa9f2af3368f8269ffd9d911746a" + integrity sha512-zPJkMFRenSf7BLlVee8987G0qQXAhxy7k+Lb/5hLAGkPVHAHm+oFFeL+2ipbI2KTEFlazdmGY0M+AlLQn7pWaw== + +"@next/mdx@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@next/mdx/-/mdx-11.1.0.tgz#79d6d20e0906e81c07aed0f8286d58530d051be6" + integrity sha512-X+NwSByGr9HuPkGs1nrzhmJ5qqQbv6CpEA3dJKEYHaDe7YzEUCM00Y7abxOzowXhUXeDdAHJ7IeIV4GZMhdcpg== "@next/polyfill-module@10.2.3": version "10.2.3" resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.2.3.tgz#5a29f50c3ce3a56b8268d3b8331c691d8039467a" integrity sha512-OkeY4cLhzfYbXxM4fd+6V4s5pTPuyfKSlavItfNRA6PpS7t1/R6YjO7S7rB8tu1pbTGuDHGIdE1ioDv15bAbDQ== +"@next/polyfill-module@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-11.1.0.tgz#ee6b9117a1f9bb137479dfa51d5a9e38e066a62f" + integrity sha512-64EgW8SzJRQls2yJ5DkuljRxgE24o2kYtX/ghTkPUJYsfidHMWzQGwg26IgRbb/uHqTd1G0W5UkKag+Nt8TWaQ== + "@next/react-dev-overlay@10.2.3": version "10.2.3" resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.2.3.tgz#95313d10a8848f6c7b9e31ae3bd2a3627d136841" @@ -1946,11 +1948,40 @@ stacktrace-parser "0.1.10" strip-ansi "6.0.0" +"@next/react-dev-overlay@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-11.1.0.tgz#8d4e8020a4cbdacbca431a0bf40c4d28187083af" + integrity sha512-h+ry0sTk1W3mJw+TwEf91aqLbBJ5oqAsxfx+QryqEItNtfW6zLSSjxkyTYTqX8DkgSssQQutQfATkzBVgOR+qQ== + dependencies: + "@babel/code-frame" "7.12.11" + anser "1.4.9" + chalk "4.0.0" + classnames "2.2.6" + css.escape "1.5.1" + data-uri-to-buffer "3.0.1" + platform "1.3.6" + shell-quote "1.7.2" + source-map "0.8.0-beta.0" + stacktrace-parser "0.1.10" + strip-ansi "6.0.0" + "@next/react-refresh-utils@10.2.3": version "10.2.3" resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.2.3.tgz#2f3e42fe6680798f276e3621345c2886b231348b" integrity sha512-qtBF56vPC6d6a8p7LYd0iRjW89fhY80kAIzmj+VonvIGjK/nymBjcFUhbKiMFqlhsarCksnhwX+Zmn95Dw9qvA== +"@next/react-refresh-utils@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-11.1.0.tgz#60c3c7b127a5dab8b0a2889a7dcf8a90d2c4e592" + integrity sha512-g5DtFTpLTGa36iy9DuZawtJeitI11gysFGKPQQqy+mNbSFazguArcJ10gAYFlbqpIi4boUamWNI5mAoSPx3kog== + +"@node-rs/helper@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@node-rs/helper/-/helper-1.2.1.tgz#e079b05f21ff4329d82c4e1f71c0290e4ecdc70c" + integrity sha512-R5wEmm8nbuQU0YGGmYVjEc0OHtYsuXdpRG+Ut/3wZ9XAvQWyThN08bTh2cBJgoZxHQUPtvRfeQuxcAgLuiBISg== + dependencies: + "@napi-rs/triples" "^1.0.3" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1989,10 +2020,10 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.14.0.tgz#c67fc20a4d891447ca1a855d7d70fa79a3533001" integrity sha512-sDOAZcYwynHFTbLo6n8kIbLiVF3a3BLkrmehJUyEbT9F+Smbi47kLGS2gG2g0fjBLR/Lr1InPD7kXL7FaTqEkw== -"@popperjs/core@^2.9.2": - version "2.9.2" - resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353" - integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q== +"@popperjs/core@^2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.3.tgz#8b68da1ebd7fc603999cf6ebee34a4899a14b88e" + integrity sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ== "@preconstruct/cli@2.1.0": version "2.1.0" @@ -2059,20 +2090,12 @@ resolved "https://registry.yarnpkg.com/@preconstruct/next/-/next-3.0.0.tgz#71781cbaecd011f43e456a149817094a43e4755f" integrity sha512-G90cyJX9w4Zr3Bt/j2fURgDhsJb5+agqf4YUgrvDe3Dyvbbssy9a6d0tzLH0ehfa2Osxw/EEhQb+W4X+v/x06A== -"@prisma/client@2.28.0": - version "2.28.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.28.0.tgz#6cd91a3c2757814a9454ea23d7e33cd2ff3952db" - integrity sha512-iwdxpy0Nz8N40MnhdlRvhZOBk8+GawpEsY5FU8Tfw1k9rvIeTAi+wBHrqhY8bXq6pneZkzrdQ1Hj3tqkrbRmoQ== - dependencies: - "@prisma/engines-version" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - -"@prisma/debug@2.27.0": - version "2.27.0" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-2.27.0.tgz#ae4c565813011c6e6d29fbe948672f39bc4899d3" - integrity sha512-Et01S4RoLnQP9u547dCp74aSnLWTu0akfBCzF4zQZsbEdw7wXLttrcvbcYKr+KhpfF5Xu291UP/Kaxg0aj8o4w== +"@prisma/client@2.29.1": + version "2.29.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.29.1.tgz#a7b91c9644800de4e00b2f7c3789ff4bae42b3d6" + integrity sha512-GhieSvHGPIV5IwRYIkJ4FrGSNfX18lPhFtlyVWxhvX0ocdy8oTnjNZVTFgGxB6qVmJIUpH1HsckAzIoAX689IA== dependencies: - debug "4.3.2" - ms "^2.1.3" + "@prisma/engines-version" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" "@prisma/debug@2.28.0": version "2.28.0" @@ -2082,41 +2105,50 @@ debug "4.3.2" ms "^2.1.3" -"@prisma/engine-core@2.28.0": - version "2.28.0" - resolved "https://registry.yarnpkg.com/@prisma/engine-core/-/engine-core-2.28.0.tgz#9e1dcd11b88c55e48af292c272258a10cba8391f" - integrity sha512-J8lbA85PFpcwm2+u6bQHLomnbsrEnsJkq1PFrplthvJDlOK/tOl9CDuxU2IriJ9KHd+0Y0qaqcGmO+xXfFu3HA== +"@prisma/debug@2.29.1": + version "2.29.1" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-2.29.1.tgz#f5a6b480ea063844c6b1b40974402be8bda05fad" + integrity sha512-8OAh4ozVCvlcZU1HaP7QTWIA6Aqzs98nYgTYrxjDLqXJcytIvpIjHxfgqKhuPQ8MTkUm9cI5TJGswwcjJt0/0g== dependencies: - "@prisma/debug" "2.28.0" - "@prisma/engines" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - "@prisma/generator-helper" "2.28.0" - "@prisma/get-platform" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - chalk "^4.0.0" - execa "^5.0.0" - get-stream "^6.0.0" - indent-string "^4.0.0" - new-github-issue-url "^0.2.1" - p-retry "^4.2.0" - terminal-link "^2.1.1" + "@types/debug" "4.1.7" + debug "4.3.2" + ms "2.1.3" + +"@prisma/engine-core@2.29.1": + version "2.29.1" + resolved "https://registry.yarnpkg.com/@prisma/engine-core/-/engine-core-2.29.1.tgz#9fdb1fddea6d650a97ae1d86e8264d4b95dbe364" + integrity sha512-vbZNry916sErWIagF62kASy665Pe9xDYdOQuJ9Njg90sApg4qHPAmDqRC5cf4wQzWOnhnenmWC7AjUfQKrrFiQ== + dependencies: + "@prisma/debug" "2.29.1" + "@prisma/engines" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + "@prisma/generator-helper" "2.29.1" + "@prisma/get-platform" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + chalk "4.1.2" + execa "5.1.1" + get-stream "6.0.1" + indent-string "4.0.0" + new-github-issue-url "0.2.1" + p-retry "4.6.1" + terminal-link "2.1.1" undici "3.3.6" -"@prisma/engines-version@2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27": - version "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27.tgz#6dbccdce64f792dbc21c99daef80ec03020c110d" - integrity sha512-BWTvF1mGxjG8EtG215uhxdeW5Uf5aiH4xhfzcFPFC3Ux5BdEM1uEBrLIixX67mI+ZNhqNZSBPf0DSf2I1IsaZw== +"@prisma/engines-version@2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a": + version "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a.tgz#96db92f09714d4dd2a5e21054c28bd1c0820fa77" + integrity sha512-BU1DNNDhdzqjHtycpUzDrU8+jf6ZY+fbXvCV/rbqG+0JifljlIo4vbkHDMg97gBi1Do8pTLZGlTH16FlniKgAg== -"@prisma/engines@2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27": - version "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27.tgz#5a563ead03405fc6ccb33fe48f8b3a8712be391e" - integrity sha512-r3/EnwKjbu2qz13I98hPQQdeFrOEcwdjlrB9CcoSoqRCjSHLnpdVMUvRfYuRKIoEF7p941R7/Fov0/CxOLF/MQ== +"@prisma/engines@2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a": + version "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a.tgz#0a44a6dcbee7e0a2850ea086675a8a4f4d627f9d" + integrity sha512-cgEoGK3dmKZkMp/sRbL8TsuVS50rHXYBHk2NY18DPUGr5//4ICno46EjzlayqAFVak8J6RtWZEs+8tE8j8frAQ== -"@prisma/fetch-engine@2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27": - version "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27.tgz#41de37f756e59dab8d50ecb5c7cdd521da19a689" - integrity sha512-o30L0+IrnK8ncT5qypnMW0AagpdTGCDL9eitDp59PA4KTPcfyusgcjcIgPm0qfcsiOrbvriBYCDmjXhNKNfaMA== +"@prisma/fetch-engine@2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a": + version "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a.tgz#de40f3bd761f3da1f2e0e0af027514c845f58264" + integrity sha512-HsTHffo2xg0rZchdqWJHDvl7JaOi2U1rDockKYAYPf3grGZ14AnJ/ZVP292xMm0IiGLkb5YE3qkI5SKoGEUqMw== dependencies: - "@prisma/debug" "2.27.0" - "@prisma/get-platform" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" + "@prisma/debug" "2.28.0" + "@prisma/get-platform" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" chalk "^4.0.0" execa "^5.0.0" find-cache-dir "^3.3.1" @@ -2133,83 +2165,83 @@ temp-dir "^2.0.0" tempy "^1.0.0" -"@prisma/generator-helper@2.28.0": - version "2.28.0" - resolved "https://registry.yarnpkg.com/@prisma/generator-helper/-/generator-helper-2.28.0.tgz#5444f3c5bf8334f234ba25ca3c2360408e7a1495" - integrity sha512-KaDFroGSVfDJBQRoKdXmDvP93Ukj3gI9DVjtABlQFl5FII4VCtsOix76VK+h/Hej4p0yrU6LCCU24Jmw4Enksg== - dependencies: - "@prisma/debug" "2.28.0" - "@types/cross-spawn" "^6.0.1" - chalk "^4.0.0" - cross-spawn "^7.0.2" - -"@prisma/get-platform@2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27": - version "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27.tgz#52904ae6b982fa162202220f7e03554daeacabca" - integrity sha512-OdTubLy4lVRYNvF3N9eODWxLxUhgh2oapDVvdMO3YmHQSeYQzzHHhYHBKoUY9zpCCAbAPBik+YIXgimJp3lqQQ== +"@prisma/generator-helper@2.29.1": + version "2.29.1" + resolved "https://registry.yarnpkg.com/@prisma/generator-helper/-/generator-helper-2.29.1.tgz#7bd984f649ba830d11fdd514f8781c92a2782e41" + integrity sha512-rba/mfxv1JtbAm51yXipYY7DVYB4L2DVVnnXOvSAhAqDLUVylT68WxLXQKiSBxNAlks5ngD2DeaH6qpGi+XnYw== dependencies: - "@prisma/debug" "2.27.0" + "@prisma/debug" "2.29.1" + "@types/cross-spawn" "6.0.2" + chalk "4.1.2" + cross-spawn "7.0.3" -"@prisma/migrate@2.28.0": - version "2.28.0" - resolved "https://registry.yarnpkg.com/@prisma/migrate/-/migrate-2.28.0.tgz#7923a463cb58ac1eb32d93cf0a828da3744c81a2" - integrity sha512-vWE8OSKDPIlfW8bJU98xm11s8nKtve77M7N8oJlktYL8pENHnSYoWcDynq/RZcZlf0Zb74OOmOJ7Qh6/WBO3DA== +"@prisma/get-platform@2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a": + version "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a.tgz#78e9200abdfa15446d4830c101e5b2d692bef7ab" + integrity sha512-eEuXFcELlo8bAszQOz3YOyVpoKMjD/RSml29P51WFaDaHZaudfqV6OkSpMV2Qchcyg6neI1mvT+0acutBpNXTw== dependencies: "@prisma/debug" "2.28.0" - "@prisma/get-platform" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - "@sindresorhus/slugify" "^1.1.0" - execa "^5.0.0" - global-dirs "^3.0.0" - has-yarn "^2.1.0" - indent-string "^4.0.0" - log-update "^4.0.0" - new-github-issue-url "^0.2.1" - open "^7.0.3" - pkg-up "^3.1.0" - prompts "^2.3.2" - resolve-pkg "^2.0.0" - strip-ansi "^6.0.0" - strip-indent "^3.0.0" -"@prisma/sdk@2.28.0": - version "2.28.0" - resolved "https://registry.yarnpkg.com/@prisma/sdk/-/sdk-2.28.0.tgz#8c2f3c3fbcc53bfb1eb68944fd023e8870a5be7c" - integrity sha512-JlZiuREFWoElZHbH4aJ5wqij1qQGQGnnu6h+hULX8ZQjzwreyfyQ36QTGNak6FPeR4cjjIpOsQqRxvKvhETalw== +"@prisma/migrate@2.29.1": + version "2.29.1" + resolved "https://registry.yarnpkg.com/@prisma/migrate/-/migrate-2.29.1.tgz#dc0be465ff89cdce4435c90938b25cd453b842c7" + integrity sha512-hIupZVHlHNByxCV+k7I5X3GaxV6pNYcQgPGbPphK8zOX0gDbRJIlJZ+L+uT5rz6GLIbCwzbW32sumxcWbk04WQ== dependencies: - "@prisma/debug" "2.28.0" - "@prisma/engine-core" "2.28.0" - "@prisma/engines" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - "@prisma/fetch-engine" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - "@prisma/generator-helper" "2.28.0" - "@prisma/get-platform" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" - "@timsuchanek/copy" "^1.4.5" - archiver "^4.0.0" - arg "^5.0.0" - chalk "4.1.1" + "@prisma/debug" "2.29.1" + "@prisma/get-platform" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + "@sindresorhus/slugify" "1.1.2" + execa "5.1.1" + global-dirs "3.0.0" + has-yarn "2.1.0" + indent-string "4.0.0" + log-update "4.0.0" + new-github-issue-url "0.2.1" + open "7.4.2" + pkg-up "3.1.0" + prompts "2.4.1" + resolve-pkg "2.0.0" + strip-ansi "6.0.0" + strip-indent "3.0.0" + +"@prisma/sdk@2.29.1": + version "2.29.1" + resolved "https://registry.yarnpkg.com/@prisma/sdk/-/sdk-2.29.1.tgz#1619f4f20847caa408c75b9e8175e6a2913c4184" + integrity sha512-vtcF5jdUskjalL5Q9iagbcc+vfwUa+DAQnNdYg0PtmpaS2llWezptvEJd2ihMR7O5m2sAmI87QDiCn9Y5FxNjQ== + dependencies: + "@prisma/debug" "2.29.1" + "@prisma/engine-core" "2.29.1" + "@prisma/engines" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + "@prisma/fetch-engine" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + "@prisma/generator-helper" "2.29.1" + "@prisma/get-platform" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" + "@timsuchanek/copy" "1.4.5" + archiver "4.0.2" + arg "5.0.0" + chalk "4.1.2" checkpoint-client "1.1.20" - cli-truncate "^2.1.0" - dotenv "^10.0.0" - execa "^5.0.0" + cli-truncate "2.1.0" + dotenv "10.0.0" + execa "5.1.1" find-up "5.0.0" - global-dirs "^3.0.0" - globby "^11.0.0" - has-yarn "^2.1.0" - is-ci "^3.0.0" - make-dir "^3.0.2" + global-dirs "3.0.0" + globby "11.0.4" + has-yarn "2.1.0" + is-ci "3.0.0" + make-dir "3.1.0" node-fetch "2.6.1" - p-map "^4.0.0" - read-pkg-up "^7.0.1" - resolve "^1.2.0" - rimraf "^3.0.2" - shell-quote "^1.7.2" - string-width "^4.2.0" + p-map "4.0.0" + read-pkg-up "7.0.1" + resolve "1.20.0" + rimraf "3.0.2" + shell-quote "1.7.2" + string-width "4.2.2" strip-ansi "6.0.0" strip-indent "3.0.0" - tar "^6.0.1" - temp-dir "^2.0.0" - temp-write "^4.0.0" - tempy "^1.0.0" - terminal-link "^2.1.1" + tar "6.1.6" + temp-dir "2.0.0" + temp-write "4.0.0" + tempy "1.0.1" + terminal-link "2.1.1" tmp "0.2.1" "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": @@ -2350,7 +2382,7 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sindresorhus/slugify@^1.1.0", "@sindresorhus/slugify@^1.1.2": +"@sindresorhus/slugify@1.1.2", "@sindresorhus/slugify@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@sindresorhus/slugify/-/slugify-1.1.2.tgz#c2c0129298b8caace2d9156176fe244d0e83156c" integrity sha512-V9nR/W0Xd9TSGXpZ4iFUcFGhuOJtZX82Fzxj1YISlbSgKvIiNa7eLEZrT0vAraPOt++KHauIVNYgGRgjc13dXA== @@ -2528,7 +2560,7 @@ dependencies: "@babel/runtime" "^7.12.5" -"@timsuchanek/copy@^1.4.5": +"@timsuchanek/copy@1.4.5": version "1.4.5" resolved "https://registry.yarnpkg.com/@timsuchanek/copy/-/copy-1.4.5.tgz#8e9658c056e24e1928a88bed45f9eac6a72b7c40" integrity sha512-N4+2/DvfwzQqHYL/scq07fv8yXbZc6RyUxKJoE8Clm14JpLOf9yNI4VB4D6RsV3h9zgzZ4loJUydHKM7pp3blw== @@ -2665,13 +2697,20 @@ resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== -"@types/cross-spawn@^6.0.1": +"@types/cross-spawn@6.0.2": version "6.0.2" resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.2.tgz#168309de311cd30a2b8ae720de6475c2fbf33ac7" integrity sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw== dependencies: "@types/node" "*" +"@types/debug@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" + integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg== + dependencies: + "@types/ms" "*" + "@types/esrever@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@types/esrever/-/esrever-0.2.0.tgz#96404a2284b2c7527f08a1e957f8a31705f9880f" @@ -2768,9 +2807,9 @@ integrity sha512-Kl5beBoB0OXw7WeFgHHpLEchvC7HyIu3v1AksNNTemAF6jmEmQGqhZQSHcG6BOU/Lq0xsByQNqLzicLPjVkxYQ== "@types/http-assert@*": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" - integrity sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ== + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.2.tgz#a7fb59a7ca366e141789a084555a633801b9af3b" + integrity sha512-Ddzuzv/bB2prZnJKlS1sEYhaeT50wfJjhcTTTQLjEsEZJlk3XB4Xohieyq+P4VXIzg7lrQ1Spd/PfRnBpQsdqA== "@types/http-errors@*": version "1.8.1" @@ -2801,18 +2840,18 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*", "@types/jest@^26.0.24": - version "26.0.24" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" - integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== +"@types/jest@*", "@types/jest@^27.0.1": + version "27.0.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.1.tgz#fafcc997da0135865311bb1215ba16dba6bdf4ca" + integrity sha512-HTLpVXHrY69556ozYkcq47TtQJXpcWAWfkoqz+ZGz2JnmZhzlRjprCIyFnetSy8gpDWwTTGBcRVv1J1I1vBrHw== dependencies: - jest-diff "^26.0.0" - pretty-format "^26.0.0" + jest-diff "^27.0.0" + pretty-format "^27.0.0" -"@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8": - version "7.0.8" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" - integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== +"@types/json-schema@^7.0.7": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== "@types/keygrip@*": version "1.0.2" @@ -2841,9 +2880,9 @@ "@types/node" "*" "@types/lodash@^4.14.149": - version "4.14.171" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b" - integrity sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg== + version "4.14.172" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.172.tgz#aad774c28e7bfd7a67de25408e03ee5a8c3d028a" + integrity sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw== "@types/long@^4.0.0": version "4.0.1" @@ -2879,6 +2918,11 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== +"@types/ms@*": + version "0.7.31" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" + integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== + "@types/node-fetch@^2.5.12": version "2.5.12" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66" @@ -2898,9 +2942,9 @@ integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== "@types/node@^12.7.1": - version "12.20.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.18.tgz#37a0aab0560d1186da54ee5d62ff6a78cacb8c75" - integrity sha512-YoTiIwdKxM3VLiY2sM05x4iGuTveYiCcDaUVmo1L5ndrXxPGW/NEoZu+pGcBirziomizcZsnsQoemikKcB2fRA== + version "12.20.19" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.19.tgz#538e61fc220f77ae4a4663c3d8c3cb391365c209" + integrity sha512-niAuZrwrjKck4+XhoCw6AAVQBENHftpXw9F4ryk66fTgYaKQ53R4FI7c9vUGGw5vQis1HKBHDR1gcYI/Bq1xvw== "@types/nodemailer@^6.4.4": version "6.4.4" @@ -2980,10 +3024,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17.0.15": - version "17.0.15" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.15.tgz#c7533dc38025677e312606502df7656a6ea626d0" - integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw== +"@types/react@*", "@types/react@^17.0.18": + version "17.0.18" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.18.tgz#4109cbbd901be9582e5e39e3d77acd7b66bb7fbe" + integrity sha512-YTLgu7oS5zvSqq49X5Iue5oAbVGhgPc5Au29SJC4VeE17V6gASoOxVkUDy9pXFMRFxCWCD9fLeweNFizo3UzOg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -3129,9 +3173,9 @@ "@types/vfile-message" "*" "@types/webpack-sources@*": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-2.1.1.tgz#6af17e3a3ded71eec2b98008d7c12f498a0a4506" - integrity sha512-MjM1R6iuw8XaVbtkCBz0N349cyqBjJHCbQiOeppe3VBeFvxqs74RKHAVt9LkxTnUWc7YLZOEsUfPUnmK6SBPKQ== + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b" + integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg== dependencies: "@types/node" "*" "@types/source-list-map" "*" @@ -3161,13 +3205,6 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== -"@types/yargs@^15.0.0": - version "15.0.14" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" - integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^16.0.0": version "16.0.4" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" @@ -3182,86 +3219,86 @@ dependencies: "@types/node" "*" -"@types/zen-observable@^0.8.0": +"@types/zen-observable@0.8.3": version "0.8.3" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3" integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== -"@typescript-eslint/eslint-plugin@^4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.0.tgz#b866c9cd193bfaba5e89bade0015629ebeb27996" - integrity sha512-eiREtqWRZ8aVJcNru7cT/AMVnYd9a2UHsfZT8MR1dW3UUEg6jDv9EQ9Cq4CUPZesyQ58YUpoAADGv71jY8RwgA== +"@typescript-eslint/eslint-plugin@^4.29.2": + version "4.29.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.2.tgz#f54dc0a32b8f61c6024ab8755da05363b733838d" + integrity sha512-x4EMgn4BTfVd9+Z+r+6rmWxoAzBaapt4QFqE+d8L8sUtYZYLDTK6VG/y/SMMWA5t1/BVU5Kf+20rX4PtWzUYZg== dependencies: - "@typescript-eslint/experimental-utils" "4.29.0" - "@typescript-eslint/scope-manager" "4.29.0" + "@typescript-eslint/experimental-utils" "4.29.2" + "@typescript-eslint/scope-manager" "4.29.2" debug "^4.3.1" functional-red-black-tree "^1.0.1" regexpp "^3.1.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.29.0", "@typescript-eslint/experimental-utils@^4.0.1", "@typescript-eslint/experimental-utils@^4.2.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.0.tgz#19b1417602d0e1ef325b3312ee95f61220542df5" - integrity sha512-FpNVKykfeaIxlArLUP/yQfv/5/3rhl1ov6RWgud4OgbqWLkEq7lqgQU9iiavZRzpzCRQV4XddyFz3wFXdkiX9w== +"@typescript-eslint/experimental-utils@4.29.2", "@typescript-eslint/experimental-utils@^4.0.1", "@typescript-eslint/experimental-utils@^4.2.0": + version "4.29.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.2.tgz#5f67fb5c5757ef2cb3be64817468ba35c9d4e3b7" + integrity sha512-P6mn4pqObhftBBPAv4GQtEK7Yos1fz/MlpT7+YjH9fTxZcALbiiPKuSIfYP/j13CeOjfq8/fr9Thr2glM9ub7A== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.29.0" - "@typescript-eslint/types" "4.29.0" - "@typescript-eslint/typescript-estree" "4.29.0" + "@typescript-eslint/scope-manager" "4.29.2" + "@typescript-eslint/types" "4.29.2" + "@typescript-eslint/typescript-estree" "4.29.2" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/parser@^4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.29.0.tgz#e5367ca3c63636bb5d8e0748fcbab7a4f4a04289" - integrity sha512-+92YRNHFdXgq+GhWQPT2bmjX09X7EH36JfgN2/4wmhtwV/HPxozpCNst8jrWcngLtEVd/4zAwA6BKojAlf+YqA== +"@typescript-eslint/parser@^4.29.2": + version "4.29.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.29.2.tgz#1c7744f4c27aeb74610c955d3dce9250e95c370a" + integrity sha512-WQ6BPf+lNuwteUuyk1jD/aHKqMQ9jrdCn7Gxt9vvBnzbpj7aWEf+aZsJ1zvTjx5zFxGCt000lsbD9tQPEL8u6g== dependencies: - "@typescript-eslint/scope-manager" "4.29.0" - "@typescript-eslint/types" "4.29.0" - "@typescript-eslint/typescript-estree" "4.29.0" + "@typescript-eslint/scope-manager" "4.29.2" + "@typescript-eslint/types" "4.29.2" + "@typescript-eslint/typescript-estree" "4.29.2" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.29.0.tgz#cf5474f87321bedf416ef65839b693bddd838599" - integrity sha512-HPq7XAaDMM3DpmuijxLV9Io8/6pQnliiXMQUcAdjpJJSR+fdmbD/zHCd7hMkjJn04UQtCQBtshgxClzg6NIS2w== +"@typescript-eslint/scope-manager@4.29.2": + version "4.29.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.29.2.tgz#442b0f029d981fa402942715b1718ac7fcd5aa1b" + integrity sha512-mfHmvlQxmfkU8D55CkZO2sQOueTxLqGvzV+mG6S/6fIunDiD2ouwsAoiYCZYDDK73QCibYjIZmGhpvKwAB5BOA== dependencies: - "@typescript-eslint/types" "4.29.0" - "@typescript-eslint/visitor-keys" "4.29.0" + "@typescript-eslint/types" "4.29.2" + "@typescript-eslint/visitor-keys" "4.29.2" -"@typescript-eslint/types@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.29.0.tgz#c8f1a1e4441ea4aca9b3109241adbc145f7f8a4e" - integrity sha512-2YJM6XfWfi8pgU2HRhTp7WgRw78TCRO3dOmSpAvIQ8MOv4B46JD2chnhpNT7Jq8j0APlIbzO1Bach734xxUl4A== +"@typescript-eslint/types@4.29.2": + version "4.29.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.29.2.tgz#fc0489c6b89773f99109fb0aa0aaddff21f52fcd" + integrity sha512-K6ApnEXId+WTGxqnda8z4LhNMa/pZmbTFkDxEBLQAbhLZL50DjeY0VIDCml/0Y3FlcbqXZrABqrcKxq+n0LwzQ== -"@typescript-eslint/typescript-estree@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.0.tgz#af7ab547757b86c91bfdbc54ff86845410856256" - integrity sha512-8ZpNHDIOyqzzgZrQW9+xQ4k5hM62Xy2R4RPO3DQxMc5Rq5QkCdSpk/drka+DL9w6sXNzV5nrdlBmf8+x495QXQ== +"@typescript-eslint/typescript-estree@4.29.2": + version "4.29.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.2.tgz#a0ea8b98b274adbb2577100ba545ddf8bf7dc219" + integrity sha512-TJ0/hEnYxapYn9SGn3dCnETO0r+MjaxtlWZ2xU+EvytF0g4CqTpZL48SqSNn2hXsPolnewF30pdzR9a5Lj3DNg== dependencies: - "@typescript-eslint/types" "4.29.0" - "@typescript-eslint/visitor-keys" "4.29.0" + "@typescript-eslint/types" "4.29.2" + "@typescript-eslint/visitor-keys" "4.29.2" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.0.tgz#1ff60f240def4d85ea68d4fd2e4e9759b7850c04" - integrity sha512-LoaofO1C/jAJYs0uEpYMXfHboGXzOJeV118X4OsZu9f7rG7Pr9B3+4HTU8+err81rADa4xfQmAxnRnPAI2jp+Q== +"@typescript-eslint/visitor-keys@4.29.2": + version "4.29.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.2.tgz#d2da7341f3519486f50655159f4e5ecdcb2cd1df" + integrity sha512-bDgJLQ86oWHJoZ1ai4TZdgXzJxsea3Ee9u9wsTAvjChdj2WLcVsgWYAPeY7RQMn16tKrlQaBnpKv7KBfs4EQag== dependencies: - "@typescript-eslint/types" "4.29.0" + "@typescript-eslint/types" "4.29.2" eslint-visitor-keys "^2.0.0" "@wry/context@^0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.0.tgz#f903eceb89d238ef7e8168ed30f4511f92d83e06" - integrity sha512-sAgendOXR8dM7stJw3FusRxFHF/ZinU0lffsA2YTyyIOfic86JX02qlPqPVqJNZJPAxFt+2EE8bvq6ZlS0Kf+Q== + version "0.6.1" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.1.tgz#c3c29c0ad622adb00f6a53303c4f965ee06ebeb2" + integrity sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw== dependencies: - tslib "^2.1.0" + tslib "^2.3.0" "@wry/equality@^0.1.2": version "0.1.11" @@ -3271,18 +3308,18 @@ tslib "^1.9.3" "@wry/equality@^0.5.0": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.1.tgz#b22e4e1674d7bf1439f8ccdccfd6a785f6de68b0" - integrity sha512-FZKbdpbcVcbDxQrKcaBClNsQaMg9nof1RKM7mReJe5DKUzM5u8S7T+PqwNqvib5O2j2xxF1R4p5O3+b6baTrbw== + version "0.5.2" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.2.tgz#72c8a7a7d884dff30b612f4f8464eba26c080e73" + integrity sha512-oVMxbUXL48EV/C0/M7gLVsoK6qRHPS85x8zECofEZOVvxGmIPLA9o5Z27cc2PoAyZz1S2VoM2A7FLAnpfGlneA== dependencies: - tslib "^2.1.0" + tslib "^2.3.0" "@wry/trie@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.0.tgz#3245e74988c4e3033299e479a1bf004430752463" - integrity sha512-Yw1akIogPhAT6XPYsRHlZZIS0tIGmAl9EYXHi2scf7LPKKqdqmow/Hu4kEqP2cJR3EjaU/9L0ZlAjFf3hFxmug== + version "0.3.1" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.1.tgz#2279b790f15032f8bcea7fc944d27988e5b3b139" + integrity sha512-WwB53ikYudh9pIorgxrkHKrQZcCqNM/Q/bDzZBffEaGUKGuHrRb3zZUT9Sh2qw9yogC7SsdRmQ1ER0pqvd3bfw== dependencies: - tslib "^2.1.0" + tslib "^2.3.0" abab@^2.0.3, abab@^2.0.5: version "2.0.5" @@ -3348,12 +3385,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3663,7 +3695,7 @@ archiver-utils@^2.1.0: normalize-path "^3.0.0" readable-stream "^2.0.0" -archiver@^4.0.0: +archiver@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/archiver/-/archiver-4.0.2.tgz#43c72865eadb4ddaaa2fb74852527b6a450d927c" integrity sha512-B9IZjlGwaxF33UN4oPbfBkyA4V1SxNLeIhR1qY8sRXSsbdUkEHrrOvwlYFPx+8uQeCe9M+FG6KgO+imDmQ79CQ== @@ -3681,7 +3713,7 @@ arg@4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0" integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg== -arg@^5.0.0: +arg@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.0.tgz#a20e2bb5710e82950a516b3f933fee5ed478be90" integrity sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ== @@ -3856,9 +3888,9 @@ async-retry@^1.2.1: retry "0.12.0" async@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" - integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + version "3.2.1" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8" + integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg== asynckit@^0.4.0: version "0.4.0" @@ -3875,7 +3907,7 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -available-typed-arrays@^1.0.2: +available-typed-arrays@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9" integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA== @@ -4277,7 +4309,7 @@ browserify-zlib@0.2.0, browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@4.16.6, browserslist@^4.16.6: +browserslist@4.16.6: version "4.16.6" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== @@ -4288,6 +4320,17 @@ browserslist@4.16.6, browserslist@^4.16.6: escalade "^3.1.1" node-releases "^1.1.71" +browserslist@^4.16.6, browserslist@^4.16.7: + version "4.16.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.7.tgz#108b0d1ef33c4af1b587c54f390e7041178e4335" + integrity sha512-7I4qVwqZltJ7j37wObBe3SoTz+nS8APaNcrBOlgoirb6/HbEU2XxW/LpUDTCngM6iauwFqmRTuOMfyKnFGY5JA== + dependencies: + caniuse-lite "^1.0.30001248" + colorette "^1.2.2" + electron-to-chromium "^1.3.793" + escalade "^3.1.1" + node-releases "^1.1.73" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -4456,10 +4499,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228: - version "1.0.30001248" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz#26ab45e340f155ea5da2920dadb76a533cb8ebce" - integrity sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw== +caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001248: + version "1.0.30001251" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz#6853a606ec50893115db660f82c094d18f096d85" + integrity sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A== caseless@~0.12.0: version "0.12.0" @@ -4499,10 +4542,10 @@ chalk@4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" - integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== +chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -4526,14 +4569,6 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -4710,6 +4745,14 @@ cli-table3@~0.6.0: optionalDependencies: colors "^1.1.2" +cli-truncate@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -4718,14 +4761,6 @@ cli-truncate@^0.2.1: slice-ansi "0.0.4" string-width "^1.0.1" -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - clipboard-copy@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clipboard-copy/-/clipboard-copy-4.0.1.tgz#326ef9726d4ffe72d9a82a7bbe19379de692017d" @@ -4838,9 +4873,9 @@ color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + version "1.3.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af" + integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w== colors@^1.1.2: version "1.4.0" @@ -4993,17 +5028,17 @@ copy-to-clipboard@^3.3.1: toggle-selection "^1.0.6" core-js-compat@^3.14.0, core-js-compat@^3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.16.0.tgz#fced4a0a534e7e02f7e084bff66c701f8281805f" - integrity sha512-5D9sPHCdewoUK7pSUPfTF7ZhLh8k9/CoJXWUEo+F1dZT5Z1DVgcuRqUKhjeKW+YLb8f21rTFgWwQJiNw1hoZ5Q== + version "3.16.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.16.1.tgz#c44b7caa2dcb94b673a98f27eee1c8312f55bc2d" + integrity sha512-NHXQXvRbd4nxp9TEmooTJLUf94ySUG6+DSsscBpTftN1lQLQ4LjnWvc7AoIo4UjDsFF3hB8Uh5LLCRRdaiT5MQ== dependencies: - browserslist "^4.16.6" + browserslist "^4.16.7" semver "7.0.0" core-js-pure@^3.10.2, core-js-pure@^3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.16.0.tgz#218e07add3f1844e53fab195c47871fc5ba18de8" - integrity sha512-wzlhZNepF/QA9yvx3ePDgNGudU5KDB8lu/TRPKelYA/QtSnkS/cLl2W+TIdEX1FAFcBr0YpY7tPDlcmXJ7AyiQ== + version "3.16.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.16.1.tgz#b997df2669c957a5b29f06e95813a171f993592e" + integrity sha512-TyofCdMzx0KMhi84mVRS8rL1XsRk2SPUNz2azmth53iRN0/08Uim9fdhQTaZTG1LqaXHYVci4RDHka6WrXfnvg== core-js@3.6.5: version "3.6.5" @@ -5011,9 +5046,9 @@ core-js@3.6.5: integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== core-js@^3.1.3: - version "3.16.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.16.0.tgz#1d46fb33720bc1fa7f90d20431f36a5540858986" - integrity sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g== + version "3.16.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.16.1.tgz#f4485ce5c9f3c6a7cb18fa80488e08d362097249" + integrity sha512-AAkP8i35EbefU+JddyWi12AWE9f2N/qr/pwnDtWz4nyUIBGMJPX99ANFFRSw6FefM374lDujdtLDyhN2A/btHw== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -5110,6 +5145,15 @@ cross-fetch@^3.1.4: dependencies: node-fetch "2.6.1" +cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -5119,15 +5163,6 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - crypto-browserify@3.12.0, crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -5217,6 +5252,13 @@ cssnano-preset-simple@^2.0.0: dependencies: caniuse-lite "^1.0.30001202" +cssnano-preset-simple@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-3.0.0.tgz#e95d0012699ca2c741306e9a3b8eeb495a348dbe" + integrity sha512-vxQPeoMRqUT3c/9f0vWeVa2nKQIHFpogtoBvFdW4GQ3IvEJ6uauCP6p3Y5zQDLFcI7/+40FTgX12o7XUL0Ko+w== + dependencies: + caniuse-lite "^1.0.30001202" + cssnano-simple@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-2.0.0.tgz#930d9dcd8ba105c5a62ce719cb00854da58b5c05" @@ -5224,6 +5266,13 @@ cssnano-simple@2.0.0: dependencies: cssnano-preset-simple "^2.0.0" +cssnano-simple@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-3.0.0.tgz#a4b8ccdef4c7084af97e19bc5b93b4ecf211e90f" + integrity sha512-oU3ueli5Dtwgh0DyeohcIEE00QVfbPR3HzyXdAl89SfnQG3y0/qcpfLVW+jPIh3/rgMZGwuW96rejZGaYE9eUg== + dependencies: + cssnano-preset-simple "^3.0.0" + csso@^4.0.2: version "4.2.0" resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" @@ -5573,11 +5622,6 @@ dicer@0.3.0: dependencies: streamsearch "0.1.2" -diff-sequences@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" - integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== - diff-sequences@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" @@ -5626,9 +5670,9 @@ doctrine@^3.0.0: esutils "^2.0.2" dom-accessibility-api@^0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9" - integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw== + version "0.5.7" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.7.tgz#8c2aa6325968f2933160a0b7dbb380893ddf3e7d" + integrity sha512-ml3lJIq9YjUfM9TUnEPvEYWFSwivwIGBPKpewX7tii7fwCazA8yCioGdqQcNsItPpfFvSJ3VIdMQPj60LJhcQA== dom-helpers@^5.0.1: version "5.2.1" @@ -5688,7 +5732,7 @@ dot-prop@^3.0.0: dependencies: is-obj "^1.0.0" -dotenv@^10.0.0: +dotenv@10.0.0, dotenv@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== @@ -5733,10 +5777,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.723: - version "1.3.793" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.793.tgz#c10dff5f3126238004de344db458f1da3641d554" - integrity sha512-l9NrGV6Mr4ov5mayYPvIWcwklNw5ROmy6rllzz9dCACw9nKE5y+s5uQk+CBJMetxrWZ6QJFsvEfG6WDcH2IGUg== +electron-to-chromium@^1.3.723, electron-to-chromium@^1.3.793: + version "1.3.806" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.806.tgz#21502100f11aead6c501d1cd7f2504f16c936642" + integrity sha512-AH/otJLAAecgyrYp0XK1DPiGVWcOgwPeJBOLeuFQ5l//vhQhwC9u6d+GijClqJAmsHG4XDue81ndSQPohUu0xA== elegant-spinner@^1.0.1: version "1.0.1" @@ -5824,7 +5868,7 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: +es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2, es-abstract@^1.18.5: version "1.18.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.5.tgz#9b10de7d4c206a3581fd5b2124233e04db49ae19" integrity sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA== @@ -5898,18 +5942,18 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-import-resolver-node@^0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" - integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== +eslint-import-resolver-node@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.5.tgz#939bbb0f74e179e757ca87f7a4a890dabed18ac4" + integrity sha512-XMoPKjSpXbkeJ7ZZ9icLnJMTY5Mc1kZbCakHquaFsXPpyWOwK0TK6CODO+0ca54UoM9LKOxyUNnoVZRl8TeaAg== dependencies: - debug "^2.6.9" - resolve "^1.13.1" + debug "^3.2.7" + resolve "^1.20.0" -eslint-module-utils@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" - integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A== +eslint-module-utils@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz#94e5540dd15fe1522e8ffa3ec8db3b7fa7e7a534" + integrity sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q== dependencies: debug "^3.2.7" pkg-dir "^2.0.0" @@ -5921,17 +5965,17 @@ eslint-plugin-cypress@^2.11.3: dependencies: globals "^11.12.0" -eslint-plugin-import@^2.23.3: - version "2.23.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.3.tgz#8a1b073289fff03c4af0f04b6df956b7d463e191" - integrity sha512-wDxdYbSB55F7T5CC7ucDjY641VvKmlRwT0Vxh7PkY1mI4rclVRFWYfsrjDgZvwYYDZ5ee0ZtfFKXowWjqvEoRQ== +eslint-plugin-import@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.24.0.tgz#697ffd263e24da5e84e03b282f5fb62251777177" + integrity sha512-Kc6xqT9hiYi2cgybOc0I2vC9OgAYga5o/rAFinam/yF/t5uBqxQbauNPMC6fgb640T/89P0gFoO27FOilJ/Cqg== dependencies: array-includes "^3.1.3" array.prototype.flat "^1.2.4" debug "^2.6.9" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.4" - eslint-module-utils "^2.6.1" + eslint-import-resolver-node "^0.3.5" + eslint-module-utils "^2.6.2" find-up "^2.0.0" has "^1.0.3" is-core-module "^2.4.0" @@ -6480,14 +6524,6 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-loader@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" - integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - file-type@^10.10.0: version "10.11.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-10.11.0.tgz#2961d09e4675b9fb9a3ee6b69e9cd23f43fd1890" @@ -6629,12 +6665,12 @@ focus-lock@^0.9.1: dependencies: tslib "^2.0.3" -focus-trap@^6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-6.6.0.tgz#7fb37679926ec1bd762d0748b056c68a64a9e8cf" - integrity sha512-2hWVR3XbBejn5v8wDW9DFzLWXcxMNaSJ/CtE3E+FJjjBCLwIYbZJwjUi2RDBfQPM58gHEt5hck0jrJgHR9/s+A== +focus-trap@^6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-6.6.1.tgz#761ce2c82ddd72beeb049e968bc8414e25b704aa" + integrity sha512-x9BWuAeF5UrfWuYKJ3jYrjcVYSYptS9CqtxH5IH7lPlZrMsaugKeAa0HtoZSBZe5DmeTMx2m0qY464ZMzqarzw== dependencies: - tabbable "^5.2.0" + tabbable "^5.2.1" follow-redirects@^1.10.0: version "1.14.1" @@ -6864,6 +6900,11 @@ get-stdin@^6.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== +get-stream@6.0.1, get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -6883,11 +6924,6 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -6976,6 +7012,13 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + global-dirs@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" @@ -6983,26 +7026,19 @@ global-dirs@^2.0.1: dependencies: ini "1.3.7" -global-dirs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" - integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== - dependencies: - ini "2.0.0" - globals@^11.1.0, globals@^11.12.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.10.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.10.0.tgz#60ba56c3ac2ca845cfbf4faeca727ad9dd204676" - integrity sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g== + version "13.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" + integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== dependencies: type-fest "^0.20.2" -globby@^11.0.0, globby@^11.0.1, globby@^11.0.3, globby@^11.0.4: +globby@11.0.4, globby@^11.0.0, globby@^11.0.1, globby@^11.0.3, globby@^11.0.4: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== @@ -7058,9 +7094,9 @@ got@^9.6.0: url-parse-lax "^3.0.0" graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + version "4.2.8" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== grapheme-splitter@^1.0.4: version "1.0.4" @@ -7083,7 +7119,7 @@ graphql-subscriptions@^1.0.0: dependencies: iterall "^1.3.0" -graphql-tag@^2.11.0, graphql-tag@^2.12.0, graphql-tag@^2.12.5: +graphql-tag@^2.11.0, graphql-tag@^2.12.3, graphql-tag@^2.12.5: version "2.12.5" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.5.tgz#5cff974a67b417747d05c8d9f5f3cb4495d0db8f" integrity sha512-5xNhP4063d16Pz3HBtKprutsPrmHZi5IdUGOWRxA2B6VF7BIRGOHZ5WQvDmJXZuPcBg7rYwaFxvQYjqkSdR3TQ== @@ -7167,6 +7203,13 @@ has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -7198,7 +7241,7 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has-yarn@^2.1.0: +has-yarn@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== @@ -7468,7 +7511,7 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== -image-size@^1.0.0: +image-size@1.0.0, image-size@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.0.tgz#58b31fe4743b1cec0a0ac26f5c914d3c5b2f0750" integrity sha512-JLJ6OwBfO1KcA+TvJT+v8gbE6iWbj24LyDNFgFEN0lzegn6cC6a/p3NIDaepMsJjQjlUWqIC7wJv8lBFxPNjcw== @@ -7508,16 +7551,16 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@4.0.0, indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + indent-string@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - inflection@^1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.13.1.tgz#c5cadd80888a90cf84c2e96e340d7edc85d5f0cb" @@ -7637,11 +7680,12 @@ is-alphanumerical@^1.0.0: is-decimal "^1.0.0" is-arguments@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" - integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" + has-tostringtag "^1.0.0" is-arrayish@^0.2.1: version "0.2.1" @@ -7649,9 +7693,11 @@ is-arrayish@^0.2.1: integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" - integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" is-binary-path@^1.0.0: version "1.0.1" @@ -7668,11 +7714,12 @@ is-binary-path@~2.1.0: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" - integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" + has-tostringtag "^1.0.0" is-buffer@^1.1.5: version "1.1.6" @@ -7685,9 +7732,16 @@ is-buffer@^2.0.0: integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" - integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-ci@3.0.0, is-ci@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== + dependencies: + ci-info "^3.1.1" is-ci@^2.0.0: version "2.0.0" @@ -7696,13 +7750,6 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-ci@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" - integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== - dependencies: - ci-info "^3.1.1" - is-core-module@^2.2.0, is-core-module@^2.4.0: version "2.5.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" @@ -7725,9 +7772,11 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" - integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" is-decimal@^1.0.0: version "1.0.4" @@ -7812,9 +7861,11 @@ is-generator-fn@^2.0.0: integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-generator-function@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" - integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" is-glob@^2.0.0: version "2.0.1" @@ -7884,9 +7935,11 @@ is-negative-zero@^2.0.1: integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-number-object@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" - integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" is-number@^3.0.0: version "3.0.0" @@ -7967,12 +8020,12 @@ is-reference@^1.1.4, is-reference@^1.2.1: "@types/estree" "*" is-regex@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" - integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" - has-symbols "^1.0.2" + has-tostringtag "^1.0.0" is-stream@1.1.0, is-stream@^1.1.0: version "1.1.0" @@ -7985,9 +8038,11 @@ is-stream@^2.0.0: integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.0.5, is-string@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" - integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" is-subdir@^1.1.1: version "1.2.0" @@ -8003,16 +8058,16 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" - integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== +is-typed-array@^1.1.3, is-typed-array@^1.1.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.7.tgz#881ddc660b13cb8423b2090fa88c0fe37a83eb2f" + integrity sha512-VxlpTBGknhQ3o7YiVjIhdLU6+oD8dPz/79vvvH4F+S/c8608UCVa9fgDpa1kZgFoUST2DCgacc70UszKgzKuvA== dependencies: - available-typed-arrays "^1.0.2" + available-typed-arrays "^1.0.4" call-bind "^1.0.2" - es-abstract "^1.18.0-next.2" + es-abstract "^1.18.5" foreach "^2.0.5" - has-symbols "^1.0.1" + has-tostringtag "^1.0.0" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" @@ -8208,17 +8263,7 @@ jest-config@^27.0.6: micromatch "^4.0.4" pretty-format "^27.0.6" -jest-diff@^26.0.0: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" - integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-diff@^27.0.6: +jest-diff@^27.0.0, jest-diff@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.6.tgz#4a7a19ee6f04ad70e0e3388f35829394a44c7b5e" integrity sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg== @@ -8271,11 +8316,6 @@ jest-environment-node@^27.0.6: jest-mock "^27.0.6" jest-util "^27.0.6" -jest-get-type@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" - integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== - jest-get-type@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" @@ -9015,16 +9055,7 @@ log-symbols@^4.0.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -log-update@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" - integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= - dependencies: - ansi-escapes "^3.0.0" - cli-cursor "^2.0.0" - wrap-ansi "^3.0.1" - -log-update@^4.0.0: +log-update@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== @@ -9034,6 +9065,15 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +log-update@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" + integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= + dependencies: + ansi-escapes "^3.0.0" + cli-cursor "^2.0.0" + wrap-ansi "^3.0.1" + loglevel@^1.6.7: version "1.7.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" @@ -9626,7 +9666,7 @@ mime-db@1.49.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.32" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== @@ -9776,14 +9816,14 @@ multipipe@^1.0.2: object-assign "^4.1.0" nan@^2.12.1: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== -nanoid@^3.1.22: - version "3.1.23" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" - integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== +nanoid@^3.1.22, nanoid@^3.1.23: + version "3.1.25" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" + integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== nanomatch@^1.2.9: version "1.2.13" @@ -9819,7 +9859,7 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -new-github-issue-url@^0.2.1: +new-github-issue-url@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/new-github-issue-url/-/new-github-issue-url-0.2.1.tgz#e17be1f665a92de465926603e44b9f8685630c1d" integrity sha512-md4cGoxuT4T4d/HDOXbrUHkTKrp/vp+m3aOA7XXVYwNsUNMK49g3SQicTSeV5GIz/5QVGAeYRAOlyp9OvlgsYA== @@ -9829,18 +9869,10 @@ next-compose-plugins@^2.2.1: resolved "https://registry.yarnpkg.com/next-compose-plugins/-/next-compose-plugins-2.2.1.tgz#020fc53f275a7e719d62521bef4300fbb6fde5ab" integrity sha512-OjJ+fV15FXO2uQXQagLD4C0abYErBjyjE0I0FHpOEIB8upw0hg1ldFP6cqHTJBH1cZqy96OeR3u1dJ+Ez2D4Bg== -next-images@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/next-images/-/next-images-1.8.1.tgz#adea0c46a2e837cb49b6f95b478500a7ed4eea4f" - integrity sha512-/DoXucQKWkEBT2rCQKtm9bb+KTAnd1vVTLO12lX4oxdiBQa2uqn5vhcMPwKsdJlxNBzwg6EVnddFs3aqcwiiGA== - dependencies: - file-loader "^6.2.0" - url-loader "^4.1.0" - -next-sitemap@^1.6.140: - version "1.6.140" - resolved "https://registry.yarnpkg.com/next-sitemap/-/next-sitemap-1.6.140.tgz#b391633596f4845ec9ceb54a51da8c1f6f97ae43" - integrity sha512-VTOQMae/nDLBRGBHnkmGoIsArymlOpJWkZUDvPapktF4QX2U7yvwHIHe7V7amqV2gE2xiDYgd0ayXOBBGuFHog== +next-sitemap@^1.6.157: + version "1.6.157" + resolved "https://registry.yarnpkg.com/next-sitemap/-/next-sitemap-1.6.157.tgz#9e894188217ec94a00d4e10ddebb92393a3ea14a" + integrity sha512-5aIGplbGj0vRsetVla1ZW+0CaRtFnDY9ffW8qqii/nAfOyFuBgaHgKS92TsuBPNzz7O0/7vz4x8jZXEoR9tg2w== dependencies: "@corex/deepmerge" "^2.6.20" matcher "^4.0.0" @@ -9902,6 +9934,62 @@ next@^10.2.3: vm-browserify "1.1.2" watchpack "2.1.1" +"next@npm:next@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/next/-/next-11.1.0.tgz#767d4c4fa0b9b0c768cdbd6c9f03dd86b5d701c0" + integrity sha512-GHBk/c7Wyr6YbFRFZF37I0X7HKzkHHI8pur/loyXo5AIE8wdkbGPGO0ds3vNAO6f8AxZAKGCRYtAzoGlVLoifA== + dependencies: + "@babel/runtime" "7.12.5" + "@hapi/accept" "5.0.2" + "@next/env" "11.1.0" + "@next/polyfill-module" "11.1.0" + "@next/react-dev-overlay" "11.1.0" + "@next/react-refresh-utils" "11.1.0" + "@node-rs/helper" "1.2.1" + assert "2.0.0" + ast-types "0.13.2" + browserify-zlib "0.2.0" + browserslist "4.16.6" + buffer "5.6.0" + caniuse-lite "^1.0.30001228" + chalk "2.4.2" + chokidar "3.5.1" + constants-browserify "1.0.0" + crypto-browserify "3.12.0" + cssnano-simple "3.0.0" + domain-browser "4.19.0" + encoding "0.1.13" + etag "1.8.1" + find-cache-dir "3.3.1" + get-orientation "1.1.2" + https-browserify "1.0.0" + image-size "1.0.0" + jest-worker "27.0.0-next.5" + native-url "0.3.4" + node-fetch "2.6.1" + node-html-parser "1.4.9" + node-libs-browser "^2.2.1" + os-browserify "0.3.0" + p-limit "3.1.0" + path-browserify "1.0.1" + pnp-webpack-plugin "1.6.4" + postcss "8.2.15" + process "0.11.10" + querystring-es3 "0.2.1" + raw-body "2.4.1" + react-is "17.0.2" + react-refresh "0.8.3" + stream-browserify "3.0.0" + stream-http "3.1.1" + string_decoder "1.3.0" + styled-jsx "4.0.0" + timers-browserify "2.0.12" + tty-browserify "0.0.1" + use-subscription "1.5.1" + util "0.12.3" + vm-browserify "1.1.2" + watchpack "2.1.1" + node-fetch@2.6.1, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -9953,10 +10041,10 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-releases@^1.1.71: - version "1.1.73" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" - integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== +node-releases@^1.1.71, node-releases@^1.1.73: + version "1.1.74" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.74.tgz#e5866488080ebaa70a93b91144ccde06f3c3463e" + integrity sha512-caJBVempXZPepZoZAPCWRTNxYQ+xtG/KAi4ozTA5A+nJ7IU+kLQCbqaUjb5Rwy14M9upBWiQ4NutcmW04LJSRw== nodemailer@^6.6.3: version "6.6.3" @@ -10208,7 +10296,7 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^7.0.3: +open@7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== @@ -10216,7 +10304,7 @@ open@^7.0.3: is-docker "^2.0.0" is-wsl "^2.1.1" -optimism@^0.16.0: +optimism@^0.16.1: version "0.16.1" resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.1.tgz#7c8efc1f3179f18307b887e18c15c5b7133f6e7d" integrity sha512-64i+Uw3otrndfq5kaoGNoY7pvOhSsjFEN4bdEFh80MWVk/dbgJfMv7VFDeCT8LxNAlEVhQmdVEbfE7X2nWNIIg== @@ -10349,19 +10437,19 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== - -p-map@^4.0.0: +p-map@4.0.0, p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" -p-retry@^4.2.0: +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-retry@4.6.1, p-retry@^4.2.0: version "4.6.1" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.1.tgz#8fcddd5cdf7a67a0911a9cf2ef0e5df7f602316c" integrity sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA== @@ -10623,6 +10711,13 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" @@ -10630,22 +10725,15 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" -pkg-up@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" - integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== - dependencies: - find-up "^3.0.0" - platform@1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== -playwright@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.13.1.tgz#53c49244ffa7fc2d5d8bf7ce87739393206eda5e" - integrity sha512-pGbILcUV1+Yx0fcRJZBEIsqTDjQSwGYkJ4tVUQUtuAyMuHg5B++Nasl+pIeIJRMqKSWTyvZfPxJuPIGsLJgiFg== +playwright@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.14.0.tgz#18301b11f5278a446d36b5cf96f67db36ce2cd20" + integrity sha512-aR5oZ1iVsjQkGfYCjgYAmyMAVu0MQ0i8MgdnfdqDu9EVLfbnpuuFmTv/Rb7/Yjno1kOrDUP9+RyNC+zfG3wozA== dependencies: commander "^6.1.0" debug "^4.1.1" @@ -10693,6 +10781,15 @@ postcss@8.2.13: nanoid "^3.1.22" source-map "^0.6.1" +postcss@8.2.15: + version "8.2.15" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65" + integrity sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q== + dependencies: + colorette "^1.2.2" + nanoid "^3.1.23" + source-map "^0.6.1" + preferred-pm@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-3.0.3.tgz#1b6338000371e3edbce52ef2e4f65eb2e73586d6" @@ -10733,17 +10830,7 @@ pretty-bytes@^5.4.1: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -pretty-format@^26.0.0, pretty-format@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - -pretty-format@^27.0.2, pretty-format@^27.0.6: +pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f" integrity sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ== @@ -10763,12 +10850,12 @@ prism-react-renderer@^1.2.1: resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.2.1.tgz#392460acf63540960e5e3caa699d851264e99b89" integrity sha512-w23ch4f75V1Tnz8DajsYKvY5lF7H1+WvzvLUcF0paFxkTHSp42RS0H5CttdN2Q8RR3DRGZ9v5xD/h3n8C8kGmg== -prisma@2.28.0: - version "2.28.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.28.0.tgz#33adb56e336d4c4569ec2f49df9b606771df15d0" - integrity sha512-f83KPLy3xk07KMY4e5otNwP2I+GsdftjOfu3e8snXylnyAC1oEpRZNe7rmONr0vAI+Qgz3LFRArhWUE/dFjKIA== +prisma@2.29.1: + version "2.29.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.29.1.tgz#7845f55c7f09955b01f973c6a4b1f330cb212e7d" + integrity sha512-fRGh90+z0m3Jw3D6KBE6wyVCRR0w6M6QD93jh+em8IOQycmC48zB8hho8zeri3J9//C0k8fkDeQrRLJUosXROw== dependencies: - "@prisma/engines" "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" + "@prisma/engines" "2.29.0-34.1be4cd60b89afa04b192acb1ef47758a39810f3a" process-nextick-args@~2.0.0: version "2.0.1" @@ -10785,7 +10872,7 @@ progress@^2.0.0, progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -prompts@^2.0.1, prompts@^2.3.2, prompts@^2.4.1: +prompts@2.4.1, prompts@^2.0.1, prompts@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== @@ -11069,7 +11156,7 @@ react-is@16.13.1, react-is@^16.7.0, react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.1: +react-is@17.0.2, react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== @@ -11154,15 +11241,7 @@ react@^17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - -read-pkg-up@^7.0.1: +read-pkg-up@7.0.1, read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== @@ -11171,6 +11250,14 @@ read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -11570,7 +11657,7 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-pkg@^2.0.0: +resolve-pkg@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-2.0.0.tgz#ac06991418a7623edc119084edc98b0e6bf05a41" integrity sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ== @@ -11582,7 +11669,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.2.0, resolve@^1.20.0: +resolve@1.20.0, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -11649,7 +11736,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -11665,9 +11752,9 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: inherits "^2.0.1" rollup@^2.32.0: - version "2.55.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.55.1.tgz#66a444648e2fb603d8e329e77a61c608a6510fda" - integrity sha512-1P9w5fpb6b4qroePh8vHKGIvPNxwoCQhjJpIqfZGHLKpZ0xcU2/XBmFxFbc9697/6bmHpmFTLk5R1dAQhFSo0g== + version "2.56.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.56.2.tgz#a045ff3f6af53ee009b5f5016ca3da0329e5470f" + integrity sha512-s8H00ZsRi29M2/lGdm1u8DJpJ9ML8SUOpVVBd33XNeEeL3NVaTiUcSBHzBdF3eAyR0l7VSpsuoVUGrRHq7aPwQ== optionalDependencies: fsevents "~2.3.2" @@ -11734,15 +11821,6 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" -schema-utils@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - scroll-into-view-if-needed@^2.2.20, scroll-into-view-if-needed@^2.2.28: version "2.2.28" resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz#5a15b2f58a52642c88c8eca584644e01703d645a" @@ -11876,7 +11954,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@1.7.2, shell-quote@^1.7.2: +shell-quote@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== @@ -12117,9 +12195,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" - integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== + version "3.0.10" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" + integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -12286,6 +12364,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-width@4.2.2, string-width@^4.1.0, string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -12303,15 +12390,6 @@ string-width@^2.0.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - string.prototype.matchall@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da" @@ -12450,10 +12528,10 @@ strip-outer@^1.0.1: dependencies: escape-string-regexp "^1.0.2" -stripe@^8.167.0: - version "8.167.0" - resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.167.0.tgz#a7d7bd3828fa34210f9101b061b10f56b3a45b18" - integrity sha512-iWrof4zc/TRjzKr181Tj5Tz/RaxpNQjqkkVjjfqmzXuSY1tNV4HTbS26ucsBNkw2MJZr/q1Hx8gWDRdhZ8FG2g== +stripe@^8.169.0: + version "8.169.0" + resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.169.0.tgz#5970cc055872fccbc49713f6428a682ece7f3e76" + integrity sha512-h4JVMGnhYwkEu8dFHX0MewtTFEWcjJQ2zTiFcvhW5toa/2JcEeObfwH27Z29A6t/6s2vnXrXxEnuyL7gBBAXqA== dependencies: "@types/node" ">=8.1.0" qs "^6.6.0" @@ -12479,6 +12557,20 @@ styled-jsx@3.3.2: stylis "3.5.4" stylis-rule-sheet "0.0.10" +styled-jsx@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-4.0.0.tgz#f7b90e7889d0a4f4635f8d1ae9ac32f3acaedc57" + integrity sha512-2USeoWMoJ/Lx5s2y1PxuvLy/cz2Yrr8cTySV3ILHU1Vmaw1bnV7suKdblLPjnyhMD+qzN7B1SWyh4UZTARn/WA== + dependencies: + "@babel/plugin-syntax-jsx" "7.14.5" + "@babel/types" "7.15.0" + convert-source-map "1.7.0" + loader-utils "1.2.3" + source-map "0.7.3" + string-hash "1.1.3" + stylis "3.5.4" + stylis-rule-sheet "0.0.10" + stylis-rule-sheet@0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" @@ -12522,10 +12614,10 @@ superagent@^6.1.0: readable-stream "^3.6.0" semver "^7.3.2" -supertest@^6.1.4: - version "6.1.4" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.1.4.tgz#ea8953343e0ca316e80e975b39340934f754eb06" - integrity sha512-giC9Zm+Bf1CZP09ciPdUyl+XlMAu6rbch79KYiYKOGcbK2R1wH8h+APul1i/3wN6RF1XfWOIF+8X1ga+7SBrug== +supertest@^6.1.5: + version "6.1.5" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.1.5.tgz#b011c8465281b30c64e9d4be4cb3808b91cd1ec0" + integrity sha512-Is3pFB2TxSFPohDS2tIM8h2JOMvUQwbJ9TvTfsWAm89ZZv1CF4VTLAsQz+5+5S1wOgaMqFqSpFriU15L3e2AXQ== dependencies: methods "^1.1.2" superagent "^6.1.0" @@ -12603,10 +12695,10 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -tabbable@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.2.0.tgz#4fba60991d8bb89d06e5d9455c92b453acf88fb2" - integrity sha512-0uyt8wbP0P3T4rrsfYg/5Rg3cIJ8Shl1RJ54QMqYxm1TLdWqJD1u6+RQjr2Lor3wmfT7JRHkirIwy99ydBsyPg== +tabbable@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.2.1.tgz#e3fda7367ddbb172dcda9f871c0fdb36d1c4cd9c" + integrity sha512-40pEZ2mhjaZzK0BnI+QGNjJO8UYx9pP5v7BGe17SORTO0OEuuaAwQTkAp8whcZvqon44wKFOikD+Al11K3JICQ== table@^6.0.9: version "6.7.1" @@ -12631,10 +12723,10 @@ tar-stream@^2.1.2: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.0.1: - version "6.1.3" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.3.tgz#e44b97ee7d6cc7a4c574e8b01174614538291825" - integrity sha512-3rUqwucgVZXTeyJyL2jqtUau8/8r54SioM1xj3AmTX3HnWQdj2AydfJ2qYYayPyIIznSplcvU9mhBb7dR2XF3w== +tar@6.1.6: + version "6.1.6" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.6.tgz#c23d797b0a1efe5d479b1490805c5443f3560c5d" + integrity sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" @@ -12643,17 +12735,17 @@ tar@^6.0.1: mkdirp "^1.0.3" yallist "^4.0.0" +temp-dir@2.0.0, temp-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" + integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== + temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= -temp-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" - integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== - -temp-write@^4.0.0: +temp-write@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== @@ -12664,15 +12756,7 @@ temp-write@^4.0.0: temp-dir "^1.0.0" uuid "^3.3.2" -tempy@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.2.1.tgz#9038e4dbd1c201b74472214179bc2c6f7776e54c" - integrity sha512-LB83o9bfZGrntdqPuRdanIVCPReam9SOZKW0fOy5I9X3A854GGWi0tjCqoXEk84XIEYBc/x9Hq3EFop/H5wJaw== - dependencies: - temp-dir "^1.0.0" - unique-string "^1.0.0" - -tempy@^1.0.0: +tempy@1.0.1, tempy@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/tempy/-/tempy-1.0.1.tgz#30fe901fd869cfb36ee2bd999805aa72fbb035de" integrity sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w== @@ -12683,6 +12767,14 @@ tempy@^1.0.0: type-fest "^0.16.0" unique-string "^2.0.0" +tempy@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.2.1.tgz#9038e4dbd1c201b74472214179bc2c6f7776e54c" + integrity sha512-LB83o9bfZGrntdqPuRdanIVCPReam9SOZKW0fOy5I9X3A854GGWi0tjCqoXEk84XIEYBc/x9Hq3EFop/H5wJaw== + dependencies: + temp-dir "^1.0.0" + unique-string "^1.0.0" + term-size@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" @@ -12703,7 +12795,7 @@ terminal-link-cli@^3.0.0: meow "^9.0.0" terminal-link "^3.0.0" -terminal-link@^2.0.0, terminal-link@^2.1.1: +terminal-link@2.1.1, terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== @@ -12970,10 +13062,10 @@ ts-invariant@^0.4.0: dependencies: tslib "^1.9.3" -ts-invariant@^0.8.0: - version "0.8.2" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.8.2.tgz#62af654ebfb8b1eeb55bc9adc2f40c6b93b0ff7e" - integrity sha512-VI1ZSMW8soizP5dU8DsMbj/TncHf7bIUqavuE7FTeYeQat454HHurJ8wbfCnVWcDOMkyiBUWOW2ytew3xUxlRw== +ts-invariant@^0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.9.1.tgz#87dfde9894a4ce3c7711b02b1b449e7fd7384b13" + integrity sha512-hSeYibh29ULlHkuEfukcoiyTct+s2RzczMLTv4x3NWC/YrBy7x7ps5eYq/b4Y3Sb9/uAlf54+/5CAEMVxPhuQw== dependencies: tslib "^2.1.0" @@ -12996,10 +13088,10 @@ tslib@^1.0.0, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3, tslib@^2.1.0, tslib@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" - integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@~2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== tslib@~2.1.0: version "2.1.0" @@ -13107,9 +13199,9 @@ type-fest@^0.8.0, type-fest@^0.8.1: integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-fest@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.3.0.tgz#6be848243853df1173fa95530112e1358ab0810b" - integrity sha512-mYUYkAy6fPatVWtUeCV/qGeGL3IVucmdJOzeAEfwgCJDx8gP0JaW8jn6KQ5xDfPec31e0KXWn5EUOZMhquR1zA== + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== type-is@^1.6.16, type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" @@ -13443,15 +13535,6 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-loader@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" - integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== - dependencies: - loader-utils "^2.0.0" - mime-types "^2.1.27" - schema-utils "^3.0.0" - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -13625,9 +13708,9 @@ vfile-location@^2.0.0: integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA== vfile-message@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.0.1.tgz#b9bcf87cb5525e61777e0c6df07e816a577588a3" - integrity sha512-gYmSHcZZUEtYpTmaWaFJwsuUD70/rTY4v09COp8TGtOkix6gGxb/a8iTQByIY9ciTk9GwAwIXd/J9OPfM4Bvaw== + version "3.0.2" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.0.2.tgz#db7eaebe7fecb853010f2ef1664427f52baf8f74" + integrity sha512-UUjZYIOg9lDRwwiBAuezLIsu9KlXntdxwG+nXnjuQAHvBpcX3x0eN8h+I7TkY5nkCXj+cWVp4ZqebtGBvok8ww== dependencies: "@types/unist" "^2.0.0" unist-util-stringify-position "^3.0.0" @@ -13818,17 +13901,16 @@ which-pm@2.0.0: path-exists "^4.0.0" which-typed-array@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" - integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + version "1.1.6" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.6.tgz#f3713d801da0720a7f26f50c596980a9f5c8b383" + integrity sha512-DdY984dGD5sQ7Tf+x1CkXzdg85b9uEel6nr4UkFg1LoE9OXv3uRuZhe5CoWdawhGACeFpEZXH8fFLQnDhbpm/Q== dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.0" - es-abstract "^1.18.0-next.1" + available-typed-arrays "^1.0.4" + call-bind "^1.0.2" + es-abstract "^1.18.5" foreach "^2.0.5" - function-bind "^1.1.1" - has-symbols "^1.0.1" - is-typed-array "^1.1.3" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.6" which@^1.2.9: version "1.3.1" @@ -14040,7 +14122,15 @@ zen-observable-ts@^0.8.21: tslib "^1.9.3" zen-observable "^0.8.0" -zen-observable@^0.8.0, zen-observable@^0.8.14: +zen-observable-ts@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83" + integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA== + dependencies: + "@types/zen-observable" "0.8.3" + zen-observable "0.8.15" + +zen-observable@0.8.15, zen-observable@^0.8.0: version "0.8.15" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== From 712bea2e86879913d036a7e3771ec05dc357ef87 Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Tue, 17 Aug 2021 16:26:27 +1000 Subject: [PATCH 17/28] Release notes for 2021-08-17 release (#6345) * New release notes. * Update index.tsx * Update 2021-08-17.mdx * Update next-env.d.ts * Update index.mdx * Update 2021-08-17.mdx --- docs/next-env.d.ts | 3 ++ docs/pages/releases/2021-08-17.mdx | 74 ++++++++++++++++++++++++++++++ docs/pages/releases/index.mdx | 40 ++++++++-------- docs/pages/updates/index.tsx | 22 ++++++++- 4 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 docs/pages/releases/2021-08-17.mdx diff --git a/docs/next-env.d.ts b/docs/next-env.d.ts index f87ea7fa66a..6ab0ecc22f4 100644 --- a/docs/next-env.d.ts +++ b/docs/next-env.d.ts @@ -2,3 +2,6 @@ /// /// /// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/docs/pages/releases/2021-08-17.mdx b/docs/pages/releases/2021-08-17.mdx new file mode 100644 index 00000000000..1a5b5a3bfca --- /dev/null +++ b/docs/pages/releases/2021-08-17.mdx @@ -0,0 +1,74 @@ +import { Markdown, getStaticProps } from '../../components/Markdown'; +import { Emoji } from '../../components/primitives/Emoji'; + +# Release: 17th August 2021 + +A major milestone in the path to a `General Availability` status for **Keystone 6**, this release includes: + +- **A new and improved GraphQL API** +- Enhancements to Custom Admin UI Pages +- Better deletion notifications +- And more… + +**⚠️   This release contains breaking changes, please see below!** + +```json +"@keystone-ui/notice": "4.0.1", +"@keystone-ui/segmented-control": "4.0.2", +"@keystone-ui/toast": "4.0.2", +"@keystone-next/admin-ui-utils": "5.0.6", +"@keystone-next/auth": "31.0.0", +"@keystone-next/cloudinary": "6.0.6", +"@keystone-next/fields": "14.0.0", +"@keystone-next/fields-document": "8.0.0", +"@keystone-next/keystone": "24.0.0", +"@keystone-next/testing": "1.1.1", +"@keystone-next/types": "24.0.0", +"@keystone-next/utils": "1.0.4", +``` + +## A new & improved GraphQL API + +We’ve made the experience of working with Keystone’s GraphQL API easier to program and reason about: + +- **Queries:** the names of top-level queries are now easier to understand. We also removed deprecated and unused legacy features. +- **Filters:** the arguments used in queries have been updated to accept a filter object for each field, rather than having all the filter options available at the top level. +- **Mutations:** all generated CRUD mutations have the same names and return types, but their inputs have changed. +- **Input Types:** we’ve updated the input types used for relationship fields in `update` and `create` operations, removing obsolete options and making the syntax between the two operations easier to differentiate. + +#### Upgrade guidance + +We've [written a complete guide](https://keystonejs.com/updates/new-graphql-api) to the improvements we've made, and it includes [a checklist of the steps you need to take to upgrade your Keystone projects](https://keystonejs.com/updates/new-graphql-api#upgrade-checklist). Be sure to check it out! + +!> While there are a lot of changes to this API, if you approach the upgrade process systematically your experience should be pretty smooth. If you get stuck or have questions, reach out to us in the [Keystone community slack](https://community.keystonejs.com) to get the help you need. + +Screen Shot 2021-08-17 at 11 28 56 am + +## Custom Admin UI Pages + +Our [Custom Admin UI Pages](https://keystonejs.com/docs/guides/custom-admin-ui-pages) guide has been expanded, with an example to make your custom pages look more like the Admin UI, as well as adding links to your custom pages from the Admin UI Navigation! + +Screen Shot 2021-08-17 at 11 30 39 am + +## Improved Deletion Notifications + +When items are deleted via the Admin UI, we now display all the items that were successfully deleted, and any that failed, instead of displaying multiple notifications without any context. + +Screen Shot 2021-08-17 at 11 04 47 am + +## Deeper GraphQL Errors + +A `config.graphql.debug` option has been added, which can be used to control whether debug information such as stack traces are included in the errors returned by the GraphQL API. + +## Prisma Update + +Updated Prisma dependencies from `2.27.0` to `2.29.0`, check out the [Prisma releases page](https://github.com/prisma/prisma/releases/tag/2.29.0) for more details. + +## Credits + +Added option for `Bearer` token auth when using session, thanks to [@gautamsi](https://github.com/gautamsi)! + +You can also view the [verbose release notes](https://github.com/keystonejs/keystone/releases/tag/2021-08-17) on GitHub. + +export default ({ children, ...props }) => {children}; +export { getStaticProps } diff --git a/docs/pages/releases/index.mdx b/docs/pages/releases/index.mdx index 538727144f1..911bcd49c02 100644 --- a/docs/pages/releases/index.mdx +++ b/docs/pages/releases/index.mdx @@ -4,75 +4,79 @@ import { Status } from '../../components/primitives/Status'; # Release Notes -## 29th July 2021 +## 17th August 2021 -Custom **navigation**, **pages** and **logo** in this big **Admin UI** themed release! — [read more](/releases/2021-07-29) +A **major milestone** in the path to a General Availability status for Keystone 6 this release includes ** new and improved GraphQL API** and more. — [Read more](/releases/2021-08-17) + +## 29th July 2021 + +Custom **navigation**, **pages** and **logo** in this big **Admin UI** themed release! — [Read more](/releases/2021-07-29) ## 13th July 2021 -More examples, types, and UI rendering tweaks as we push forward towards a general availability release! — [read more](/releases/2021-07-13) +More examples, types, and UI rendering tweaks as we push forward towards a general availability release! — [Read more](/releases/2021-07-13) ## 30th June 2021 -We fixed an issue with `cloudinaryImage` and `relationship` fields. — [read more](/releases/2021-06-30) +We fixed an issue with `cloudinaryImage` and `relationship` fields. — [Read more](/releases/2021-06-30) ## 29th June 2021 -The ID Field option has been revamped with `cuid`, `uuid` and `autoincrement` options! — [read more](/releases/2021-06-29) +The ID Field option has been revamped with `cuid`, `uuid` and `autoincrement` options! — [Read more](/releases/2021-06-29) ## 28th June 2021 -A new package to help test the behaviour of your GraphQL API, a document field example, better error messages, accessibility updates and another Prisma update! — [read more](/releases/2021-06-28) +A new package to help test the behaviour of your GraphQL API, a document field example, better error messages, accessibility updates and another Prisma update! — [Read more](/releases/2021-06-28) ## 15th June 2021 -Keystone Next now has a new core , unblocking many of the features you’ve been waiting for! — [read more](/releases/2021-06-15) +Keystone Next now has a new core , unblocking many of the features you’ve been waiting for! — [Read more](/releases/2021-06-15) ## 2nd June 2021 -We have a new JSON field , a bunch of new learning resources, and plenty of under the hood optimisations in this big release. — [read more](/releases/2021-06-02) +We have a new JSON field , a bunch of new learning resources, and plenty of under the hood optimisations in this big release. — [Read more](/releases/2021-06-02) ## 19th May 2021 -Node updates and Admin UI has moved! — [read more](/releases/2021-05-19) +Node updates and Admin UI has moved! — [Read more](/releases/2021-05-19) ## 17th May 2021 -Apollo caching can now be configured for performance and a basic authentication example to get your started — [read more](/releases/2021-05-17) +Apollo caching can now be configured for performance and a basic authentication example to get your started — [Read more](/releases/2021-05-17) ## 11th May 2021 -A bunch of admin UI tweaks in this release , among other minor fixes — [read more](/releases/2021-05-11) +A bunch of admin UI tweaks in this release , among other minor fixes — [Read more](/releases/2021-05-11) ## 5th May 2021 -Aside from dependency updates , we added an `isIndexed` config option to the `text`, `integer`, `float`, `select`, and `timestamp` field types — [read more](/releases/2021-05-05) +Aside from dependency updates , we added an `isIndexed` config option to the `text`, `integer`, `float`, `select`, and `timestamp` field types — [Read more](/releases/2021-05-05) ## 3rd May 2021 -Files in Keystone Next ! This release involved a bunch of busywork behind the scenes in Keystone Next — [read more](/releases/2021-05-03) +Files in Keystone Next ! This release involved a bunch of busywork behind the scenes in Keystone Next — [Read more](/releases/2021-05-03) ## 20th April 2021 -Improvements to the Lists API, deprecating `resolveFields` — [read more](/releases/2021-04-20) +Improvements to the Lists API, deprecating `resolveFields` — [Read more](/releases/2021-04-20) ## 6th April 2021 -Controlled code demolition , Better pagination in Admin UI — [read more](/releases/2021-04-06) +Controlled code demolition , Better pagination in Admin UI — [Read more](/releases/2021-04-06) ## 30th March 2021 -Goodbye legacy code , Improved `select` field type , Squashed bugs — [read more](/releases/2021-03-30) +Goodbye legacy code , Improved `select` field type , Squashed bugs — [Read more](/releases/2021-03-30) ## 23rd March 2021 -Added support for SQLite with Prisma , Noteworthy bug-squashing — [read more](/releases/2021-03-23) +Added support for SQLite with Prisma , Noteworthy bug-squashing — [Read more](/releases/2021-03-23) ## 22nd March 2021 -Prisma migrations , Noteworthy bug-squashing — [read more](/releases/2021-03-22) +Prisma migrations , Noteworthy bug-squashing — [Read more](/releases/2021-03-22)
    diff --git a/docs/pages/updates/index.tsx b/docs/pages/updates/index.tsx index 92518a0fdd8..8c004183f9c 100644 --- a/docs/pages/updates/index.tsx +++ b/docs/pages/updates/index.tsx @@ -10,6 +10,7 @@ import { Alert } from '../../components/primitives/Alert'; import { Type } from '../../components/primitives/Type'; import { DocsPage } from '../../components/Page'; import { ArrowR } from '../../components/icons/ArrowR'; +import { Emoji } from '../../components/primitives/Emoji'; import { useMediaQuery } from '../../lib/media'; type TimelineProps = { @@ -165,7 +166,26 @@ export default function WhatsNew() { gap: 0, })} > - + + + A major milestone in the path to a General Availability status + for Keystone 6, we've just released a new and improved GraphQL API.{' '} + +
    +
    + We’ve made the experience of working with Keystone’s GraphQL API easier to program and + reason about: We've{' '} + written a complete guide to + the improvements we've made, and it includes a{' '} + + checklist of the steps you need to take to upgrade your Keystone projects + + . +
    +
    + Be sure to check it out! +
    + We're opening Admin UI up to support a more personal content experience. Now you can:
      From 46b2dc63f75ee46b2d4aaa063d1b0aa8e272b0b8 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Wed, 18 Aug 2021 08:16:51 +1000 Subject: [PATCH 18/28] Fixed markdown for bold styles (#6346) --- docs/pages/releases/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/releases/index.mdx b/docs/pages/releases/index.mdx index 911bcd49c02..bc09fae72e6 100644 --- a/docs/pages/releases/index.mdx +++ b/docs/pages/releases/index.mdx @@ -8,7 +8,7 @@ import { Status } from '../../components/primitives/Status'; -A **major milestone** in the path to a General Availability status for Keystone 6 this release includes ** new and improved GraphQL API** and more. — [Read more](/releases/2021-08-17) +A **major milestone** in the path to a General Availability status for Keystone 6 this release includes **new and improved GraphQL API** and more. — [Read more](/releases/2021-08-17) ## 29th July 2021 From 44d6ec184b62aa308d0449f9eb87ad34c15e8116 Mon Sep 17 00:00:00 2001 From: Tim Leslie Date: Wed, 18 Aug 2021 09:45:28 +1000 Subject: [PATCH 19/28] Update wording on upgrade guide and release notes (#6353) --- docs/pages/releases/2021-08-17.mdx | 2 +- docs/pages/updates/new-graphql-api.mdx | 7 ++++--- tests/test-projects/crud-notifications/CHANGELOG.md | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/pages/releases/2021-08-17.mdx b/docs/pages/releases/2021-08-17.mdx index 1a5b5a3bfca..fff504f9081 100644 --- a/docs/pages/releases/2021-08-17.mdx +++ b/docs/pages/releases/2021-08-17.mdx @@ -40,7 +40,7 @@ We’ve made the experience of working with Keystone’s GraphQL API easier to p We've [written a complete guide](https://keystonejs.com/updates/new-graphql-api) to the improvements we've made, and it includes [a checklist of the steps you need to take to upgrade your Keystone projects](https://keystonejs.com/updates/new-graphql-api#upgrade-checklist). Be sure to check it out! -!> While there are a lot of changes to this API, if you approach the upgrade process systematically your experience should be pretty smooth. If you get stuck or have questions, reach out to us in the [Keystone community slack](https://community.keystonejs.com) to get the help you need. +!> While there are a lot of changes to this API, we've put a lot of effort into making the upgrade process as smooth as possible. If you get stuck or have questions, reach out to us in the [Keystone community slack](https://community.keystonejs.com) to get the help you need. Screen Shot 2021-08-17 at 11 28 56 am diff --git a/docs/pages/updates/new-graphql-api.mdx b/docs/pages/updates/new-graphql-api.mdx index 47c6b0afc89..06711c705cb 100644 --- a/docs/pages/updates/new-graphql-api.mdx +++ b/docs/pages/updates/new-graphql-api.mdx @@ -48,7 +48,7 @@ export const lists = createSchema({ ## Query -We’ve changed the names of our top-level queries easier understand. +We’ve changed the names of our top-level queries so they’re easier to understand. We also took this opportunity to remove deprecated and unused legacy features. ### Changes @@ -138,7 +138,8 @@ tasks( ) { id } ``` -There is a one-to-one correspondence between the old filters and the new filters, so you can make a simple systematic set of changes to bring your filters up to date. +There is a one-to-one correspondence between the old filters and the new filters. +No filter functionality has been removed or added, however the individual filters are now in a nested object, and the names have changed from `snake_case` to `camelCase`. ?> **Note:** The old filter syntax used `{ fieldName: value }` to test for equality. The new syntax requires you to make this explicit, and write `{ fieldName: { equals: value} }`. @@ -386,7 +387,7 @@ input TagRelateToManyForCreateInput { ## Upgrade Checklist -While there are a lot of changes to this API, if you approach the upgrade process systematically your experience should be pretty smooth. If you get stuck or have questions, reach out to us in the [Keystone community slack](https://community.keystonejs.com/) to get the help you need. +While there are a lot of changes to this API, we've put a lot of effort into making the upgrade process as smooth as possible. If you get stuck or have questions, reach out to us in the [Keystone community slack](https://community.keystonejs.com/) to get the help you need. ?> Before you begin: check that your project doesn't rely on any of the features we've marked as deprecated in this document, or the `search` argument to filters. If you do, apply the recommended substitute. diff --git a/tests/test-projects/crud-notifications/CHANGELOG.md b/tests/test-projects/crud-notifications/CHANGELOG.md index 4315359a821..d1ab06577e5 100644 --- a/tests/test-projects/crud-notifications/CHANGELOG.md +++ b/tests/test-projects/crud-notifications/CHANGELOG.md @@ -1,6 +1,7 @@ # @keystone-next/test-projects-crud-notifications ## 0.0.3 + ### Patch Changes - Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: From cdde50874fb1c8e71cc4c54ccbe88690241af629 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Thu, 19 Aug 2021 11:16:55 +1000 Subject: [PATCH 20/28] Updates nav additions (#6366) * Relocated "K5 vs K6" guidance * Updated sidebar navigation content for `/updates` --- docs/components/docs/Navigation.tsx | 32 ++++++++++++++++++- docs/pages/docs/guides/index.tsx | 8 ----- docs/pages/docs/index.tsx | 2 +- docs/pages/updates/index.tsx | 4 +-- .../keystone-5-vs-keystone-6-preview.mdx | 2 +- docs/pages/updates/roadmap.tsx | 2 +- docs/redirects.js | 7 +++- 7 files changed, 42 insertions(+), 15 deletions(-) rename docs/pages/{docs/guides => updates}/keystone-5-vs-keystone-6-preview.mdx (98%) diff --git a/docs/components/docs/Navigation.tsx b/docs/components/docs/Navigation.tsx index f854aea01c7..75264b6f4b6 100644 --- a/docs/components/docs/Navigation.tsx +++ b/docs/components/docs/Navigation.tsx @@ -10,6 +10,7 @@ import { useMediaQuery } from '../../lib/media'; import { useHeaderContext } from '../Header'; import { Badge } from '../primitives/Badge'; import { Type } from '../primitives/Type'; +import { Emoji } from '../primitives/Emoji'; type SectionProps = { label: string; children: ReactNode }; export function Section({ label, children }: SectionProps) { @@ -133,7 +134,6 @@ export function DocsNavigation() { Walkthroughs Examples
      - Keystone 5 vs 6 Command Line Relationships @@ -214,6 +214,36 @@ export function UpdatesNavigation({ releases = [] }: { releases: string[] }) { ))}
      ) : null} +
      + + +   New GraphQL API + + + +   Customisable Admin UI + + + +   Jed’s Prisma Day Talk + + + +   New Core + + + +   New Examples Collection + + + +   Keystone 5 vs 6 + +
      ); } diff --git a/docs/pages/docs/guides/index.tsx b/docs/pages/docs/guides/index.tsx index 57c52da5d11..5d6876a84fa 100644 --- a/docs/pages/docs/guides/index.tsx +++ b/docs/pages/docs/guides/index.tsx @@ -35,14 +35,6 @@ export default function Docs() { gap: 'var(--space-xlarge)', })} > - - We’re transitioning to Keystone 6 soon. If you’re wondering which version to start a new - project with today, this guide is for you. - Keystone’s CLI helps you develop, build, and deploy projects. This guide explains all you need to standup a new backend in the terminal. diff --git a/docs/pages/docs/index.tsx b/docs/pages/docs/index.tsx index 7b7408e6ae0..b20d962eca3 100644 --- a/docs/pages/docs/index.tsx +++ b/docs/pages/docs/index.tsx @@ -101,7 +101,7 @@ export default function Docs() { We’re graduating Keystone 6 soon. If you’re wondering which version to start a new project with today, this guide is for you. diff --git a/docs/pages/updates/index.tsx b/docs/pages/updates/index.tsx index 8c004183f9c..e10cf09a19b 100644 --- a/docs/pages/updates/index.tsx +++ b/docs/pages/updates/index.tsx @@ -167,7 +167,7 @@ export default function WhatsNew() { })} > - + A major milestone in the path to a General Availability status for Keystone 6, we've just released a new and improved GraphQL API.{' '} @@ -412,7 +412,7 @@ export default function WhatsNew() { Keystone 5 is now in maintenance mode while we focus all our efforts on building Keystone diff --git a/docs/pages/docs/guides/keystone-5-vs-keystone-6-preview.mdx b/docs/pages/updates/keystone-5-vs-keystone-6-preview.mdx similarity index 98% rename from docs/pages/docs/guides/keystone-5-vs-keystone-6-preview.mdx rename to docs/pages/updates/keystone-5-vs-keystone-6-preview.mdx index 0ded46e55ad..75bae6756d5 100644 --- a/docs/pages/docs/guides/keystone-5-vs-keystone-6-preview.mdx +++ b/docs/pages/updates/keystone-5-vs-keystone-6-preview.mdx @@ -1,4 +1,4 @@ -import { Markdown } from '../../../components/Markdown'; +import { Markdown } from '../../components/Markdown'; # Keystone 5 vs 6, which should you use? diff --git a/docs/pages/updates/roadmap.tsx b/docs/pages/updates/roadmap.tsx index 6a66741b49b..a469b123120 100644 --- a/docs/pages/updates/roadmap.tsx +++ b/docs/pages/updates/roadmap.tsx @@ -240,7 +240,7 @@ export default function Roadmap() { {/* If you're assessing whether to start a project today on Keystone 5 or 6, check our{' '} - + Comparison Page */} diff --git a/docs/redirects.js b/docs/redirects.js index 52a8281777f..e4beb19507c 100644 --- a/docs/redirects.js +++ b/docs/redirects.js @@ -106,7 +106,12 @@ const ORIGINAL_NEXT = [ const CURRENT = [ { source: '/docs/guides/keystone-5-vs-keystone-next', - destination: '/docs/guides/keystone-5-vs-keystone-6-preview', + destination: '/updates/keystone-5-vs-keystone-6-preview', + permanent: true, + }, + { + source: '/docs/guides/keystone-5-vs-keystone-6-preview', + destination: '/updates/keystone-5-vs-keystone-6-preview', permanent: true, }, ]; From d43a1d8d31b984d246444c91e16a7849a90ce1a0 Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Fri, 20 Aug 2021 08:22:26 +1000 Subject: [PATCH 21/28] Update new-graphql-api.mdx (#6375) --- docs/pages/updates/new-graphql-api.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/updates/new-graphql-api.mdx b/docs/pages/updates/new-graphql-api.mdx index 06711c705cb..bb1b40223af 100644 --- a/docs/pages/updates/new-graphql-api.mdx +++ b/docs/pages/updates/new-graphql-api.mdx @@ -143,7 +143,7 @@ No filter functionality has been removed or added, however the individual filter ?> **Note:** The old filter syntax used `{ fieldName: value }` to test for equality. The new syntax requires you to make this explicit, and write `{ fieldName: { equals: value} }`. -!> See the [Filters Guide](/docs/guides/filters) for a detailed walk through the new filtering syntex. +!> See the [Filters Guide](/docs/guides/filters) for a detailed walk through the new filtering syntax. !> See the [API docs](/docs/apis/filters) for a comprehensive list of all the new filters for each field type. From e7ed4d3e547f6d5c38d5e123581ca302b34f0b45 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Fri, 20 Aug 2021 09:12:22 +1000 Subject: [PATCH 22/28] Updated URLs (#6374) --- docs/pages/docs/guides/custom-fields.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/docs/guides/custom-fields.mdx b/docs/pages/docs/guides/custom-fields.mdx index 8ccae1ba737..8ae9e65d04f 100644 --- a/docs/pages/docs/guides/custom-fields.mdx +++ b/docs/pages/docs/guides/custom-fields.mdx @@ -12,9 +12,9 @@ There are two parts to a field type: - The **frontend** portion which defines how the field looks and behaves in the Admin UI. The general approach to creating a custom field type is to take an existing field type and make the appropriate changes for your use case. -In this guide we're going to create a field type `myInt` which recreates the [`integer`](https://github.com/keystonejs/keystone/tree/master/packages/fields/src/types/integer) field type. +In this guide we're going to create a field type `myInt` which recreates the [`integer`](https://github.com/keystonejs/keystone/tree/master/packages/keystone/src/fields/types/integer) field type. -!> For inspiration, see [the source for the fields that Keystone provides](https://github.com/keystonejs/keystone/tree/master/packages/fields/src/types) and the [Custom Fields](https://github.com/keystonejs/keystone/tree/master/examples/custom-field) example project. +!> For inspiration, see [the source for the fields that Keystone provides](https://github.com/keystonejs/keystone/tree/master/packages/keystone/src/fields/types) and the [Custom Fields](https://github.com/keystonejs/keystone/tree/master/examples/custom-field) example project. ## Backend From 67e0756a7736b09f0917426bc88214a1e88b9ceb Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Fri, 20 Aug 2021 16:14:34 +1000 Subject: [PATCH 23/28] Tweaking docs for Next.js walkthrough for latest version (#6383) * Tweaking docs for Next.js walkthrough. * Update next-env.d.ts --- .../embedded-mode-with-sqlite-nextjs.mdx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx b/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx index fea513eff8b..34cda79bccd 100644 --- a/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx +++ b/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx @@ -38,11 +38,15 @@ Here's what we're going to do: ## Setup a Next.js app -Create a basic Next.js project with the `--typescript` option in an empty directory. +x> **Warning:** We normally advise to set up a new Next.js app with `yarn create next-app --typescript my-project`, however this will install Next.js `11.x`. This version isn't compatible with this guide until we upgrade Keystone's Next.js internals to `11.x`. + +x> To continue, you'll need to use Next.js `10.x` until this upgrade is completed. We've set up a repository below using Next.js `10.x` you can clone in the mean time. + +Clone the basic Next.js project below. ```bash -yarn create next-app --typescript my-project -cd my-project +git clone https://github.com/keystonejs/embedded-mode-with-sqlite-nextjs +cd embedded-mode-with-sqlite-nextjs ``` !> Keystone 6 has great TypeScript support. Including it in your project will make it easier to use Keystone’s APIs later. @@ -256,7 +260,7 @@ export async function getStaticProps({ params, }: GetStaticPropsContext) { const [post] = await lists.Post.findMany({ - where: { slug: params!.slug as string }, + where: { slug: { equals: params!.slug as string } }, query: 'id title content', }); return { props: { post } }; From a3fb2eac382953227e71ab5e6dde9a5f7ad18007 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Mon, 23 Aug 2021 09:13:17 +1000 Subject: [PATCH 24/28] Added upgrade notices to GraphQL API related pages (#6387) --- docs/pages/docs/apis/filters.mdx | 2 ++ docs/pages/docs/apis/graphql.mdx | 2 ++ docs/pages/docs/guides/filters.mdx | 2 ++ 3 files changed, 6 insertions(+) diff --git a/docs/pages/docs/apis/filters.mdx b/docs/pages/docs/apis/filters.mdx index 5e58fc12cad..a23a630431c 100644 --- a/docs/pages/docs/apis/filters.mdx +++ b/docs/pages/docs/apis/filters.mdx @@ -2,6 +2,8 @@ import { Markdown } from '../../../components/Markdown'; # Query Filter API +?> We recently improved this API so it’s easier to program and reason about. If you were using it prior to August 17th 2021, [read this guide](/updates/new-graphql-api) for info on how to upgrade. + Each field type provides its own set of filters which can be used with [queries](./graphql#all-users). This page lists all the filters available for each field type. For more details on how to use filters in queries please consult to the [GraphQL Queries - Filters](../guides/filters) guide. diff --git a/docs/pages/docs/apis/graphql.mdx b/docs/pages/docs/apis/graphql.mdx index 37c9099195d..8c03f57431b 100644 --- a/docs/pages/docs/apis/graphql.mdx +++ b/docs/pages/docs/apis/graphql.mdx @@ -2,6 +2,8 @@ import { Markdown } from '../../../components/Markdown'; # GraphQL API +?> We recently improved this API so it’s easier to program and reason about. If you were using it prior to August 17th 2021, [read this guide](/updates/new-graphql-api) for info on how to upgrade. + Keystone generates a CRUD (create, read, update, delete) GraphQL API based on the [schema](./schema) definition provided in the system [config](./config). Consider the following system definition: diff --git a/docs/pages/docs/guides/filters.mdx b/docs/pages/docs/guides/filters.mdx index 869cb8acfe3..52859c236d3 100644 --- a/docs/pages/docs/guides/filters.mdx +++ b/docs/pages/docs/guides/filters.mdx @@ -2,6 +2,8 @@ import { Markdown } from '../../../components/Markdown'; # GraphQL Queries - Filters +?> We recently improved our GraphQL API so it’s easier to program and reason about. If you were using it prior to August 17th 2021, [read this guide](/updates/new-graphql-api) for info on how to upgrade. + Keystone provides a powerful GraphQL API for querying the data in your system. At the core of this API are **query filters**. This guide will show you how to use filters to get data you need. From 31a20648f4dc254ecb1dc2ec816a72c707d5a901 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Mon, 23 Aug 2021 09:30:10 +1000 Subject: [PATCH 25/28] Fixed link color inconsistency (#6388) --- docs/lib/prose-lite.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/lib/prose-lite.ts b/docs/lib/prose-lite.ts index d96b0595a7c..fbf5edd053d 100644 --- a/docs/lib/prose-lite.ts +++ b/docs/lib/prose-lite.ts @@ -7,15 +7,15 @@ export const proseStyles = { content: '""', }, a: { - color: 'var(--text)', textDecoration: 'underline', fontWeight: 500, + color: 'inherit', }, 'a:hover': { color: 'var(--link)', }, strong: { - color: 'var(--text)', + color: 'inherit', fontWeight: 600, }, 'ol[type="A"], ol[type="A" s]': { From 80f122fcd5050b7643f05a9826c3426b7a5faa53 Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Mon, 23 Aug 2021 11:50:20 +1000 Subject: [PATCH 26/28] Add note to use `yarn` in the embedded Next.js guide (#6384) * Tweaking docs for Next.js walkthrough. * Update next-env.d.ts * Update embedded-mode-with-sqlite-nextjs.mdx --- .../docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx b/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx index 34cda79bccd..74220a2f128 100644 --- a/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx +++ b/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx @@ -49,6 +49,8 @@ git clone https://github.com/keystonejs/embedded-mode-with-sqlite-nextjs cd embedded-mode-with-sqlite-nextjs ``` +Then run `yarn` to install the dependencies. + !> Keystone 6 has great TypeScript support. Including it in your project will make it easier to use Keystone’s APIs later. Delete the `/pages/api` directory. We’ll add a GraphQL API later in the tutorial. Your `/pages` directory should now look like this: From 2da90e0a6ddf3614ce7f608bb8beeca07d66c1a1 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Tue, 24 Aug 2021 16:19:59 +1000 Subject: [PATCH 27/28] [WIP] Related content links (#6360) * WIP: get well in bottom of page * New RelatedContent component. * Added related content for POC * Added more related content cards * Updated guides index * Styles fix for inline code within component Co-authored-by: Thomas Walker --- docs/components/RelatedContent.tsx | 24 +++++++++ docs/components/primitives/Well.tsx | 4 +- docs/pages/docs/apis/auth.mdx | 13 +++++ docs/pages/docs/apis/config.mdx | 14 +++++ docs/pages/docs/apis/context.mdx | 20 +++++++ docs/pages/docs/apis/db-items.mdx | 20 +++++++ docs/pages/docs/apis/fields.mdx | 22 +++++++- docs/pages/docs/apis/filters.mdx | 13 +++++ docs/pages/docs/apis/graphql.mdx | 20 +++++++ docs/pages/docs/apis/hooks.mdx | 13 +++++ docs/pages/docs/apis/list-items.mdx | 19 +++++++ docs/pages/docs/apis/schema.mdx | 27 ++++++++++ docs/pages/docs/apis/session.mdx | 21 ++++++++ docs/pages/docs/guides/cli.mdx | 13 +++++ .../docs/guides/custom-admin-ui-logo.mdx | 28 ++++++++++ .../guides/custom-admin-ui-navigation.mdx | 27 ++++++++++ .../docs/guides/custom-admin-ui-pages.mdx | 29 +++++++++++ docs/pages/docs/guides/custom-fields.mdx | 17 +++++- .../pages/docs/guides/document-field-demo.mdx | 22 ++++++++ docs/pages/docs/guides/document-fields.mdx | 22 ++++++++ docs/pages/docs/guides/filters.mdx | 13 +++++ docs/pages/docs/guides/hooks.mdx | 10 ++++ docs/pages/docs/guides/index.tsx | 52 +++++++++++++++++-- docs/pages/docs/guides/relationships.mdx | 13 +++++ docs/pages/docs/guides/testing.mdx | 28 ++++++++++ docs/pages/docs/guides/virtual-fields.mdx | 21 ++++++++ .../embedded-mode-with-sqlite-nextjs.mdx | 13 +++++ ...tting-started-with-create-keystone-app.mdx | 26 ++++++++++ 28 files changed, 558 insertions(+), 6 deletions(-) create mode 100644 docs/components/RelatedContent.tsx diff --git a/docs/components/RelatedContent.tsx b/docs/components/RelatedContent.tsx new file mode 100644 index 00000000000..425b4cc882b --- /dev/null +++ b/docs/components/RelatedContent.tsx @@ -0,0 +1,24 @@ +/** @jsx jsx */ +import { jsx } from '@emotion/react'; +import { ReactNode } from 'react'; + +import { useMediaQuery } from '../lib/media'; + +export function RelatedContent({ children }: { children: ReactNode }) { + const mq = useMediaQuery(); + + return ( +
      a > p > p > code': { + textDecoration: 'none', + }, + })} + > + {children} +
      + ); +} diff --git a/docs/components/primitives/Well.tsx b/docs/components/primitives/Well.tsx index a08cff47dee..c087f5b7213 100644 --- a/docs/components/primitives/Well.tsx +++ b/docs/components/primitives/Well.tsx @@ -27,6 +27,7 @@ export function Well({ grad = 'grad1', heading, href, children, ...props }: Well color: 'var(--text)', overflow: 'hidden', transition: 'box-shadow 0.2s ease, transform 0.2s ease, padding 0.2s ease', + textDecoration: 'none !important', ':before': { content: '""', position: 'absolute', @@ -47,8 +48,9 @@ export function Well({ grad = 'grad1', heading, href, children, ...props }: Well as="h2" look="heading20bold" css={{ - margin: '0 0 1rem 0', + margin: '0 0 1rem 0 !important', paddingRight: '2rem', + fontSize: '1.25rem !important', }} > {heading} → diff --git a/docs/pages/docs/apis/auth.mdx b/docs/pages/docs/apis/auth.mdx index bca3897dbd6..3c12f781d46 100644 --- a/docs/pages/docs/apis/auth.mdx +++ b/docs/pages/docs/apis/auth.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Authentication API @@ -475,4 +477,15 @@ If the token has expired the value `{ code: TOKEN_EXPIRED, message: 'The auth to If the token is valid then the session handler will start a new session and return the encoded session cookie data as `sessionToken`. The authenticated item will be returned as `item`. +## Related resources + + + + Adds password-based authentication to the Task Manager starter project. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/config.mdx b/docs/pages/docs/apis/config.mdx index 3b6e7618400..249781d2507 100644 --- a/docs/pages/docs/apis/config.mdx +++ b/docs/pages/docs/apis/config.mdx @@ -1,4 +1,7 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; # System Configuration API @@ -404,4 +407,15 @@ Options: - `generateNextGraphqlAPI`: Creates a file at `node_modules/.keystone/next/graphql-api` with `default` and `config` exports that can be re-exported in a Next API route - `generateNodeAPI`: Creates a file at `node_modules/.keystone/api` with a `lists` export +## Related resources + + + + The API to configure your options used with the list function. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/context.mdx b/docs/pages/docs/apis/context.mdx index 665c62b2c5d..2c06bbda7e7 100644 --- a/docs/pages/docs/apis/context.mdx +++ b/docs/pages/docs/apis/context.mdx @@ -1,4 +1,7 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; # Context API @@ -161,4 +164,21 @@ They will be removed in future releases. `gqlNames`: A function which takes a `listKey` and returns an object containing the GraphQL query, mutation and type names related to that list. +## Related resources + + + + A programmatic API for running CRUD operations against your GraphQL API. For each list in your system you get an API at context.lists.<listName> + + + The API for running CRUD operations against the internal GraphQL resolvers in your system. It returns internal item objects, which can be returned from GraphQL resolvers. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/db-items.mdx b/docs/pages/docs/apis/db-items.mdx index e857f995535..375ebabca99 100644 --- a/docs/pages/docs/apis/db-items.mdx +++ b/docs/pages/docs/apis/db-items.mdx @@ -1,4 +1,7 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; # Database Items API @@ -140,4 +143,21 @@ const users = await context.db.lists.User.deleteMany({ }); ``` +## Related resources + + + + A programmatic API for running CRUD operations against your GraphQL API. For each list in your system you get an API at context.lists.<listName>. + + + The API for run-time functionality in your Keystone system. Use it to write business logic for access control, hooks, testing, GraphQL schema extensions, and more. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/fields.mdx b/docs/pages/docs/apis/fields.mdx index 88d5c0ef067..3f5daecb149 100644 --- a/docs/pages/docs/apis/fields.mdx +++ b/docs/pages/docs/apis/fields.mdx @@ -1,4 +1,7 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; # Fields API @@ -732,4 +735,21 @@ export default config({ }); ``` -export default ({ children }) => {children}; +## Related resources + + + + The API to configure your options used with the list() function. + + + A complete CRUD (create, read, update, delete) GraphQL API derived from the list and field names you configure in your system. + + + +export default ({ children }) => {children} diff --git a/docs/pages/docs/apis/filters.mdx b/docs/pages/docs/apis/filters.mdx index a23a630431c..dabc8c8c8c9 100644 --- a/docs/pages/docs/apis/filters.mdx +++ b/docs/pages/docs/apis/filters.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Query Filter API @@ -149,4 +151,15 @@ The `file` field type does not support filters. The `image` field type does not support filters. +## Related resources + + + + Query filters are an integral part of Keystone’s powerful GraphQL APIs. This guide will show you how to use filters to get the data you need from your system. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/graphql.mdx b/docs/pages/docs/apis/graphql.mdx index 8c03f57431b..f4ef99a7344 100644 --- a/docs/pages/docs/apis/graphql.mdx +++ b/docs/pages/docs/apis/graphql.mdx @@ -1,4 +1,7 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; # GraphQL API @@ -409,4 +412,21 @@ type User { } ``` +## Related resources + + + + The API to configure your options used with the list() function. + + + The API to configure all the parts parts of your Keystone system. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/hooks.mdx b/docs/pages/docs/apis/hooks.mdx index bf1537c7849..3241396d799 100644 --- a/docs/pages/docs/apis/hooks.mdx +++ b/docs/pages/docs/apis/hooks.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Hooks API @@ -438,4 +440,15 @@ The data resolving steps are applied in the following order: 5. Field hooks (user defined): A `resolveInput` field hook can return a new value for its field, which will the current field value on `resolvedData`. 6. List hooks (user defined): A `resolveInput` list hook can return a new value for the entire `resolvedData` object. +## Related resources + + + + Learn how to use Hooks within your schema to extend Keystone’s powerful CRUD GraphQL APIs with your own business logic. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/list-items.mdx b/docs/pages/docs/apis/list-items.mdx index 12630497fd1..b0006bf5a1f 100644 --- a/docs/pages/docs/apis/list-items.mdx +++ b/docs/pages/docs/apis/list-items.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # List Items API @@ -144,4 +146,21 @@ const users = await context.lists.User.deleteMany({ }); ``` +## Related resources + + + + The API for run-time functionality in your Keystone system. Use it to write business logic for access control, hooks, testing, GraphQL schema extensions, and more. + + + The API for running CRUD operations against the internal GraphQL resolvers in your system. It returns internal item objects, which can be returned from GraphQL resolvers. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/schema.mdx b/docs/pages/docs/apis/schema.mdx index 176ea2e956b..8b09c020d83 100644 --- a/docs/pages/docs/apis/schema.mdx +++ b/docs/pages/docs/apis/schema.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Schema API @@ -207,4 +209,29 @@ export default config({ The `description` option defines a string which will be used as a description in the Admin UI and GraphQL API docs. This option can be individually overridden by the `graphql.description` or `ui.description` options. +## Related resources + + + + Defines the names, types, and configuration of Keystone fields. See all the fields and the configuration options they accept. + + + The API to configure all the parts parts of your Keystone system. + + + A basic Blog schema with Posts and Authors. Use this as a starting place for learning how to use Keystone. It’s also a starter for other feature projects. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/apis/session.mdx b/docs/pages/docs/apis/session.mdx index 01c514b7921..69073de4586 100644 --- a/docs/pages/docs/apis/session.mdx +++ b/docs/pages/docs/apis/session.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Session API @@ -122,4 +124,23 @@ If you configure your Keystone session with session management then the [`Keysto The `startSession` and `endSession` functions will be used by [authentication mutations](./auth) to start and end authenticated sessions. These mutations will set the value of `session` to include the values `{ listKey, itemId }`. +## Related resources + + + + The API to configure all the parts parts of your Keystone system. + + + Adds password-based authentication to the Task Manager starter project. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/cli.mdx b/docs/pages/docs/guides/cli.mdx index d20b45eb04b..53ceebb1db0 100644 --- a/docs/pages/docs/guides/cli.mdx +++ b/docs/pages/docs/guides/cli.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Command Line @@ -186,4 +188,15 @@ yarn keystone-next start - If you promote your build through separate environments in a pipeline (e.g testing → staging → production) you should run migrations during the **promote** step and **not** as part of the build script. - It is important you do **not run migrations against your production database from staging builds**. If you have staging or preview environments set up in production, make sure they are not pointed to your production database. +## Related resources + + + + How to use Keystone's CLI app to standup a new local project with an Admin UI & the GraphQL API playground. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/custom-admin-ui-logo.mdx b/docs/pages/docs/guides/custom-admin-ui-logo.mdx index 00c11d9d90e..e363a2cec0c 100644 --- a/docs/pages/docs/guides/custom-admin-ui-logo.mdx +++ b/docs/pages/docs/guides/custom-admin-ui-logo.mdx @@ -1,6 +1,8 @@ import { ComingSoon } from '../../../components/docs/ComingSoon'; import { Markdown } from '../../../components/Markdown'; import { Alert } from '../../../components/primitives/Alert' +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Custom Admin UI Logo @@ -46,4 +48,30 @@ Of course this is purely a recommendation, if you would prefer to roll your own Once you've added your custom logo component you should see it rendered out in the Admin UI. ![image of Admin UI with custom logo](/assets/guides/custom-admin-ui-logo/custom-logo-result.png) +## Related resources + + + + Adds a custom logo component in the Admin UI. Builds on the Task Manager starter project. + + + Learn how to create your own custom Navigation components in Keytone’s Admin UI. + + + Learn how to add your own custom pages to Keystone’s Admin UI. + + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/custom-admin-ui-navigation.mdx b/docs/pages/docs/guides/custom-admin-ui-navigation.mdx index f37ab3e2da3..2afc212c67f 100644 --- a/docs/pages/docs/guides/custom-admin-ui-navigation.mdx +++ b/docs/pages/docs/guides/custom-admin-ui-navigation.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Custom Admin UI Navigation @@ -304,4 +306,29 @@ type NavItemProps = { By default the `isSelected` value will be evaluated by the condition `router.pathname === href`. Pass in `isSelected` if you have a custom condition or would like more granular control over the "selected" state of Navigation items. +## Related resources + + + + Adds a custom navigation component to the Admin UI. Builds on the Task Manager starter project. + + + Learn how to add your own custom logo to Keystone’s Admin UI. + + + Learn how to add your own custom pages to Keystone’s Admin UI. + + + export default ({ children }) => {children} diff --git a/docs/pages/docs/guides/custom-admin-ui-pages.mdx b/docs/pages/docs/guides/custom-admin-ui-pages.mdx index 30af0ff1f6f..1bb1b559ae8 100644 --- a/docs/pages/docs/guides/custom-admin-ui-pages.mdx +++ b/docs/pages/docs/guides/custom-admin-ui-pages.mdx @@ -1,6 +1,8 @@ import { ComingSoon } from '../../../components/docs/ComingSoon'; import { Markdown } from '../../../components/Markdown'; import { Alert } from '../../../components/primitives/Alert'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Custom Admin UI Pages @@ -9,6 +11,8 @@ import { Alert } from '../../../components/primitives/Alert'; In this guide we'll show you how to add custom pages to the Keystone Admin UI. As the Admin UI is built on top of [Next.js](https://nextjs.org/docs/basic-features/pages), it exposes the same pages directory for adding custom pages. +## Getting started + To create a custom page, ensure that the `admin/pages` directory exists in the root of your Keystone Project. Much like with Next.js, all files in this directory will be added as routes to the Admin UI. The default export of every file in this directory is expected to be a valid React Component rendered out as the contents of the route. @@ -173,4 +177,29 @@ export default function CustomPage () { Using `emotion` for styling is purely a recommendation, if you would prefer to use another css-in-js or css solution for your custom component please feel free to. This may require additional configuration currently outside of the scope of this guide. +## Related resources + + + + Adds a custom page in the Admin UI. Builds on the Task Manager starter project. + + + Learn how to add your own custom logo to Keystone’s Admin UI. + + + Learn how to create your own custom Navigation components in Keytone’s Admin UI. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/custom-fields.mdx b/docs/pages/docs/guides/custom-fields.mdx index 8ae9e65d04f..ade3bf42fe2 100644 --- a/docs/pages/docs/guides/custom-fields.mdx +++ b/docs/pages/docs/guides/custom-fields.mdx @@ -1,5 +1,7 @@ -import { ComingSoon } from '../../../components/docs/ComingSoon'; import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; # Custom Fields @@ -223,4 +225,17 @@ export const CardValue: CardValueComponent = ({ item, field }) => { }; ``` +## Related resources + + + + Adds a custom field type based on the integer field type which lets users rate items on a 5-star scale. Builds on the Blog starter project. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/document-field-demo.mdx b/docs/pages/docs/guides/document-field-demo.mdx index 5f906a89733..d80abb27b26 100644 --- a/docs/pages/docs/guides/document-field-demo.mdx +++ b/docs/pages/docs/guides/document-field-demo.mdx @@ -9,6 +9,9 @@ import { DocumentFeaturesProvider, } from '../../../components/docs/DocumentEditorDemo'; import Intro from './_doc-field-intro.mdx'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; ## Configure the demo @@ -51,3 +54,22 @@ export default ({ children }) => { ); }; + +## Related resources + + + + Keystone’s document field is a highly customisable rich text editor that stores content as structured JSON. Learn how to configure it and incorporate your own custom React components. + + + Illustrates how to configure document fields in your Keystone system and render their data in a frontend application. Builds on the Blog starter project. + + diff --git a/docs/pages/docs/guides/document-fields.mdx b/docs/pages/docs/guides/document-fields.mdx index 9b2b5da7aa7..fcfe22e204d 100644 --- a/docs/pages/docs/guides/document-fields.mdx +++ b/docs/pages/docs/guides/document-fields.mdx @@ -1,4 +1,7 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; # How To Use Document Fields @@ -676,4 +679,23 @@ const componentBlockRenderers: InferRenderersForComponentBlocks; ``` +## Related resources + + + + Illustrates how to configure document fields in your Keystone system and render their data in a frontend application. Builds on the Blog starter project. + + + Test drive the many features of Keystone’s Document field on this website. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/filters.mdx b/docs/pages/docs/guides/filters.mdx index 52859c236d3..bca50788605 100644 --- a/docs/pages/docs/guides/filters.mdx +++ b/docs/pages/docs/guides/filters.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # GraphQL Queries - Filters @@ -173,4 +175,15 @@ For example, to find all the people which have `some` posts with the label `"Hel } ``` +## Related resources + + + + The complete list of filters available to Keystone field types. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/hooks.mdx b/docs/pages/docs/guides/hooks.mdx index fbfb9eff43e..5efc910c478 100644 --- a/docs/pages/docs/guides/hooks.mdx +++ b/docs/pages/docs/guides/hooks.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Hooks @@ -222,4 +224,12 @@ export default config({ See the [Hooks API](../apis/hooks) for the details of all the arguments available for all the different hook functions. +## Related resources + + + + The complete reference for executing code at different stages of the mutation lifecycle + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/index.tsx b/docs/pages/docs/guides/index.tsx index 5d6876a84fa..2da2b2dc68d 100644 --- a/docs/pages/docs/guides/index.tsx +++ b/docs/pages/docs/guides/index.tsx @@ -47,14 +47,60 @@ export default function Docs() { Query filters are an integral part of Keystone’s powerful GraphQL APIs. This guide will show you how to use filters to get the data you need from your system.
      + + Learn how to use Hooks within your schema to extend Keystone’s powerful CRUD GraphQL APIs + with your own business logic. + Keystone’s document field is a highly customisable rich text editor that stores content as structured JSON. Learn how to configure it and incorporate your own custom React components. - - Learn how to use Hooks within your schema to extend Keystone’s powerful CRUD GraphQL APIs - with your own business logic. + + Test drive the many features of Keystone’s Document field on this website. + + + Learn how to define your own custom field types in Keystone, with customisable backend + data structure, and Admin UI appearance. + + + Learn how to test the behaviour of your Keystone system to ensure it does what you expect. + + + Virtual fields offer a powerful way to extend your GraphQL API. This guide introduces the + syntax and shows you how start simply and end up with a complex result. + +
    + + Admin UI Customisation + +
    + + Learn how to add your own custom logo to Keystone’s Admin UI. + + + Learn how to create your own custom Navigation components in Keytone’s Admin UI. + + + Learn how to add your own custom pages to Keystone’s Admin UI.
    diff --git a/docs/pages/docs/guides/relationships.mdx b/docs/pages/docs/guides/relationships.mdx index cd5ac552c5e..6d546873fc1 100644 --- a/docs/pages/docs/guides/relationships.mdx +++ b/docs/pages/docs/guides/relationships.mdx @@ -1,5 +1,7 @@ import { Emoji } from '../../../components/primitives/Emoji'; import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Understanding Relationships @@ -314,4 +316,15 @@ export default config({ Keystone relationships are managed using the [relationship](../apis/fields#relationship) field type. They can be configured as one-sided or two-sided by the `ref` config option. Their cardinality can be set using the `many` flag. Keystone gives you the flexibility to choose what you want based on what you need to achieve. +## Related resources + + + + Defines the names, types, and configuration of Keystone fields. See all the fields and the configuration options they accept. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/testing.mdx b/docs/pages/docs/guides/testing.mdx index e70afacb7e0..ae34ac045b6 100644 --- a/docs/pages/docs/guides/testing.mdx +++ b/docs/pages/docs/guides/testing.mdx @@ -1,4 +1,7 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; # Testing @@ -234,4 +237,29 @@ describe('Example tests using test environment', () => { }); ``` +## Related resources + + + + Shows you how to write tests against the GraphQL API to your Keystone system. Builds on the Authentication example project. + + + The API for run-time functionality in your Keystone system. Use it to write business logic for access control, hooks, testing, GraphQL schema extensions, and more. + + + A programmatic API for running CRUD operations against your GraphQL API. For each list in your system you get an API at context.lists.<listName>. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/guides/virtual-fields.mdx b/docs/pages/docs/guides/virtual-fields.mdx index e1a697ee4f7..270c279b887 100644 --- a/docs/pages/docs/guides/virtual-fields.mdx +++ b/docs/pages/docs/guides/virtual-fields.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # Virtual Fields @@ -315,4 +317,23 @@ Another way to address this is to use a [scalar field](../apis/fields#scalar-typ The other main consideration is that it is not possible to filter on a virtual field, as each item calcutes its value dynamically, rather than having it stored in the database. Using a pre-calculated scalar field is the best solution to use if you need filtering for your field. +## Related resources + + + + A demo project that shows you how to add virtual fields to a Keystone list. + + + A virtual field represents a value which is computed a read time, rather than stored in the database. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx b/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx index 74220a2f128..cec664c2356 100644 --- a/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx +++ b/docs/pages/docs/walkthroughs/embedded-mode-with-sqlite-nextjs.mdx @@ -1,4 +1,6 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; # How to embed Keystone + SQLite in a Next.js app @@ -323,4 +325,15 @@ Embedded mode is a great way to operate a personal Next.js blog or portfolio wit Keystone’s Embedded mode and SQLite support gives you the option to run a self contained CMS from the same place you keep your frontend code. While this option restricts read-write access to people who can run the project in local development, it has advantages with ease of setup, security, and web deployment. This is also a great way to deploy a read-only API on the web for content you manage on your computer. +## Related resources + + + + How to use Keystone's CLI app to standup a new local project with an Admin UI & the GraphQL API playground. + + + export default ({ children }) => {children}; diff --git a/docs/pages/docs/walkthroughs/getting-started-with-create-keystone-app.mdx b/docs/pages/docs/walkthroughs/getting-started-with-create-keystone-app.mdx index 28005d1e5c8..07efdebe3e7 100644 --- a/docs/pages/docs/walkthroughs/getting-started-with-create-keystone-app.mdx +++ b/docs/pages/docs/walkthroughs/getting-started-with-create-keystone-app.mdx @@ -1,4 +1,7 @@ import { Markdown } from '../../../components/Markdown'; +import { Well } from '../../../components/primitives/Well'; +import { RelatedContent } from '../../../components/RelatedContent'; +import { InlineCode } from '../../../components/primitives/Code'; ![A terminal with the output of create-keystone-app](/assets/walkthroughs/getting-started/cover.svg) @@ -120,4 +123,27 @@ Read more about the cli in our [command line guides](/guides/cli). - Customise Keystone with the [System Configuration API](/apis/config). - Add fields and relationships using [Schema API](/apis/schema). +## Related resources + + + + Keystone’s CLI helps you develop, build, and deploy projects. This guide explains all you need to standup a new backend in the terminal. + + + The API to configure your options used with the list() function. + + + The API to configure all the parts parts of your Keystone system. + + + export default ({ children }) => {children} From 903e5ce7d6251b5b59f3d1faf001f20963247c41 Mon Sep 17 00:00:00 2001 From: Ronald Aveling Date: Wed, 25 Aug 2021 10:01:05 +1000 Subject: [PATCH 28/28] Added 2x testimonials (#6400) --- docs/pages/for-developers.tsx | 19 ++++++++++++++++++- docs/public/assets/benoit-richert.jpg | Bin 0 -> 9566 bytes docs/public/assets/divslingerx.jpg | Bin 0 -> 25015 bytes 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 docs/public/assets/benoit-richert.jpg create mode 100644 docs/public/assets/divslingerx.jpg diff --git a/docs/pages/for-developers.tsx b/docs/pages/for-developers.tsx index 9a8d63884a5..5f2df11ab01 100644 --- a/docs/pages/for-developers.tsx +++ b/docs/pages/for-developers.tsx @@ -463,12 +463,29 @@ export default function ForDevelopers() { gridTemplateRows: 'masonry', // experimental and hopefully supported soon })} > + + How good is Keystone support! The answers are fast, thought through, technical when + needed, and always gentle... Kudos to the Keystone team, thank you very much!{' '} + + + + I love how Keystone’s access control lets me declare every single Create, Read, + Update, and Delete operation at both the model and{' '} + field level. It’s my favorite way of implementing Auth. + Working with Keystone is a very satisfying experience. I wrapped up 50% of my app’s - schema, API and seed data in a day . The dev + schema, API and seed data in a day The dev experience feels too good to be true {' '} + + + @KeystoneJS + {' '} + is almost too good to be open source. I can’t stress enough how awesome the dev + experience is. This is what I wish Wordpress was. + I think I'm in love. Keystone‘s just what I needed: a dashboard & GraphQL API that works like a charm. As a frontend dev with skills in node and elastic search, Keystone diff --git a/docs/public/assets/benoit-richert.jpg b/docs/public/assets/benoit-richert.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8264538c5fc0c583317beee98c994ceeaa8fdf62 GIT binary patch literal 9566 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 Mb7ltn_VGXe+3&0UHUIzs literal 0 HcmV?d00001